From commits-return-120436-archive-asf-public=cust-asf.ponee.io@lucene.apache.org Wed Jan 13 11:48:12 2021 Return-Path: X-Original-To: archive-asf-public@cust-asf.ponee.io Delivered-To: archive-asf-public@cust-asf.ponee.io Received: from mxout1-he-de.apache.org (mxout1-he-de.apache.org [95.216.194.37]) by mx-eu-01.ponee.io (Postfix) with ESMTPS id CBD5418066D for ; Wed, 13 Jan 2021 12:48:12 +0100 (CET) Received: from mail.apache.org (mailroute1-lw-us.apache.org [207.244.88.153]) by mxout1-he-de.apache.org (ASF Mail Server at mxout1-he-de.apache.org) with SMTP id EB4FE64C59 for ; Wed, 13 Jan 2021 11:48:11 +0000 (UTC) Received: (qmail 73153 invoked by uid 500); 13 Jan 2021 11:48:11 -0000 Mailing-List: contact commits-help@lucene.apache.org; run by ezmlm Precedence: bulk List-Help: List-Unsubscribe: List-Post: List-Id: Reply-To: dev@lucene.apache.org Delivered-To: mailing list commits@lucene.apache.org Received: (qmail 73140 invoked by uid 99); 13 Jan 2021 11:48:11 -0000 Received: from ec2-52-202-80-70.compute-1.amazonaws.com (HELO gitbox.apache.org) (52.202.80.70) by apache.org (qpsmtpd/0.29) with ESMTP; Wed, 13 Jan 2021 11:48:11 +0000 Received: by gitbox.apache.org (ASF Mail Server at gitbox.apache.org, from userid 33) id D90EE820F7; Wed, 13 Jan 2021 11:48:10 +0000 (UTC) Date: Wed, 13 Jan 2021 11:48:09 +0000 To: "commits@lucene.apache.org" Subject: [lucene-solr] branch jira/solr-15055-2 updated: SOLR-15055: API refactoring + extensions to support vetoing different types of placement modification requests. MIME-Version: 1.0 Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: 8bit Message-ID: <161053848877.5568.11037241885437201947@gitbox.apache.org> From: ab@apache.org X-Git-Host: gitbox.apache.org X-Git-Repo: lucene-solr X-Git-Refname: refs/heads/jira/solr-15055-2 X-Git-Reftype: branch X-Git-Oldrev: c49ba2e3f4e7fd54d382ede805a30811579284dc X-Git-Newrev: 57c2db331f41a5954f7ce0b3262d1044b3a4eb35 X-Git-Rev: 57c2db331f41a5954f7ce0b3262d1044b3a4eb35 X-Git-NotificationType: ref_changed_plus_diff X-Git-Multimail-Version: 1.5.dev Auto-Submitted: auto-generated This is an automated email from the ASF dual-hosted git repository. ab pushed a commit to branch jira/solr-15055-2 in repository https://gitbox.apache.org/repos/asf/lucene-solr.git The following commit(s) were added to refs/heads/jira/solr-15055-2 by this push: new 57c2db3 SOLR-15055: API refactoring + extensions to support vetoing different types of placement modification requests. 57c2db3 is described below commit 57c2db331f41a5954f7ce0b3262d1044b3a4eb35 Author: Andrzej Bialecki AuthorDate: Wed Jan 13 12:47:17 2021 +0100 SOLR-15055: API refactoring + extensions to support vetoing different types of placement modification requests. --- .../solr/cloud/api/collections/DeleteNodeCmd.java | 3 +- .../cloud/api/collections/DeleteReplicaCmd.java | 76 ++++++++++++++++---- .../cluster/placement/DeleteReplicasRequest.java | 29 ++++++++ .../cluster/placement/DeleteShardsRequest.java | 27 +++++++ .../cluster/placement/ModificationRequest.java | 30 ++++++++ .../solr/cluster/placement/PlacementContext.java | 44 ++++++++++++ .../solr/cluster/placement/PlacementPlugin.java | 15 ++-- .../solr/cluster/placement/PlacementRequest.java | 7 +- .../placement/impl/ModificationRequestImpl.java | 82 ++++++++++++++++++++++ .../placement/impl/PlacementContextImpl.java | 39 ++++++++++ .../impl/PlacementPluginAssignStrategy.java | 12 ++-- .../impl/SimpleClusterAbstractionsImpl.java | 6 +- .../plugins/AffinityPlacementFactory.java | 23 ++++-- .../plugins/MinimizeCoresPlacementFactory.java | 16 +++-- .../placement/plugins/RandomPlacementFactory.java | 17 +++-- .../apache/solr/cluster/placement/Builders.java | 24 +++++++ .../plugins/AffinityPlacementFactoryTest.java | 63 ++++++++--------- 17 files changed, 420 insertions(+), 93 deletions(-) diff --git a/solr/core/src/java/org/apache/solr/cloud/api/collections/DeleteNodeCmd.java b/solr/core/src/java/org/apache/solr/cloud/api/collections/DeleteNodeCmd.java index 19865d3..c69675b 100644 --- a/solr/core/src/java/org/apache/solr/cloud/api/collections/DeleteNodeCmd.java +++ b/solr/core/src/java/org/apache/solr/cloud/api/collections/DeleteNodeCmd.java @@ -18,6 +18,7 @@ package org.apache.solr.cloud.api.collections; +import java.io.IOException; import java.lang.invoke.MethodHandles; import java.util.ArrayList; import java.util.List; @@ -98,7 +99,7 @@ public class DeleteNodeCmd implements OverseerCollectionMessageHandler.Cmd { List sourceReplicas, OverseerCollectionMessageHandler ocmh, String node, - String async) throws InterruptedException { + String async) throws IOException, InterruptedException { CountDownLatch cleanupLatch = new CountDownLatch(sourceReplicas.size()); for (ZkNodeProps sourceReplica : sourceReplicas) { String coll = sourceReplica.getStr(COLLECTION_PROP); diff --git a/solr/core/src/java/org/apache/solr/cloud/api/collections/DeleteReplicaCmd.java b/solr/core/src/java/org/apache/solr/cloud/api/collections/DeleteReplicaCmd.java index 4d7975d..93ababd 100644 --- a/solr/core/src/java/org/apache/solr/cloud/api/collections/DeleteReplicaCmd.java +++ b/solr/core/src/java/org/apache/solr/cloud/api/collections/DeleteReplicaCmd.java @@ -23,6 +23,7 @@ import static org.apache.solr.common.params.CollectionAdminParams.COUNT_PROP; import static org.apache.solr.common.params.CollectionAdminParams.FOLLOW_ALIASES; import static org.apache.solr.common.params.CommonAdminParams.ASYNC; +import java.io.IOException; import java.lang.invoke.MethodHandles; import java.util.ArrayList; import java.util.Collection; @@ -34,6 +35,14 @@ import java.util.concurrent.Callable; import org.apache.solr.cloud.api.collections.OverseerCollectionMessageHandler.Cmd; import org.apache.solr.cloud.api.collections.OverseerCollectionMessageHandler.ShardRequestTracker; +import org.apache.solr.cluster.placement.AttributeFetcher; +import org.apache.solr.cluster.placement.DeleteReplicasRequest; +import org.apache.solr.cluster.placement.PlacementContext; +import org.apache.solr.cluster.placement.PlacementException; +import org.apache.solr.cluster.placement.PlacementPlugin; +import org.apache.solr.cluster.placement.impl.AttributeFetcherImpl; +import org.apache.solr.cluster.placement.impl.ModificationRequestImpl; +import org.apache.solr.cluster.placement.impl.PlacementContextImpl; import org.apache.solr.common.SolrException; import org.apache.solr.common.cloud.ClusterState; import org.apache.solr.common.cloud.DocCollection; @@ -69,15 +78,22 @@ public class DeleteReplicaCmd implements Cmd { @SuppressWarnings("unchecked") void deleteReplica(ClusterState clusterState, ZkNodeProps message, @SuppressWarnings({"rawtypes"})NamedList results, Runnable onComplete) - throws KeeperException, InterruptedException { + throws KeeperException, IOException, InterruptedException { if (log.isDebugEnabled()) { log.debug("deleteReplica() : {}", Utils.toJSONString(message)); } boolean parallel = message.getBool("parallel", false); + PlacementPlugin placementPlugin = ocmh.overseer.getCoreContainer().getPlacementPluginFactory().createPluginInstance(); + PlacementContext placementContext = new PlacementContextImpl(ocmh.cloudManager); //If a count is specified the strategy needs be different if (message.getStr(COUNT_PROP) != null) { - deleteReplicaBasedOnCount(clusterState, message, results, onComplete, parallel); + try { + deleteReplicaBasedOnCount(clusterState, message, results, onComplete, parallel, placementPlugin, placementContext); + } catch (PlacementException pe) { + throw new SolrException(SolrException.ErrorCode.BAD_REQUEST, + "Delete replica(s) rejected by replica placement plugin: " + pe.getMessage(), pe); + } return; } @@ -102,7 +118,12 @@ public class DeleteReplicaCmd implements Cmd { "Invalid shard name : " + shard + " in collection : " + collectionName); } - deleteCore(slice, collectionName, replicaName, message, shard, results, onComplete, parallel); + try { + deleteCore(coll, shard, replicaName, message, results, onComplete, parallel, placementPlugin, placementContext); + } catch (PlacementException pe) { + throw new SolrException(SolrException.ErrorCode.BAD_REQUEST, + "Delete replica rejected by replica placement plugin: " + pe.getMessage(), pe); + } } @@ -116,8 +137,10 @@ public class DeleteReplicaCmd implements Cmd { ZkNodeProps message, @SuppressWarnings({"rawtypes"})NamedList results, Runnable onComplete, - boolean parallel) - throws KeeperException, InterruptedException { + boolean parallel, + PlacementPlugin placementPlugin, + PlacementContext placementContext) + throws KeeperException, PlacementException, InterruptedException { ocmh.checkRequired(message, COLLECTION_PROP, COUNT_PROP); int count = Integer.parseInt(message.getStr(COUNT_PROP)); String collectionName = message.getStr(COLLECTION_PROP); @@ -147,6 +170,16 @@ public class DeleteReplicaCmd implements Cmd { } } + // verify that all replicas can be deleted + for (Map.Entry> entry : shardToReplicasMapping.entrySet()) { + Slice shardSlice = entry.getKey(); + String shardId = shardSlice.getName(); + Set replicas = entry.getValue(); + //verify all replicas + DeleteReplicasRequest deleteReplicasRequest = ModificationRequestImpl.deleteReplicasRequest(coll, shardId, replicas); + placementPlugin.verifyAllowedModification(deleteReplicasRequest, placementContext); + } + for (Map.Entry> entry : shardToReplicasMapping.entrySet()) { Slice shardSlice = entry.getKey(); String shardId = shardSlice.getName(); @@ -154,7 +187,8 @@ public class DeleteReplicaCmd implements Cmd { //callDeleteReplica on all replicas for (String replica: replicas) { log.debug("Deleting replica {} for shard {} based on count {}", replica, shardId, count); - deleteCore(shardSlice, collectionName, replica, message, shard, results, onComplete, parallel); + // don't verify with the placement plugin - we already did it + deleteCore(coll, shardId, replica, message, results, onComplete, parallel, null, null); } results.add("shard_id", shardId); results.add("replicas_deleted", replicas); @@ -212,25 +246,39 @@ public class DeleteReplicaCmd implements Cmd { } @SuppressWarnings({"unchecked"}) - void deleteCore(Slice slice, String collectionName, String replicaName,ZkNodeProps message, String shard, @SuppressWarnings({"rawtypes"})NamedList results, Runnable onComplete, boolean parallel) throws KeeperException, InterruptedException { - + void deleteCore(DocCollection coll, + String shardId, + String replicaName, + ZkNodeProps message, + @SuppressWarnings({"rawtypes"})NamedList results, + Runnable onComplete, + boolean parallel, + PlacementPlugin placementPlugin, + PlacementContext placementContext) throws KeeperException, PlacementException, InterruptedException { + + Slice slice = coll.getSlice(shardId); Replica replica = slice.getReplica(replicaName); if (replica == null) { ArrayList l = new ArrayList<>(); for (Replica r : slice.getReplicas()) l.add(r.getName()); throw new SolrException(SolrException.ErrorCode.BAD_REQUEST, "Invalid replica : " + replicaName + " in shard/collection : " + - shard + "/" + collectionName + " available replicas are " + StrUtils.join(l, ',')); + shardId + "/" + coll.getName() + " available replicas are " + StrUtils.join(l, ',')); } // If users are being safe and only want to remove a shard if it is down, they can specify onlyIfDown=true // on the command. if (Boolean.parseBoolean(message.getStr(OverseerCollectionMessageHandler.ONLY_IF_DOWN)) && replica.getState() != Replica.State.DOWN) { throw new SolrException(SolrException.ErrorCode.BAD_REQUEST, - "Attempted to remove replica : " + collectionName + "/" + shard + "/" + replicaName + + "Attempted to remove replica : " + coll.getName() + "/" + shardId + "/" + replicaName + " with onlyIfDown='true', but state is '" + replica.getStr(ZkStateReader.STATE_PROP) + "'"); } + if (placementPlugin != null) { + // verify that we are allowed to delete this replica + DeleteReplicasRequest deleteReplicasRequest = ModificationRequestImpl.deleteReplicasRequest(coll, shardId, Set.of(replicaName)); + placementPlugin.verifyAllowedModification(deleteReplicasRequest, placementContext); + } ShardHandler shardHandler = ocmh.shardHandlerFactory.getShardHandler(); String core = replica.getStr(ZkStateReader.CORE_NAME_PROP); String asyncId = message.getStr(ASYNC); @@ -256,12 +304,12 @@ public class DeleteReplicaCmd implements Cmd { shardRequestTracker.processResponses(results, shardHandler, false, null); //check if the core unload removed the corenode zk entry - if (ocmh.waitForCoreNodeGone(collectionName, shard, replicaName, 30000)) return Boolean.TRUE; + if (ocmh.waitForCoreNodeGone(coll.getName(), shardId, replicaName, 30000)) return Boolean.TRUE; } // try and ensure core info is removed from cluster state - ocmh.deleteCoreNode(collectionName, replicaName, replica, core); - if (ocmh.waitForCoreNodeGone(collectionName, shard, replicaName, 30000)) return Boolean.TRUE; + ocmh.deleteCoreNode(coll.getName(), replicaName, replica, core); + if (ocmh.waitForCoreNodeGone(coll.getName(), shardId, replicaName, 30000)) return Boolean.TRUE; return Boolean.FALSE; } catch (Exception e) { results.add("failure", "Could not complete delete " + e.getMessage()); @@ -275,7 +323,7 @@ public class DeleteReplicaCmd implements Cmd { try { if (!callable.call()) throw new SolrException(SolrException.ErrorCode.SERVER_ERROR, - "Could not remove replica : " + collectionName + "/" + shard + "/" + replicaName); + "Could not remove replica : " + coll.getName() + "/" + shardId + "/" + replicaName); } catch (InterruptedException | KeeperException e) { throw e; } catch (Exception ex) { diff --git a/solr/core/src/java/org/apache/solr/cluster/placement/DeleteReplicasRequest.java b/solr/core/src/java/org/apache/solr/cluster/placement/DeleteReplicasRequest.java new file mode 100644 index 0000000..09f5762 --- /dev/null +++ b/solr/core/src/java/org/apache/solr/cluster/placement/DeleteReplicasRequest.java @@ -0,0 +1,29 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.solr.cluster.placement; + +import org.apache.solr.cluster.Replica; + +import java.util.Set; + +/** + * + */ +public interface DeleteReplicasRequest extends ModificationRequest { + Set getReplicas(); +} diff --git a/solr/core/src/java/org/apache/solr/cluster/placement/DeleteShardsRequest.java b/solr/core/src/java/org/apache/solr/cluster/placement/DeleteShardsRequest.java new file mode 100644 index 0000000..5bff54f --- /dev/null +++ b/solr/core/src/java/org/apache/solr/cluster/placement/DeleteShardsRequest.java @@ -0,0 +1,27 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.solr.cluster.placement; + +import java.util.Set; + +/** + * + */ +public interface DeleteShardsRequest extends ModificationRequest { + Set getShardNames(); +} diff --git a/solr/core/src/java/org/apache/solr/cluster/placement/ModificationRequest.java b/solr/core/src/java/org/apache/solr/cluster/placement/ModificationRequest.java new file mode 100644 index 0000000..964d9bf --- /dev/null +++ b/solr/core/src/java/org/apache/solr/cluster/placement/ModificationRequest.java @@ -0,0 +1,30 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.solr.cluster.placement; + +import org.apache.solr.cluster.SolrCollection; + +/** + * + */ +public interface ModificationRequest { + /** + * The {@link SolrCollection} to modify. + */ + SolrCollection getCollection(); +} diff --git a/solr/core/src/java/org/apache/solr/cluster/placement/PlacementContext.java b/solr/core/src/java/org/apache/solr/cluster/placement/PlacementContext.java new file mode 100644 index 0000000..89deecd --- /dev/null +++ b/solr/core/src/java/org/apache/solr/cluster/placement/PlacementContext.java @@ -0,0 +1,44 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.solr.cluster.placement; + +import org.apache.solr.cluster.Cluster; + +/** + * + */ +public interface PlacementContext { + /** + * Initial state of the cluster. Note there are {@link java.util.Set}'s and {@link java.util.Map}'s + * accessible from the {@link Cluster} and other reachable instances. These collection will not change + * while the plugin is executing and will be thrown away once the plugin is done. The plugin code can + * therefore modify them if needed. + */ + Cluster getCluster(); + + /** + * Factory used by the plugin to fetch additional attributes from the cluster nodes, such as + * count of cores, system properties etc.. + */ + AttributeFetcher getAttributeFetcher(); + + /** + * Factory used to create instances of {@link PlacementPlan} to return computed decision. + */ + PlacementPlanFactory getPlacementPlanFactory(); +} diff --git a/solr/core/src/java/org/apache/solr/cluster/placement/PlacementPlugin.java b/solr/core/src/java/org/apache/solr/cluster/placement/PlacementPlugin.java index bbb52cb..c1e4c35 100644 --- a/solr/core/src/java/org/apache/solr/cluster/placement/PlacementPlugin.java +++ b/solr/core/src/java/org/apache/solr/cluster/placement/PlacementPlugin.java @@ -17,8 +17,6 @@ package org.apache.solr.cluster.placement; -import org.apache.solr.cluster.Cluster; - /** *

Implemented by external plugins to control replica placement and movement on the search cluster (as well as other things * such as cluster elasticity?) when cluster changes are required (initiated elsewhere, most likely following a Collection @@ -36,16 +34,11 @@ public interface PlacementPlugin { * *

Configuration is passed upon creation of a new instance of this class by {@link PlacementPluginFactory#createPluginInstance}. * - * @param cluster initial state of the cluster. Note there are {@link java.util.Set}'s and {@link java.util.Map}'s - * accessible from the {@link Cluster} and other reachable instances. These collection will not change - * while the plugin is executing and will be thrown away once the plugin is done. The plugin code can - * therefore modify them if needed. * @param placementRequest request for placing new replicas or moving existing replicas on the cluster. - * @param attributeFetcher Factory used by the plugin to fetch additional attributes from the cluster nodes, such as - * count of coresm ssytem properties etc.. - * @param placementPlanFactory Factory used to create instances of {@link PlacementPlan} to return computed decision. * @return plan satisfying the placement request. */ - PlacementPlan computePlacement(Cluster cluster, PlacementRequest placementRequest, AttributeFetcher attributeFetcher, - PlacementPlanFactory placementPlanFactory) throws PlacementException, InterruptedException; + PlacementPlan computePlacement(PlacementRequest placementRequest, PlacementContext placementContext) throws PlacementException, InterruptedException; + + void verifyAllowedModification(ModificationRequest modificationRequest, PlacementContext placementContext) + throws PlacementException, InterruptedException; } diff --git a/solr/core/src/java/org/apache/solr/cluster/placement/PlacementRequest.java b/solr/core/src/java/org/apache/solr/cluster/placement/PlacementRequest.java index 44222a2..0ece962 100644 --- a/solr/core/src/java/org/apache/solr/cluster/placement/PlacementRequest.java +++ b/solr/core/src/java/org/apache/solr/cluster/placement/PlacementRequest.java @@ -30,12 +30,7 @@ import java.util.Set; *

The set of {@link Node}s on which the replicas should be placed * is specified (defaults to being equal to the set returned by {@link Cluster#getLiveNodes()}). */ -public interface PlacementRequest { - /** - * The {@link SolrCollection} to add {@link Replica}(s) to. - */ - SolrCollection getCollection(); - +public interface PlacementRequest extends ModificationRequest { /** *

Shard name(s) for which new replicas placement should be computed. The shard(s) might exist or not (that's why this * method returns a {@link Set} of {@link String}'s and not directly a set of {@link Shard} instances). diff --git a/solr/core/src/java/org/apache/solr/cluster/placement/impl/ModificationRequestImpl.java b/solr/core/src/java/org/apache/solr/cluster/placement/impl/ModificationRequestImpl.java new file mode 100644 index 0000000..5161869 --- /dev/null +++ b/solr/core/src/java/org/apache/solr/cluster/placement/impl/ModificationRequestImpl.java @@ -0,0 +1,82 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.solr.cluster.placement.impl; + +import org.apache.solr.cluster.Replica; +import org.apache.solr.cluster.Shard; +import org.apache.solr.cluster.SolrCollection; +import org.apache.solr.cluster.placement.DeleteReplicasRequest; +import org.apache.solr.cluster.placement.DeleteShardsRequest; +import org.apache.solr.common.cloud.DocCollection; +import org.apache.solr.common.cloud.Slice; + +import java.util.HashSet; +import java.util.Set; + +/** + * + */ +public class ModificationRequestImpl { + + public static DeleteReplicasRequest deleteReplicasRequest(SolrCollection collection, Set replicas) { + return new DeleteReplicasRequest() { + @Override + public Set getReplicas() { + return replicas; + } + + @Override + public SolrCollection getCollection() { + return collection; + } + }; + } + + public static DeleteReplicasRequest deleteReplicasRequest(DocCollection docCollection, String shardName, Set replicaNames) { + SolrCollection solrCollection = SimpleClusterAbstractionsImpl.SolrCollectionImpl.fromDocCollection(docCollection); + Shard shard = solrCollection.getShard(shardName); + Slice slice = docCollection.getSlice(shardName); + Set solrReplicas = new HashSet<>(); + replicaNames.forEach(name -> { + org.apache.solr.common.cloud.Replica replica = slice.getReplica(name); + Replica solrReplica = new SimpleClusterAbstractionsImpl.ReplicaImpl(replica.getName(), shard, replica); + solrReplicas.add(solrReplica); + }); + return deleteReplicasRequest(solrCollection, solrReplicas); + } + + + public static DeleteShardsRequest deleteShardsRequest(SolrCollection collection, Set shardNames) { + return new DeleteShardsRequest() { + @Override + public Set getShardNames() { + return shardNames; + } + + @Override + public SolrCollection getCollection() { + return collection; + } + }; + } + + public static DeleteShardsRequest deleteShardsRequest(DocCollection docCollection, Set shardNames) { + SolrCollection solrCollection = SimpleClusterAbstractionsImpl.SolrCollectionImpl.fromDocCollection(docCollection); + return deleteShardsRequest(solrCollection, shardNames); + } +} diff --git a/solr/core/src/java/org/apache/solr/cluster/placement/impl/PlacementContextImpl.java b/solr/core/src/java/org/apache/solr/cluster/placement/impl/PlacementContextImpl.java new file mode 100644 index 0000000..eed649b --- /dev/null +++ b/solr/core/src/java/org/apache/solr/cluster/placement/impl/PlacementContextImpl.java @@ -0,0 +1,39 @@ +package org.apache.solr.cluster.placement.impl; + +import org.apache.solr.client.solrj.cloud.SolrCloudManager; +import org.apache.solr.cluster.Cluster; +import org.apache.solr.cluster.placement.AttributeFetcher; +import org.apache.solr.cluster.placement.PlacementContext; +import org.apache.solr.cluster.placement.PlacementPlanFactory; + +import java.io.IOException; + +/** + * + */ +public class PlacementContextImpl implements PlacementContext { + + private final Cluster cluster; + private final AttributeFetcher attributeFetcher; + private final PlacementPlanFactory placementPlanFactory = new PlacementPlanFactoryImpl(); + + public PlacementContextImpl(SolrCloudManager solrCloudManager) throws IOException { + cluster = new SimpleClusterAbstractionsImpl.ClusterImpl(solrCloudManager); + attributeFetcher = new AttributeFetcherImpl(solrCloudManager); + } + + @Override + public Cluster getCluster() { + return cluster; + } + + @Override + public AttributeFetcher getAttributeFetcher() { + return attributeFetcher; + } + + @Override + public PlacementPlanFactory getPlacementPlanFactory() { + return placementPlanFactory; + } +} diff --git a/solr/core/src/java/org/apache/solr/cluster/placement/impl/PlacementPluginAssignStrategy.java b/solr/core/src/java/org/apache/solr/cluster/placement/impl/PlacementPluginAssignStrategy.java index c4c5667..a226ab8 100644 --- a/solr/core/src/java/org/apache/solr/cluster/placement/impl/PlacementPluginAssignStrategy.java +++ b/solr/core/src/java/org/apache/solr/cluster/placement/impl/PlacementPluginAssignStrategy.java @@ -22,8 +22,8 @@ import java.util.List; import org.apache.solr.client.solrj.cloud.SolrCloudManager; import org.apache.solr.cloud.api.collections.Assign; -import org.apache.solr.cluster.Cluster; import org.apache.solr.cluster.SolrCollection; +import org.apache.solr.cluster.placement.PlacementContext; import org.apache.solr.cluster.placement.PlacementException; import org.apache.solr.cluster.placement.PlacementPlugin; import org.apache.solr.cluster.placement.PlacementPlan; @@ -35,8 +35,6 @@ import org.apache.solr.common.cloud.ReplicaPosition; */ public class PlacementPluginAssignStrategy implements Assign.AssignStrategy { - private static final PlacementPlanFactoryImpl PLACEMENT_PLAN_FACTORY = new PlacementPlanFactoryImpl(); - private final PlacementPlugin plugin; private final DocCollection collection; @@ -53,14 +51,14 @@ public class PlacementPluginAssignStrategy implements Assign.AssignStrategy { public List assign(SolrCloudManager solrCloudManager, Assign.AssignRequest assignRequest) throws Assign.AssignmentException, IOException, InterruptedException { - Cluster cluster = new SimpleClusterAbstractionsImpl.ClusterImpl(solrCloudManager); - SolrCollection solrCollection = new SimpleClusterAbstractionsImpl.SolrCollectionImpl(collection); + PlacementContext placementContext = new PlacementContextImpl(solrCloudManager); + SolrCollection solrCollection = placementContext.getCluster().getCollection(collection.getName()); - PlacementRequestImpl placementRequest = PlacementRequestImpl.toPlacementRequest(cluster, solrCollection, assignRequest); + PlacementRequestImpl placementRequest = PlacementRequestImpl.toPlacementRequest(placementContext.getCluster(), solrCollection, assignRequest); final PlacementPlan placementPlan; try { - placementPlan = plugin.computePlacement(cluster, placementRequest, new AttributeFetcherImpl(solrCloudManager), PLACEMENT_PLAN_FACTORY); + placementPlan = plugin.computePlacement(placementRequest, placementContext); } catch (PlacementException pe) { throw new Assign.AssignmentException(pe); } diff --git a/solr/core/src/java/org/apache/solr/cluster/placement/impl/SimpleClusterAbstractionsImpl.java b/solr/core/src/java/org/apache/solr/cluster/placement/impl/SimpleClusterAbstractionsImpl.java index e26a374..292fe5c 100644 --- a/solr/core/src/java/org/apache/solr/cluster/placement/impl/SimpleClusterAbstractionsImpl.java +++ b/solr/core/src/java/org/apache/solr/cluster/placement/impl/SimpleClusterAbstractionsImpl.java @@ -324,7 +324,7 @@ class SimpleClusterAbstractionsImpl { return new Pair<>(replicas, leader); } - private ReplicaImpl(String replicaName, Shard shard, org.apache.solr.common.cloud.Replica sliceReplica) { + ReplicaImpl(String replicaName, Shard shard, org.apache.solr.common.cloud.Replica sliceReplica) { this.replicaName = replicaName; this.coreName = sliceReplica.getCoreName(); this.shard = shard; @@ -334,7 +334,7 @@ class SimpleClusterAbstractionsImpl { this.node = new NodeImpl(sliceReplica.getNodeName()); } - private Replica.ReplicaType translateType(org.apache.solr.common.cloud.Replica.Type type) { + Replica.ReplicaType translateType(org.apache.solr.common.cloud.Replica.Type type) { switch (type) { case NRT: return Replica.ReplicaType.NRT; @@ -347,7 +347,7 @@ class SimpleClusterAbstractionsImpl { } } - private Replica.ReplicaState translateState(org.apache.solr.common.cloud.Replica.State state) { + Replica.ReplicaState translateState(org.apache.solr.common.cloud.Replica.State state) { switch (state) { case ACTIVE: return Replica.ReplicaState.ACTIVE; diff --git a/solr/core/src/java/org/apache/solr/cluster/placement/plugins/AffinityPlacementFactory.java b/solr/core/src/java/org/apache/solr/cluster/placement/plugins/AffinityPlacementFactory.java index ce79792..8a561c3 100644 --- a/solr/core/src/java/org/apache/solr/cluster/placement/plugins/AffinityPlacementFactory.java +++ b/solr/core/src/java/org/apache/solr/cluster/placement/plugins/AffinityPlacementFactory.java @@ -190,15 +190,16 @@ public class AffinityPlacementFactory implements PlacementPluginFactory nodes = request.getTargetNodes(); SolrCollection solrCollection = request.getCollection(); - nodes = filterNodesWithCollection(cluster, request, nodes); + nodes = filterNodesWithCollection(placementContext.getCluster(), request, nodes); // Request all needed attributes + AttributeFetcher attributeFetcher = placementContext.getAttributeFetcher(); attributeFetcher.requestNodeSystemProperty(AVAILABILITY_ZONE_SYSPROP).requestNodeSystemProperty(REPLICA_TYPE_SYSPROP); attributeFetcher .requestNodeMetric(NodeMetricImpl.NUM_CORES) @@ -243,11 +244,23 @@ public class AffinityPlacementFactory implements PlacementPluginFactory getZonesFromNodes(Set nodes, final AttributeValues attrValues) { diff --git a/solr/core/src/java/org/apache/solr/cluster/placement/plugins/MinimizeCoresPlacementFactory.java b/solr/core/src/java/org/apache/solr/cluster/placement/plugins/MinimizeCoresPlacementFactory.java index bb1e762..d8e1370 100644 --- a/solr/core/src/java/org/apache/solr/cluster/placement/plugins/MinimizeCoresPlacementFactory.java +++ b/solr/core/src/java/org/apache/solr/cluster/placement/plugins/MinimizeCoresPlacementFactory.java @@ -50,15 +50,15 @@ public class MinimizeCoresPlacementFactory implements PlacementPluginFactory nodes = request.getTargetNodes(); + AttributeFetcher attributeFetcher = placementContext.getAttributeFetcher(); attributeFetcher.requestNodeMetric(NodeMetricImpl.NUM_CORES); attributeFetcher.fetchFrom(nodes); AttributeValues attrValues = attributeFetcher.fetchAttributes(); @@ -106,11 +107,16 @@ public class MinimizeCoresPlacementFactory implements PlacementPluginFactory> nodeEntriesToAssign, diff --git a/solr/core/src/java/org/apache/solr/cluster/placement/plugins/RandomPlacementFactory.java b/solr/core/src/java/org/apache/solr/cluster/placement/plugins/RandomPlacementFactory.java index 0b27d21..065353a 100644 --- a/solr/core/src/java/org/apache/solr/cluster/placement/plugins/RandomPlacementFactory.java +++ b/solr/core/src/java/org/apache/solr/cluster/placement/plugins/RandomPlacementFactory.java @@ -53,14 +53,14 @@ public class RandomPlacementFactory implements PlacementPluginFactory nodesToAssign = new ArrayList<>(cluster.getLiveNodes()); + ArrayList nodesToAssign = new ArrayList<>(placementContext.getCluster().getLiveNodes()); Collections.shuffle(nodesToAssign, replicaPlacementRandom); for (Replica.ReplicaType rt : Replica.ReplicaType.values()) { - placeForReplicaType(request.getCollection(), nodesToAssign, placementPlanFactory, replicaPlacements, shardName, request, rt); + placeForReplicaType(request.getCollection(), nodesToAssign, placementContext.getPlacementPlanFactory(), replicaPlacements, shardName, request, rt); } } - return placementPlanFactory.createPlacementPlan(request, replicaPlacements); + return placementContext.getPlacementPlanFactory().createPlacementPlan(request, replicaPlacements); + } + + @Override + public void verifyAllowedModification(ModificationRequest modificationRequest, PlacementContext placementContext) throws PlacementException, InterruptedException { + // no-op } private void placeForReplicaType(SolrCollection solrCollection, ArrayList nodesToAssign, PlacementPlanFactory placementPlanFactory, diff --git a/solr/core/src/test/org/apache/solr/cluster/placement/Builders.java b/solr/core/src/test/org/apache/solr/cluster/placement/Builders.java index 21b8369..43de56e 100644 --- a/solr/core/src/test/org/apache/solr/cluster/placement/Builders.java +++ b/solr/core/src/test/org/apache/solr/cluster/placement/Builders.java @@ -22,6 +22,7 @@ import org.apache.solr.cluster.placement.impl.AttributeFetcherImpl; import org.apache.solr.cluster.placement.impl.AttributeValuesImpl; import org.apache.solr.cluster.placement.impl.CollectionMetricsBuilder; import org.apache.solr.cluster.placement.impl.NodeMetricImpl; +import org.apache.solr.cluster.placement.impl.PlacementPlanFactoryImpl; import org.apache.solr.cluster.placement.impl.ReplicaMetricImpl; import org.apache.solr.common.util.Pair; import org.junit.Assert; @@ -92,6 +93,29 @@ public class Builders { return clusterCollections; } + private static final PlacementPlanFactory PLACEMENT_PLAN_FACTORY = new PlacementPlanFactoryImpl(); + + public PlacementContext buildPlacementContext() { + Cluster cluster = build(); + AttributeFetcher attributeFetcher = buildAttributeFetcher(); + return new PlacementContext() { + @Override + public Cluster getCluster() { + return cluster; + } + + @Override + public AttributeFetcher getAttributeFetcher() { + return attributeFetcher; + } + + @Override + public PlacementPlanFactory getPlacementPlanFactory() { + return PLACEMENT_PLAN_FACTORY; + } + }; + } + public AttributeFetcher buildAttributeFetcher() { Map> sysprops = new HashMap<>(); Map, Map> metrics = new HashMap<>(); diff --git a/solr/core/src/test/org/apache/solr/cluster/placement/plugins/AffinityPlacementFactoryTest.java b/solr/core/src/test/org/apache/solr/cluster/placement/plugins/AffinityPlacementFactoryTest.java index 80c9b1d..c5cfe2e 100644 --- a/solr/core/src/test/org/apache/solr/cluster/placement/plugins/AffinityPlacementFactoryTest.java +++ b/solr/core/src/test/org/apache/solr/cluster/placement/plugins/AffinityPlacementFactoryTest.java @@ -98,8 +98,7 @@ public class AffinityPlacementFactoryTest extends SolrTestCaseJ4 { collectionBuilder.initializeShardsReplicas(1, 0, 0, 0, List.of()); } - Cluster cluster = clusterBuilder.build(); - AttributeFetcher attributeFetcher = clusterBuilder.buildAttributeFetcher(); + PlacementContext placementContext = clusterBuilder.buildPlacementContext(); SolrCollection solrCollection = collectionBuilder.build(); List liveNodes = clusterBuilder.buildLiveNodes(); @@ -109,7 +108,7 @@ public class AffinityPlacementFactoryTest extends SolrTestCaseJ4 { Set.of(solrCollection.shards().iterator().next().getShardName()), new HashSet<>(liveNodes), 1, 0, 0); - PlacementPlan pp = plugin.computePlacement(cluster, placementRequest, attributeFetcher, new PlacementPlanFactoryImpl()); + PlacementPlan pp = plugin.computePlacement(placementRequest, placementContext); assertEquals(1, pp.getReplicaPlacements().size()); ReplicaPlacement rp = pp.getReplicaPlacements().iterator().next(); @@ -149,7 +148,7 @@ public class AffinityPlacementFactoryTest extends SolrTestCaseJ4 { PlacementRequestImpl placementRequest = new PlacementRequestImpl(solrCollection, solrCollection.getShardNames(), new HashSet<>(liveNodes), 2, 2, 2); - PlacementPlan pp = plugin.computePlacement(clusterBuilder.build(), placementRequest, clusterBuilder.buildAttributeFetcher(), new PlacementPlanFactoryImpl()); + PlacementPlan pp = plugin.computePlacement(placementRequest, clusterBuilder.buildPlacementContext()); assertEquals(18, pp.getReplicaPlacements().size()); // 3 shards, 6 replicas total each Set> placements = new HashSet<>(); @@ -162,7 +161,7 @@ public class AffinityPlacementFactoryTest extends SolrTestCaseJ4 { // Verify that if we ask for 7 replicas, the placement will use the low free space node placementRequest = new PlacementRequestImpl(solrCollection, solrCollection.getShardNames(), new HashSet<>(liveNodes), 7, 0, 0); - pp = plugin.computePlacement(clusterBuilder.build(), placementRequest, clusterBuilder.buildAttributeFetcher(), new PlacementPlanFactoryImpl()); + pp = plugin.computePlacement(placementRequest, clusterBuilder.buildPlacementContext()); assertEquals(21, pp.getReplicaPlacements().size()); // 3 shards, 7 replicas each placements = new HashSet<>(); for (ReplicaPlacement rp : pp.getReplicaPlacements()) { @@ -175,7 +174,7 @@ public class AffinityPlacementFactoryTest extends SolrTestCaseJ4 { try { placementRequest = new PlacementRequestImpl(solrCollection, solrCollection.getShardNames(), new HashSet<>(liveNodes), 8, 0, 0); - plugin.computePlacement(clusterBuilder.build(), placementRequest, clusterBuilder.buildAttributeFetcher(), new PlacementPlanFactoryImpl()); + plugin.computePlacement(placementRequest, clusterBuilder.buildPlacementContext()); fail("Placing 8 replicas should not be possible given only 7 nodes have enough space"); } catch (PlacementException e) { // expected @@ -219,7 +218,7 @@ public class AffinityPlacementFactoryTest extends SolrTestCaseJ4 { // The replicas must be placed on the most appropriate nodes, i.e. those that do not already have a replica for the // shard and then on the node with the lowest number of cores. // NRT are placed first and given the cluster state here the placement is deterministic (easier to test, only one good placement). - PlacementPlan pp = plugin.computePlacement(clusterBuilder.build(), placementRequest, clusterBuilder.buildAttributeFetcher(), new PlacementPlanFactoryImpl()); + PlacementPlan pp = plugin.computePlacement(placementRequest, clusterBuilder.buildPlacementContext()); // Each expected placement is represented as a string "shard replica-type node" Set expectedPlacements = Set.of("1 NRT 1", "1 TLOG 2", "2 NRT 0", "2 TLOG 4"); @@ -317,7 +316,7 @@ public class AffinityPlacementFactoryTest extends SolrTestCaseJ4 { // Add 2 NRT and one TLOG to each shard. PlacementRequestImpl placementRequest = new PlacementRequestImpl(solrCollection, solrCollection.getShardNames(), new HashSet<>(liveNodes), 2, 1, 0); - PlacementPlan pp = plugin.computePlacement(clusterBuilder.build(), placementRequest, clusterBuilder.buildAttributeFetcher(), new PlacementPlanFactoryImpl()); + PlacementPlan pp = plugin.computePlacement(placementRequest, clusterBuilder.buildPlacementContext()); // Shard 1: The NRT's should go to the med cores node on AZ2 and low core on az3 (even though // a low core node can take the replica in az1, there's already an NRT replica there and we want spreading across AZ's), // the TLOG to the TLOG node on AZ2 (because the tlog node on AZ1 has low free disk) @@ -331,7 +330,7 @@ public class AffinityPlacementFactoryTest extends SolrTestCaseJ4 { // If we add instead 2 PULL replicas to each shard placementRequest = new PlacementRequestImpl(solrCollection, solrCollection.getShardNames(), new HashSet<>(liveNodes), 0, 0, 2); - pp = plugin.computePlacement(clusterBuilder.build(), placementRequest, clusterBuilder.buildAttributeFetcher(), new PlacementPlanFactoryImpl()); + pp = plugin.computePlacement(placementRequest, clusterBuilder.buildPlacementContext()); // Shard 1: Given node AZ3_TLOGPULL is taken by the TLOG replica, the PULL should go to AZ1_TLOGPULL_LOWFREEDISK and AZ2_TLOGPULL // Shard 2: Similarly AZ2_TLOGPULL is taken. Replicas should go to AZ1_TLOGPULL_LOWFREEDISK and AZ3_TLOGPULL expectedPlacements = Set.of("1 PULL " + AZ1_TLOGPULL_LOWFREEDISK, "1 PULL " + AZ2_TLOGPULL, @@ -378,7 +377,7 @@ public class AffinityPlacementFactoryTest extends SolrTestCaseJ4 { for (int countNrtToPlace = 1; countNrtToPlace <= 9; countNrtToPlace++) { PlacementRequestImpl placementRequest = new PlacementRequestImpl(solrCollection, solrCollection.getShardNames(), new HashSet<>(liveNodes), countNrtToPlace, 0, 0); - PlacementPlan pp = plugin.computePlacement(clusterBuilder.build(), placementRequest, clusterBuilder.buildAttributeFetcher(), new PlacementPlanFactoryImpl()); + PlacementPlan pp = plugin.computePlacement(placementRequest, clusterBuilder.buildPlacementContext()); verifyPlacements(placements.get(countNrtToPlace - 1), pp, collectionBuilder.getShardBuilders(), liveNodes); } } @@ -414,7 +413,7 @@ public class AffinityPlacementFactoryTest extends SolrTestCaseJ4 { PlacementRequestImpl placementRequest = new PlacementRequestImpl(solrCollection, Set.of(solrCollection.iterator().next().getShardName()), new HashSet<>(liveNodes), 0, 0, 1); - PlacementPlan pp = plugin.computePlacement(clusterBuilder.build(), placementRequest, clusterBuilder.buildAttributeFetcher(), new PlacementPlanFactoryImpl()); + PlacementPlan pp = plugin.computePlacement(placementRequest, clusterBuilder.buildPlacementContext()); // Each expected placement is represented as a string "shard replica-type node" // Node 0 has less cores than node 1 (0 vs 1) so the placement should go there. @@ -427,7 +426,7 @@ public class AffinityPlacementFactoryTest extends SolrTestCaseJ4 { it.next(); // skip first shard to do placement for the second one... placementRequest = new PlacementRequestImpl(solrCollection, Set.of(it.next().getShardName()), new HashSet<>(liveNodes), 0, 0, 1); - pp = plugin.computePlacement(clusterBuilder.build(), placementRequest, clusterBuilder.buildAttributeFetcher(), new PlacementPlanFactoryImpl()); + pp = plugin.computePlacement(placementRequest, clusterBuilder.buildPlacementContext()); expectedPlacements = Set.of("2 PULL 0"); verifyPlacements(expectedPlacements, pp, collectionBuilder.getShardBuilders(), liveNodes); } @@ -510,7 +509,8 @@ public class AffinityPlacementFactoryTest extends SolrTestCaseJ4 { collectionBuilder.initializeShardsReplicas(2, 0, 0, 0, clusterBuilder.getLiveNodeBuilders()); clusterBuilder.addCollection(collectionBuilder); - Cluster cluster = clusterBuilder.build(); + PlacementContext placementContext = clusterBuilder.buildPlacementContext(); + Cluster cluster = placementContext.getCluster(); SolrCollection solrCollection = cluster.getCollection(collectionName); @@ -519,14 +519,12 @@ public class AffinityPlacementFactoryTest extends SolrTestCaseJ4 { .map(Shard::getShardName).collect(Collectors.toSet()), cluster.getLiveNodes(), 2, 2, 2); - PlacementPlanFactory placementPlanFactory = new PlacementPlanFactoryImpl(); - AttributeFetcher attributeFetcher = clusterBuilder.buildAttributeFetcher(); - PlacementPlan pp = plugin.computePlacement(cluster, placementRequest, attributeFetcher, placementPlanFactory); + PlacementPlan pp = plugin.computePlacement(placementRequest, placementContext); // 2 shards, 6 replicas assertEquals(12, pp.getReplicaPlacements().size()); // shard -> AZ -> replica count Map>> replicas = new HashMap<>(); - AttributeValues attributeValues = attributeFetcher.fetchAttributes(); + AttributeValues attributeValues = placementContext.getAttributeFetcher().fetchAttributes(); for (ReplicaPlacement rp : pp.getReplicaPlacements()) { Optional azOptional = attributeValues.getSystemProperty(rp.getNode(), AffinityPlacementFactory.AVAILABILITY_ZONE_SYSPROP); if (!azOptional.isPresent()) { @@ -570,7 +568,8 @@ public class AffinityPlacementFactoryTest extends SolrTestCaseJ4 { collectionBuilder.initializeShardsReplicas(2, 0, 0, 0, clusterBuilder.getLiveNodeBuilders()); clusterBuilder.addCollection(collectionBuilder); - Cluster cluster = clusterBuilder.build(); + PlacementContext placementContext = clusterBuilder.buildPlacementContext(); + Cluster cluster = placementContext.getCluster(); SolrCollection solrCollection = cluster.getCollection(collectionName); @@ -579,14 +578,12 @@ public class AffinityPlacementFactoryTest extends SolrTestCaseJ4 { .map(Shard::getShardName).collect(Collectors.toSet()), cluster.getLiveNodes(), 2, 2, 2); - PlacementPlanFactory placementPlanFactory = new PlacementPlanFactoryImpl(); - AttributeFetcher attributeFetcher = clusterBuilder.buildAttributeFetcher(); - PlacementPlan pp = plugin.computePlacement(cluster, placementRequest, attributeFetcher, placementPlanFactory); + PlacementPlan pp = plugin.computePlacement(placementRequest, placementContext); // 2 shards, 6 replicas assertEquals(12, pp.getReplicaPlacements().size()); // shard -> group -> replica count Map>> replicas = new HashMap<>(); - AttributeValues attributeValues = attributeFetcher.fetchAttributes(); + AttributeValues attributeValues = placementContext.getAttributeFetcher().fetchAttributes(); for (ReplicaPlacement rp : pp.getReplicaPlacements()) { Optional groupOptional = attributeValues.getSystemProperty(rp.getNode(), "group"); if (!groupOptional.isPresent()) { @@ -637,7 +634,8 @@ public class AffinityPlacementFactoryTest extends SolrTestCaseJ4 { collectionBuilder.initializeShardsReplicas(2, 0, 0, 0, clusterBuilder.getLiveNodeBuilders()); clusterBuilder.addCollection(collectionBuilder); - Cluster cluster = clusterBuilder.build(); + PlacementContext placementContext = clusterBuilder.buildPlacementContext(); + Cluster cluster = placementContext.getCluster(); SolrCollection solrCollection = cluster.getCollection(collectionName); @@ -646,9 +644,7 @@ public class AffinityPlacementFactoryTest extends SolrTestCaseJ4 { .map(Shard::getShardName).collect(Collectors.toSet()), cluster.getLiveNodes(), 1, 0, 1); - PlacementPlanFactory placementPlanFactory = new PlacementPlanFactoryImpl(); - AttributeFetcher attributeFetcher = clusterBuilder.buildAttributeFetcher(); - PlacementPlan pp = plugin.computePlacement(cluster, placementRequest, attributeFetcher, placementPlanFactory); + PlacementPlan pp = plugin.computePlacement(placementRequest, placementContext); assertEquals(4, pp.getReplicaPlacements().size()); for (ReplicaPlacement rp : pp.getReplicaPlacements()) { assertFalse("should not put any replicas on " + smallNode, rp.getNode().equals(smallNode)); @@ -667,7 +663,8 @@ public class AffinityPlacementFactoryTest extends SolrTestCaseJ4 { collectionBuilder.initializeShardsReplicas(0, 0, 0, 0, clusterBuilder.getLiveNodeBuilders()); clusterBuilder.addCollection(collectionBuilder); - Cluster cluster = clusterBuilder.build(); + PlacementContext placementContext = clusterBuilder.buildPlacementContext(); + Cluster cluster = placementContext.getCluster(); SolrCollection secondaryCollection = cluster.getCollection(secondaryCollectionName); SolrCollection primaryCollection = cluster.getCollection(primaryCollectionName); @@ -679,9 +676,7 @@ public class AffinityPlacementFactoryTest extends SolrTestCaseJ4 { Set.of("shard1", "shard2"), cluster.getLiveNodes(), 1, 0, 0); - PlacementPlanFactory placementPlanFactory = new PlacementPlanFactoryImpl(); - AttributeFetcher attributeFetcher = clusterBuilder.buildAttributeFetcher(); - PlacementPlan pp = plugin.computePlacement(cluster, placementRequest, attributeFetcher, placementPlanFactory); + PlacementPlan pp = plugin.computePlacement(placementRequest, placementContext); assertEquals(2, pp.getReplicaPlacements().size()); // verify that all placements are on nodes with the secondary replica pp.getReplicaPlacements().forEach(placement -> @@ -691,7 +686,7 @@ public class AffinityPlacementFactoryTest extends SolrTestCaseJ4 { placementRequest = new PlacementRequestImpl(primaryCollection, Set.of("shard1"), cluster.getLiveNodes(), 3, 0, 0); try { - pp = plugin.computePlacement(cluster, placementRequest, attributeFetcher, placementPlanFactory); + pp = plugin.computePlacement(placementRequest, placementContext); fail("should generate 'Not enough eligible nodes' failure here"); } catch (PlacementException pe) { assertTrue(pe.toString().contains("Not enough eligible nodes")); @@ -732,9 +727,7 @@ public class AffinityPlacementFactoryTest extends SolrTestCaseJ4 { Builders.CollectionBuilder collectionBuilder = Builders.newCollectionBuilder(collectionName); collectionBuilder.initializeShardsReplicas(numShards, 0, 0, 0, List.of()); - Cluster cluster = clusterBuilder.build(); - AttributeFetcher attributeFetcher = clusterBuilder.buildAttributeFetcher(); - + PlacementContext placementContext = clusterBuilder.buildPlacementContext(); SolrCollection solrCollection = collectionBuilder.build(); List liveNodes = clusterBuilder.buildLiveNodes(); @@ -743,7 +736,7 @@ public class AffinityPlacementFactoryTest extends SolrTestCaseJ4 { new HashSet<>(liveNodes), nrtReplicas, tlogReplicas, pullReplicas); long start = System.nanoTime(); - PlacementPlan pp = plugin.computePlacement(cluster, placementRequest, attributeFetcher, new PlacementPlanFactoryImpl()); + PlacementPlan pp = plugin.computePlacement(placementRequest, placementContext); long end = System.nanoTime(); final int REPLICAS_PER_SHARD = nrtReplicas + tlogReplicas + pullReplicas;