You are viewing a plain text version of this content. The canonical link for it is here.
Posted to issues@solr.apache.org by "pjmcarthur (via GitHub)" <gi...@apache.org> on 2023/09/14 20:45:31 UTC
[GitHub] [solr] pjmcarthur opened a new pull request, #1926: SOLR-16403: Cluster Singleton to remove inactive Shards
pjmcarthur opened a new pull request, #1926:
URL: https://github.com/apache/solr/pull/1926
https://issues.apache.org/jira/browse/SOLR-16403
# Description
This PR proposes a configurable cluster singleton plugin to remove inactive shards.
I'd like to get some feedback from the community on whether this is a reasonable thing to do, or if there are other preferred alternatives.
# Solution
Adds a Cluster Singleton plugin implementation that can be configured to run periodically and delete inactive shards that have exceeded a TTL period.
# Tests
Tests are included. Coverage may be expanded if we move forward
# Checklist
Please review the following and check all that apply:
- [x] I have reviewed the guidelines for [How to Contribute](https://wiki.apache.org/solr/HowToContribute) and my code conforms to the standards described there to the best of my ability.
- [x] I have created a Jira issue and added the issue ID to my pull request title.
- [x] I have given Solr maintainers [access](https://help.github.com/en/articles/allowing-changes-to-a-pull-request-branch-created-from-a-fork) to contribute to my PR branch. (optional but recommended)
- [x] I have developed this patch against the `main` branch.
- [x] I have run `./gradlew check`.
- [x] I have added tests for my changes.
- [ ] I have added documentation for the [Reference Guide](https://github.com/apache/solr/tree/main/solr/solr-ref-guide)
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: issues-unsubscribe@solr.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: issues-unsubscribe@solr.apache.org
For additional commands, e-mail: issues-help@solr.apache.org
Re: [PR] SOLR-16403: Cluster Singleton to remove inactive Shards [solr]
Posted by "dsmiley (via GitHub)" <gi...@apache.org>.
dsmiley commented on PR #1926:
URL: https://github.com/apache/solr/pull/1926#issuecomment-1992745371
I already reported org.apache.solr.cloud.OverseerStatusTest.test -- https://github.com/apache/solr/pull/2230#issuecomment-1984524318
BTW this new BitoAgent AI bot reviewer thing is wild. Not sure it's that helpful.
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: issues-unsubscribe@solr.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: issues-unsubscribe@solr.apache.org
For additional commands, e-mail: issues-help@solr.apache.org
[GitHub] [solr] pjmcarthur commented on a diff in pull request #1926: SOLR-16403: Cluster Singleton to remove inactive Shards
Posted by "pjmcarthur (via GitHub)" <gi...@apache.org>.
pjmcarthur commented on code in PR #1926:
URL: https://github.com/apache/solr/pull/1926#discussion_r1330310547
##########
solr/core/src/java/org/apache/solr/cluster/maintenance/InactiveShardRemover.java:
##########
@@ -0,0 +1,251 @@
+/*
+ * 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.maintenance;
+
+import com.google.common.annotations.VisibleForTesting;
+import java.io.IOException;
+import java.lang.invoke.MethodHandles;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.TimeUnit;
+import java.util.stream.Collectors;
+import org.apache.solr.api.ConfigurablePlugin;
+import org.apache.solr.client.solrj.request.CollectionAdminRequest;
+import org.apache.solr.client.solrj.response.CollectionAdminResponse;
+import org.apache.solr.client.solrj.response.RequestStatusState;
+import org.apache.solr.cloud.ClusterSingleton;
+import org.apache.solr.common.cloud.ClusterState;
+import org.apache.solr.common.cloud.DocCollection;
+import org.apache.solr.common.cloud.Slice;
+import org.apache.solr.common.cloud.ZkStateReader;
+import org.apache.solr.common.util.SolrNamedThreadFactory;
+import org.apache.solr.core.CoreContainer;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/** This Cluster Singleton can be configured to periodically find and remove inactive Shards */
+public class InactiveShardRemover
+ implements ClusterSingleton, ConfigurablePlugin<InactiveShardRemoverConfig> {
+
+ private static final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
+
+ public static final String PLUGIN_NAME = ".inactive-shard-remover";
+
+ static class DeleteActor {
+
+ private final CoreContainer coreContainer;
+
+ DeleteActor(final CoreContainer coreContainer) {
+ this.coreContainer = coreContainer;
+ }
+
+ void delete(final Slice slice, final String asyncId) throws IOException {
+ CollectionAdminRequest.DeleteShard deleteRequest =
+ CollectionAdminRequest.deleteShard(slice.getCollection(), slice.getName());
+ deleteRequest.setAsyncId(asyncId);
+ coreContainer.getZkController().getSolrCloudManager().request(deleteRequest);
+ }
+ }
+
+ private State state = State.STOPPED;
+
+ private final CoreContainer coreContainer;
+
+ private final DeleteActor deleteActor;
+
+ private ScheduledExecutorService executor;
+
+ private long scheduleIntervalSeconds;
+
+ private long ttlSeconds;
+
+ private int maxDeletesPerCycle;
+
+ /** Constructor invoked via Reflection */
+ public InactiveShardRemover(final CoreContainer cc) {
+ this(cc, new DeleteActor(cc));
+ }
+
+ @VisibleForTesting
+ InactiveShardRemover(final CoreContainer cc, final DeleteActor actor) {
+ this.coreContainer = cc;
+ this.deleteActor = actor;
+ }
+
+ @Override
+ public void configure(final InactiveShardRemoverConfig cfg) {
+ this.scheduleIntervalSeconds = cfg.scheduleIntervalSeconds;
+ this.maxDeletesPerCycle = cfg.maxDeletesPerCycle;
+ this.ttlSeconds = cfg.ttlSeconds;
+ }
+
+ @Override
+ public String getName() {
+ return PLUGIN_NAME;
+ }
+
+ @Override
+ public State getState() {
+ return state;
+ }
+
+ @Override
+ public void start() throws Exception {
+ state = State.STARTING;
+ executor = Executors.newScheduledThreadPool(1, new SolrNamedThreadFactory(PLUGIN_NAME));
+ executor.scheduleAtFixedRate(
+ this::deleteInactiveSlices,
+ scheduleIntervalSeconds,
+ scheduleIntervalSeconds,
+ TimeUnit.SECONDS);
+ state = State.RUNNING;
+ }
+
+ @Override
+ public void stop() {
+ if (state == State.RUNNING) {
+ state = State.STOPPING;
+ executor.shutdownNow();
+ try {
+ if (!executor.awaitTermination(10, TimeUnit.SECONDS)) {
+ log.warn(
+ "Executor pool did not terminate within the specified timeout: {} {}",
+ 10,
+ TimeUnit.SECONDS);
+ }
+ } catch (InterruptedException e) {
+ log.warn("Failed to shut down the executor pool", e);
+ Thread.currentThread().interrupt();
+ }
+ }
+ state = State.STOPPED;
+ }
+
+ @VisibleForTesting
+ void deleteInactiveSlices() {
+ final ClusterState clusterState = coreContainer.getZkController().getClusterState();
+ Collection<Slice> inactiveSlices = new HashSet<>();
+ clusterState
+ .getCollectionsMap()
+ .forEach((k, v) -> inactiveSlices.addAll(collectInactiveSlices(v)));
+
+ if (log.isInfoEnabled()) {
+ log.info(
+ "Found {} inactive Shards to delete, {} will be deleted",
+ inactiveSlices.size(),
+ Math.max(inactiveSlices.size(), maxDeletesPerCycle));
+ }
+
+ inactiveSlices.stream().limit(maxDeletesPerCycle).forEach(this::deleteShard);
+ }
+
+ private Collection<Slice> collectInactiveSlices(final DocCollection docCollection) {
+ final Collection<Slice> slices = new HashSet<>(docCollection.getSlices());
+ slices.removeAll(docCollection.getActiveSlices());
+ return slices.stream().filter(this::isExpired).collect(Collectors.toList());
+ }
+
+ private void deleteShard(final Slice s) {
+ final String asyncId = s.getCollection() + ";" + s.getName() + ";DELETESHARD";
Review Comment:
Honestly I was somewhat torn between the two. I chose async because I don't think the implementation really cares about waiting for the result of the delete operation, and a fire-and-forget approach seemed suitable. On the other hand, async makes testing a little more difficult. Happy to accept a recommendation or some guidance on the preferred style here.
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: issues-unsubscribe@solr.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: issues-unsubscribe@solr.apache.org
For additional commands, e-mail: issues-help@solr.apache.org
[GitHub] [solr] dsmiley commented on a diff in pull request #1926: SOLR-16403: Cluster Singleton to remove inactive Shards
Posted by "dsmiley (via GitHub)" <gi...@apache.org>.
dsmiley commented on code in PR #1926:
URL: https://github.com/apache/solr/pull/1926#discussion_r1329571065
##########
solr/core/src/java/org/apache/solr/cluster/maintenance/InactiveShardRemover.java:
##########
@@ -0,0 +1,251 @@
+/*
+ * 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.maintenance;
+
+import com.google.common.annotations.VisibleForTesting;
+import java.io.IOException;
+import java.lang.invoke.MethodHandles;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.TimeUnit;
+import java.util.stream.Collectors;
+import org.apache.solr.api.ConfigurablePlugin;
+import org.apache.solr.client.solrj.request.CollectionAdminRequest;
+import org.apache.solr.client.solrj.response.CollectionAdminResponse;
+import org.apache.solr.client.solrj.response.RequestStatusState;
+import org.apache.solr.cloud.ClusterSingleton;
+import org.apache.solr.common.cloud.ClusterState;
+import org.apache.solr.common.cloud.DocCollection;
+import org.apache.solr.common.cloud.Slice;
+import org.apache.solr.common.cloud.ZkStateReader;
+import org.apache.solr.common.util.SolrNamedThreadFactory;
+import org.apache.solr.core.CoreContainer;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/** This Cluster Singleton can be configured to periodically find and remove inactive Shards */
+public class InactiveShardRemover
+ implements ClusterSingleton, ConfigurablePlugin<InactiveShardRemoverConfig> {
+
+ private static final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
+
+ public static final String PLUGIN_NAME = ".inactive-shard-remover";
+
+ static class DeleteActor {
+
+ private final CoreContainer coreContainer;
+
+ DeleteActor(final CoreContainer coreContainer) {
+ this.coreContainer = coreContainer;
+ }
+
+ void delete(final Slice slice, final String asyncId) throws IOException {
+ CollectionAdminRequest.DeleteShard deleteRequest =
+ CollectionAdminRequest.deleteShard(slice.getCollection(), slice.getName());
+ deleteRequest.setAsyncId(asyncId);
+ coreContainer.getZkController().getSolrCloudManager().request(deleteRequest);
+ }
+ }
+
+ private State state = State.STOPPED;
+
+ private final CoreContainer coreContainer;
+
+ private final DeleteActor deleteActor;
+
+ private ScheduledExecutorService executor;
+
+ private long scheduleIntervalSeconds;
+
+ private long ttlSeconds;
+
+ private int maxDeletesPerCycle;
+
+ /** Constructor invoked via Reflection */
+ public InactiveShardRemover(final CoreContainer cc) {
+ this(cc, new DeleteActor(cc));
+ }
+
+ @VisibleForTesting
+ InactiveShardRemover(final CoreContainer cc, final DeleteActor actor) {
+ this.coreContainer = cc;
+ this.deleteActor = actor;
+ }
+
+ @Override
+ public void configure(final InactiveShardRemoverConfig cfg) {
+ this.scheduleIntervalSeconds = cfg.scheduleIntervalSeconds;
+ this.maxDeletesPerCycle = cfg.maxDeletesPerCycle;
+ this.ttlSeconds = cfg.ttlSeconds;
+ }
+
+ @Override
+ public String getName() {
+ return PLUGIN_NAME;
+ }
+
+ @Override
+ public State getState() {
+ return state;
+ }
+
+ @Override
+ public void start() throws Exception {
+ state = State.STARTING;
+ executor = Executors.newScheduledThreadPool(1, new SolrNamedThreadFactory(PLUGIN_NAME));
+ executor.scheduleAtFixedRate(
+ this::deleteInactiveSlices,
+ scheduleIntervalSeconds,
+ scheduleIntervalSeconds,
+ TimeUnit.SECONDS);
+ state = State.RUNNING;
+ }
+
+ @Override
+ public void stop() {
+ if (state == State.RUNNING) {
+ state = State.STOPPING;
+ executor.shutdownNow();
Review Comment:
We've got utilities in ExecutorUtil for shutting down executors
##########
solr/core/src/java/org/apache/solr/cluster/maintenance/InactiveShardRemover.java:
##########
@@ -0,0 +1,251 @@
+/*
+ * 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.maintenance;
+
+import com.google.common.annotations.VisibleForTesting;
+import java.io.IOException;
+import java.lang.invoke.MethodHandles;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.TimeUnit;
+import java.util.stream.Collectors;
+import org.apache.solr.api.ConfigurablePlugin;
+import org.apache.solr.client.solrj.request.CollectionAdminRequest;
+import org.apache.solr.client.solrj.response.CollectionAdminResponse;
+import org.apache.solr.client.solrj.response.RequestStatusState;
+import org.apache.solr.cloud.ClusterSingleton;
+import org.apache.solr.common.cloud.ClusterState;
+import org.apache.solr.common.cloud.DocCollection;
+import org.apache.solr.common.cloud.Slice;
+import org.apache.solr.common.cloud.ZkStateReader;
+import org.apache.solr.common.util.SolrNamedThreadFactory;
+import org.apache.solr.core.CoreContainer;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/** This Cluster Singleton can be configured to periodically find and remove inactive Shards */
Review Comment:
I would @-link the state reference and furthermore mention that this is related to shard splits. It's not obvious to anyone not doing shard splits.
##########
solr/core/src/java/org/apache/solr/cluster/maintenance/InactiveShardRemover.java:
##########
@@ -0,0 +1,251 @@
+/*
+ * 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.maintenance;
+
+import com.google.common.annotations.VisibleForTesting;
+import java.io.IOException;
+import java.lang.invoke.MethodHandles;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.TimeUnit;
+import java.util.stream.Collectors;
+import org.apache.solr.api.ConfigurablePlugin;
+import org.apache.solr.client.solrj.request.CollectionAdminRequest;
+import org.apache.solr.client.solrj.response.CollectionAdminResponse;
+import org.apache.solr.client.solrj.response.RequestStatusState;
+import org.apache.solr.cloud.ClusterSingleton;
+import org.apache.solr.common.cloud.ClusterState;
+import org.apache.solr.common.cloud.DocCollection;
+import org.apache.solr.common.cloud.Slice;
+import org.apache.solr.common.cloud.ZkStateReader;
+import org.apache.solr.common.util.SolrNamedThreadFactory;
+import org.apache.solr.core.CoreContainer;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/** This Cluster Singleton can be configured to periodically find and remove inactive Shards */
+public class InactiveShardRemover
+ implements ClusterSingleton, ConfigurablePlugin<InactiveShardRemoverConfig> {
+
+ private static final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
+
+ public static final String PLUGIN_NAME = ".inactive-shard-remover";
+
+ static class DeleteActor {
+
+ private final CoreContainer coreContainer;
+
+ DeleteActor(final CoreContainer coreContainer) {
+ this.coreContainer = coreContainer;
+ }
+
+ void delete(final Slice slice, final String asyncId) throws IOException {
+ CollectionAdminRequest.DeleteShard deleteRequest =
+ CollectionAdminRequest.deleteShard(slice.getCollection(), slice.getName());
+ deleteRequest.setAsyncId(asyncId);
+ coreContainer.getZkController().getSolrCloudManager().request(deleteRequest);
+ }
+ }
+
+ private State state = State.STOPPED;
+
+ private final CoreContainer coreContainer;
+
+ private final DeleteActor deleteActor;
+
+ private ScheduledExecutorService executor;
+
+ private long scheduleIntervalSeconds;
+
+ private long ttlSeconds;
+
+ private int maxDeletesPerCycle;
+
+ /** Constructor invoked via Reflection */
+ public InactiveShardRemover(final CoreContainer cc) {
+ this(cc, new DeleteActor(cc));
+ }
+
+ @VisibleForTesting
+ InactiveShardRemover(final CoreContainer cc, final DeleteActor actor) {
+ this.coreContainer = cc;
+ this.deleteActor = actor;
+ }
+
+ @Override
+ public void configure(final InactiveShardRemoverConfig cfg) {
+ this.scheduleIntervalSeconds = cfg.scheduleIntervalSeconds;
+ this.maxDeletesPerCycle = cfg.maxDeletesPerCycle;
+ this.ttlSeconds = cfg.ttlSeconds;
+ }
+
+ @Override
+ public String getName() {
+ return PLUGIN_NAME;
+ }
+
+ @Override
+ public State getState() {
+ return state;
+ }
+
+ @Override
+ public void start() throws Exception {
+ state = State.STARTING;
+ executor = Executors.newScheduledThreadPool(1, new SolrNamedThreadFactory(PLUGIN_NAME));
+ executor.scheduleAtFixedRate(
+ this::deleteInactiveSlices,
+ scheduleIntervalSeconds,
+ scheduleIntervalSeconds,
+ TimeUnit.SECONDS);
+ state = State.RUNNING;
+ }
+
+ @Override
+ public void stop() {
+ if (state == State.RUNNING) {
+ state = State.STOPPING;
+ executor.shutdownNow();
+ try {
+ if (!executor.awaitTermination(10, TimeUnit.SECONDS)) {
+ log.warn(
+ "Executor pool did not terminate within the specified timeout: {} {}",
+ 10,
+ TimeUnit.SECONDS);
+ }
+ } catch (InterruptedException e) {
+ log.warn("Failed to shut down the executor pool", e);
+ Thread.currentThread().interrupt();
+ }
+ }
+ state = State.STOPPED;
+ }
+
+ @VisibleForTesting
+ void deleteInactiveSlices() {
+ final ClusterState clusterState = coreContainer.getZkController().getClusterState();
+ Collection<Slice> inactiveSlices = new HashSet<>();
+ clusterState
+ .getCollectionsMap()
+ .forEach((k, v) -> inactiveSlices.addAll(collectInactiveSlices(v)));
Review Comment:
Uniqueness properly will be there without using HashSet but whatever I guess.
BTW I think this stream case is a good use for flatMap. map DocCollection to its slices (shards) then flatMap them so you have a stream of slices.
##########
solr/core/src/java/org/apache/solr/cluster/maintenance/InactiveShardRemover.java:
##########
@@ -0,0 +1,251 @@
+/*
+ * 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.maintenance;
+
+import com.google.common.annotations.VisibleForTesting;
+import java.io.IOException;
+import java.lang.invoke.MethodHandles;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.TimeUnit;
+import java.util.stream.Collectors;
+import org.apache.solr.api.ConfigurablePlugin;
+import org.apache.solr.client.solrj.request.CollectionAdminRequest;
+import org.apache.solr.client.solrj.response.CollectionAdminResponse;
+import org.apache.solr.client.solrj.response.RequestStatusState;
+import org.apache.solr.cloud.ClusterSingleton;
+import org.apache.solr.common.cloud.ClusterState;
+import org.apache.solr.common.cloud.DocCollection;
+import org.apache.solr.common.cloud.Slice;
+import org.apache.solr.common.cloud.ZkStateReader;
+import org.apache.solr.common.util.SolrNamedThreadFactory;
+import org.apache.solr.core.CoreContainer;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/** This Cluster Singleton can be configured to periodically find and remove inactive Shards */
+public class InactiveShardRemover
+ implements ClusterSingleton, ConfigurablePlugin<InactiveShardRemoverConfig> {
+
+ private static final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
+
+ public static final String PLUGIN_NAME = ".inactive-shard-remover";
+
+ static class DeleteActor {
+
+ private final CoreContainer coreContainer;
+
+ DeleteActor(final CoreContainer coreContainer) {
+ this.coreContainer = coreContainer;
+ }
+
+ void delete(final Slice slice, final String asyncId) throws IOException {
+ CollectionAdminRequest.DeleteShard deleteRequest =
+ CollectionAdminRequest.deleteShard(slice.getCollection(), slice.getName());
+ deleteRequest.setAsyncId(asyncId);
+ coreContainer.getZkController().getSolrCloudManager().request(deleteRequest);
+ }
+ }
+
+ private State state = State.STOPPED;
+
+ private final CoreContainer coreContainer;
+
+ private final DeleteActor deleteActor;
+
+ private ScheduledExecutorService executor;
+
+ private long scheduleIntervalSeconds;
+
+ private long ttlSeconds;
+
+ private int maxDeletesPerCycle;
+
+ /** Constructor invoked via Reflection */
+ public InactiveShardRemover(final CoreContainer cc) {
+ this(cc, new DeleteActor(cc));
+ }
+
+ @VisibleForTesting
+ InactiveShardRemover(final CoreContainer cc, final DeleteActor actor) {
+ this.coreContainer = cc;
+ this.deleteActor = actor;
+ }
+
+ @Override
+ public void configure(final InactiveShardRemoverConfig cfg) {
+ this.scheduleIntervalSeconds = cfg.scheduleIntervalSeconds;
+ this.maxDeletesPerCycle = cfg.maxDeletesPerCycle;
+ this.ttlSeconds = cfg.ttlSeconds;
+ }
+
+ @Override
+ public String getName() {
+ return PLUGIN_NAME;
+ }
+
+ @Override
+ public State getState() {
+ return state;
+ }
+
+ @Override
+ public void start() throws Exception {
+ state = State.STARTING;
+ executor = Executors.newScheduledThreadPool(1, new SolrNamedThreadFactory(PLUGIN_NAME));
+ executor.scheduleAtFixedRate(
+ this::deleteInactiveSlices,
+ scheduleIntervalSeconds,
+ scheduleIntervalSeconds,
+ TimeUnit.SECONDS);
+ state = State.RUNNING;
+ }
+
+ @Override
+ public void stop() {
+ if (state == State.RUNNING) {
+ state = State.STOPPING;
+ executor.shutdownNow();
+ try {
+ if (!executor.awaitTermination(10, TimeUnit.SECONDS)) {
+ log.warn(
+ "Executor pool did not terminate within the specified timeout: {} {}",
+ 10,
+ TimeUnit.SECONDS);
+ }
+ } catch (InterruptedException e) {
+ log.warn("Failed to shut down the executor pool", e);
+ Thread.currentThread().interrupt();
+ }
+ }
+ state = State.STOPPED;
+ }
+
+ @VisibleForTesting
+ void deleteInactiveSlices() {
+ final ClusterState clusterState = coreContainer.getZkController().getClusterState();
+ Collection<Slice> inactiveSlices = new HashSet<>();
+ clusterState
+ .getCollectionsMap()
+ .forEach((k, v) -> inactiveSlices.addAll(collectInactiveSlices(v)));
+
+ if (log.isInfoEnabled()) {
+ log.info(
+ "Found {} inactive Shards to delete, {} will be deleted",
+ inactiveSlices.size(),
+ Math.max(inactiveSlices.size(), maxDeletesPerCycle));
+ }
+
+ inactiveSlices.stream().limit(maxDeletesPerCycle).forEach(this::deleteShard);
+ }
+
+ private Collection<Slice> collectInactiveSlices(final DocCollection docCollection) {
+ final Collection<Slice> slices = new HashSet<>(docCollection.getSlices());
+ slices.removeAll(docCollection.getActiveSlices());
+ return slices.stream().filter(this::isExpired).collect(Collectors.toList());
+ }
+
+ private void deleteShard(final Slice s) {
+ final String asyncId = s.getCollection() + ";" + s.getName() + ";DELETESHARD";
Review Comment:
Why use the "async" style?
##########
solr/core/src/java/org/apache/solr/cluster/maintenance/InactiveShardRemover.java:
##########
@@ -0,0 +1,251 @@
+/*
+ * 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.maintenance;
+
+import com.google.common.annotations.VisibleForTesting;
+import java.io.IOException;
+import java.lang.invoke.MethodHandles;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.TimeUnit;
+import java.util.stream.Collectors;
+import org.apache.solr.api.ConfigurablePlugin;
+import org.apache.solr.client.solrj.request.CollectionAdminRequest;
+import org.apache.solr.client.solrj.response.CollectionAdminResponse;
+import org.apache.solr.client.solrj.response.RequestStatusState;
+import org.apache.solr.cloud.ClusterSingleton;
+import org.apache.solr.common.cloud.ClusterState;
+import org.apache.solr.common.cloud.DocCollection;
+import org.apache.solr.common.cloud.Slice;
+import org.apache.solr.common.cloud.ZkStateReader;
+import org.apache.solr.common.util.SolrNamedThreadFactory;
+import org.apache.solr.core.CoreContainer;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/** This Cluster Singleton can be configured to periodically find and remove inactive Shards */
+public class InactiveShardRemover
+ implements ClusterSingleton, ConfigurablePlugin<InactiveShardRemoverConfig> {
+
+ private static final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
+
+ public static final String PLUGIN_NAME = ".inactive-shard-remover";
+
+ static class DeleteActor {
+
+ private final CoreContainer coreContainer;
+
+ DeleteActor(final CoreContainer coreContainer) {
+ this.coreContainer = coreContainer;
+ }
+
+ void delete(final Slice slice, final String asyncId) throws IOException {
+ CollectionAdminRequest.DeleteShard deleteRequest =
+ CollectionAdminRequest.deleteShard(slice.getCollection(), slice.getName());
+ deleteRequest.setAsyncId(asyncId);
+ coreContainer.getZkController().getSolrCloudManager().request(deleteRequest);
+ }
+ }
+
+ private State state = State.STOPPED;
+
+ private final CoreContainer coreContainer;
+
+ private final DeleteActor deleteActor;
+
+ private ScheduledExecutorService executor;
+
+ private long scheduleIntervalSeconds;
+
+ private long ttlSeconds;
+
+ private int maxDeletesPerCycle;
+
+ /** Constructor invoked via Reflection */
+ public InactiveShardRemover(final CoreContainer cc) {
+ this(cc, new DeleteActor(cc));
+ }
+
+ @VisibleForTesting
+ InactiveShardRemover(final CoreContainer cc, final DeleteActor actor) {
+ this.coreContainer = cc;
+ this.deleteActor = actor;
+ }
+
+ @Override
+ public void configure(final InactiveShardRemoverConfig cfg) {
+ this.scheduleIntervalSeconds = cfg.scheduleIntervalSeconds;
+ this.maxDeletesPerCycle = cfg.maxDeletesPerCycle;
+ this.ttlSeconds = cfg.ttlSeconds;
+ }
+
+ @Override
+ public String getName() {
+ return PLUGIN_NAME;
+ }
+
+ @Override
+ public State getState() {
+ return state;
+ }
+
+ @Override
+ public void start() throws Exception {
+ state = State.STARTING;
+ executor = Executors.newScheduledThreadPool(1, new SolrNamedThreadFactory(PLUGIN_NAME));
+ executor.scheduleAtFixedRate(
+ this::deleteInactiveSlices,
+ scheduleIntervalSeconds,
+ scheduleIntervalSeconds,
+ TimeUnit.SECONDS);
+ state = State.RUNNING;
+ }
+
+ @Override
+ public void stop() {
+ if (state == State.RUNNING) {
+ state = State.STOPPING;
+ executor.shutdownNow();
+ try {
+ if (!executor.awaitTermination(10, TimeUnit.SECONDS)) {
+ log.warn(
+ "Executor pool did not terminate within the specified timeout: {} {}",
+ 10,
+ TimeUnit.SECONDS);
+ }
+ } catch (InterruptedException e) {
+ log.warn("Failed to shut down the executor pool", e);
+ Thread.currentThread().interrupt();
+ }
+ }
+ state = State.STOPPED;
+ }
+
+ @VisibleForTesting
+ void deleteInactiveSlices() {
+ final ClusterState clusterState = coreContainer.getZkController().getClusterState();
+ Collection<Slice> inactiveSlices = new HashSet<>();
+ clusterState
+ .getCollectionsMap()
+ .forEach((k, v) -> inactiveSlices.addAll(collectInactiveSlices(v)));
+
+ if (log.isInfoEnabled()) {
+ log.info(
+ "Found {} inactive Shards to delete, {} will be deleted",
+ inactiveSlices.size(),
+ Math.max(inactiveSlices.size(), maxDeletesPerCycle));
+ }
+
+ inactiveSlices.stream().limit(maxDeletesPerCycle).forEach(this::deleteShard);
+ }
+
+ private Collection<Slice> collectInactiveSlices(final DocCollection docCollection) {
+ final Collection<Slice> slices = new HashSet<>(docCollection.getSlices());
+ slices.removeAll(docCollection.getActiveSlices());
+ return slices.stream().filter(this::isExpired).collect(Collectors.toList());
+ }
+
+ private void deleteShard(final Slice s) {
+ final String asyncId = s.getCollection() + ";" + s.getName() + ";DELETESHARD";
+ try {
+ final RequestStatusState status = getStatus(asyncId);
+ if (status != RequestStatusState.FAILED && status != RequestStatusState.NOT_FOUND) {
+ log.info(
+ "Delete Shard request is already submitted or in progress with asyncId {}", asyncId);
+ return;
+ }
+
+ if (status == RequestStatusState.FAILED) {
+ // Delete, so that we can try again
+ deleteStatus(asyncId);
+ }
+
+ deleteActor.delete(s, asyncId);
+ } catch (IOException e) {
+ log.warn("An exception occurred when deleting an inactive shard with asyncId {}", asyncId);
+ }
+ }
+
+ /**
+ * An Inactive Shard is expired if it has not undergone a state change in the period of time
+ * defined by {@link InactiveShardRemover#ttlSeconds}. If it is expired, it is eligible for
+ * removal.
+ */
+ private boolean isExpired(final Slice slice) {
+
+ final String collectionName = slice.getCollection();
+ final String sliceName = slice.getName();
+
+ if (slice.getState() != Slice.State.INACTIVE) {
+ return false;
+ }
+
+ final String lastChangeTimestamp = slice.getStr(ZkStateReader.STATE_TIMESTAMP_PROP);
Review Comment:
cool; I didn't know this existed
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: issues-unsubscribe@solr.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: issues-unsubscribe@solr.apache.org
For additional commands, e-mail: issues-help@solr.apache.org
Re: [PR] SOLR-16403: Cluster Singleton to remove inactive Shards [solr]
Posted by "dsmiley (via GitHub)" <gi...@apache.org>.
dsmiley commented on PR #1926:
URL: https://github.com/apache/solr/pull/1926#issuecomment-2008667195
For all the AI fanciness of that bot, some good old fashioned static analysis is all we would have liked to point out that we are adding methods that are not used (leading to test flapper and follow-up PR). I suspect the "Muse" bot that was cancelled ~6 months ago would have caught this.
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: issues-unsubscribe@solr.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: issues-unsubscribe@solr.apache.org
For additional commands, e-mail: issues-help@solr.apache.org
Re: [PR] SOLR-16403: Cluster Singleton to remove inactive Shards [solr]
Posted by "BitoAgent (via GitHub)" <gi...@apache.org>.
BitoAgent commented on PR #1926:
URL: https://github.com/apache/solr/pull/1926#issuecomment-1954913579
## PR Run Status
- **AI Based Review:** Successful
- **Static Analysis:** Failure - Failed to execute static code analysis using FBinfer.
## PR Analysis
- **Main theme:** Implementation of a Cluster Singleton plugin for removing inactive shards in Solr.
- **PR summary:** This PR introduces a Cluster Singleton plugin, InactiveShardRemover, aimed at identifying and removing inactive shards that have exceeded a Time-To-Live (TTL) period. It configures a scheduled task to periodically check and delete these shards, thereby helping in maintaining the health and efficiency of the Solr cluster.
- **Type of PR:** Enhancement
- **[Score ](https://docs.bito.ai/bito-dev-agents/ai-code-review-agent/faqs#what-is-pr-quality-score-in-code-review-output)(0-100, higher is better):** 85
- **Relevant tests added:** Yes
- **[Estimated effort to review ](https://docs.bito.ai/bito-dev-agents/ai-code-review-agent/faqs#what-is-estimated-effort-to-review-in-code-review-output)(1-5, lower is better):** 2
The PR is well-structured with a clear purpose. It includes tests which should make the review process straightforward. However, the complexity of the changes and the potential impact on cluster behavior necessitate a thorough review.
## PR Feedback
- **General suggestions:** The implementation of the InactiveShardRemover as a Cluster Singleton for removing inactive shards after a TTL is a valuable addition to Solr's maintenance capabilities. The approach of scheduling periodic checks to remove expired shards can help in keeping the cluster lean and efficient. However, it's crucial to ensure that the removal process does not inadvertently affect the cluster's stability or data integrity. To further enhance this feature, consider implementing safety checks that prevent the removal of shards if they are temporarily marked as inactive due to network partitions or other transient issues. Additionally, providing more configurability for the TTL and deletion criteria could allow administrators to tailor the feature to their specific needs. Finally, extensive testing in simulated production environments is recommended to ensure that the feature behaves as expected under various failure scenarios.
## Code feedback
## file: solr/core/src/java/org/apache/solr/cluster/maintenance/InactiveShardRemover.java
- **Suggestions:**
1. Consider implementing a dry-run mode that logs which shards would be deleted without actually performing the deletion. This can be useful for administrators to safely test the impact of the InactiveShardRemover on their cluster. [important] **Relevant line**:In solr/core/src/java/org/apache/solr/cluster/maintenance/InactiveShardRemover.java at line 85
```
+ implements ClusterSingleton, ConfigurablePlugin<InactiveShardRemoverConfig> {
```
2. Add metrics or logging to track the number of shards checked and deleted per cycle. This can help in monitoring the effectiveness of the plugin and troubleshooting issues. [important] **Relevant line**:In solr/core/src/java/org/apache/solr/cluster/maintenance/InactiveShardRemover.java at line 176
```
+ inactiveSlices.stream().limit(maxDeletesPerCycle).forEach(this::deleteShard);
```
3. Ensure that the scheduled executor service is properly managed to avoid potential memory leaks. Consider using a try-with-resources statement or similar mechanism to ensure that resources are freed up. [important] **Relevant line**:In solr/core/src/java/org/apache/solr/cluster/maintenance/InactiveShardRemover.java at line 132
```
+ executor = Executors.newScheduledThreadPool(1, new SolrNamedThreadFactory(PLUGIN_NAME));
```
4. Consider adding a feature to notify administrators before deleting shards, possibly through email or a dashboard notification. This could provide an additional layer of safety before data deletion occurs. [medium] **Relevant line**:In solr/core/src/java/org/apache/solr/cluster/maintenance/InactiveShardRemover.java at line 190
```
+ private void deleteShard(final Slice s) {
```
## file: solr/core/src/java/org/apache/solr/cluster/maintenance/InactiveShardRemoverConfig.java
- **Suggestions:**
1. Validate configuration parameters (e.g., scheduleIntervalSeconds, ttlSeconds, maxDeletesPerCycle) to ensure they are within reasonable limits. This can prevent misconfigurations that could lead to aggressive shard deletions or performance issues. [important] **Relevant line**:In solr/core/src/java/org/apache/solr/cluster/maintenance/InactiveShardRemoverConfig.java at line 47
```
+ final long scheduleIntervalSeconds, final long ttlSeconds, final int maxDeletesPerCycle) {
```
2. Allow dynamic reconfiguration of the InactiveShardRemoverConfig without needing to restart the Solr cluster. This would enable administrators to adjust settings in response to the cluster's current state. [medium] **Relevant line**:In solr/core/src/java/org/apache/solr/cluster/maintenance/InactiveShardRemoverConfig.java at line 28
```
+ public InactiveShardRemoverConfig() {
```
## file: solr/core/src/test/org/apache/solr/cluster/maintenance/InactiveShardRemoverTest.java
- **Suggestions:**
1. Expand test coverage to include scenarios where shards become inactive due to network partitions or other transient conditions. Ensure that the plugin does not delete these shards prematurely. [important] **Relevant line**:In solr/core/src/test/org/apache/solr/cluster/maintenance/InactiveShardRemoverTest.java at line 210
```
+ private void setAllShardsInactive(final String collectionName) {
```
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: issues-unsubscribe@solr.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: issues-unsubscribe@solr.apache.org
For additional commands, e-mail: issues-help@solr.apache.org
[GitHub] [solr] dsmiley commented on a diff in pull request #1926: SOLR-16403: Cluster Singleton to remove inactive Shards
Posted by "dsmiley (via GitHub)" <gi...@apache.org>.
dsmiley commented on code in PR #1926:
URL: https://github.com/apache/solr/pull/1926#discussion_r1331009854
##########
solr/core/src/java/org/apache/solr/cluster/maintenance/InactiveShardRemover.java:
##########
@@ -0,0 +1,251 @@
+/*
+ * 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.maintenance;
+
+import com.google.common.annotations.VisibleForTesting;
+import java.io.IOException;
+import java.lang.invoke.MethodHandles;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.TimeUnit;
+import java.util.stream.Collectors;
+import org.apache.solr.api.ConfigurablePlugin;
+import org.apache.solr.client.solrj.request.CollectionAdminRequest;
+import org.apache.solr.client.solrj.response.CollectionAdminResponse;
+import org.apache.solr.client.solrj.response.RequestStatusState;
+import org.apache.solr.cloud.ClusterSingleton;
+import org.apache.solr.common.cloud.ClusterState;
+import org.apache.solr.common.cloud.DocCollection;
+import org.apache.solr.common.cloud.Slice;
+import org.apache.solr.common.cloud.ZkStateReader;
+import org.apache.solr.common.util.SolrNamedThreadFactory;
+import org.apache.solr.core.CoreContainer;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/** This Cluster Singleton can be configured to periodically find and remove inactive Shards */
+public class InactiveShardRemover
+ implements ClusterSingleton, ConfigurablePlugin<InactiveShardRemoverConfig> {
+
+ private static final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
+
+ public static final String PLUGIN_NAME = ".inactive-shard-remover";
+
+ static class DeleteActor {
+
+ private final CoreContainer coreContainer;
+
+ DeleteActor(final CoreContainer coreContainer) {
+ this.coreContainer = coreContainer;
+ }
+
+ void delete(final Slice slice, final String asyncId) throws IOException {
+ CollectionAdminRequest.DeleteShard deleteRequest =
+ CollectionAdminRequest.deleteShard(slice.getCollection(), slice.getName());
+ deleteRequest.setAsyncId(asyncId);
+ coreContainer.getZkController().getSolrCloudManager().request(deleteRequest);
+ }
+ }
+
+ private State state = State.STOPPED;
+
+ private final CoreContainer coreContainer;
+
+ private final DeleteActor deleteActor;
+
+ private ScheduledExecutorService executor;
+
+ private long scheduleIntervalSeconds;
+
+ private long ttlSeconds;
+
+ private int maxDeletesPerCycle;
+
+ /** Constructor invoked via Reflection */
+ public InactiveShardRemover(final CoreContainer cc) {
+ this(cc, new DeleteActor(cc));
+ }
+
+ @VisibleForTesting
+ InactiveShardRemover(final CoreContainer cc, final DeleteActor actor) {
+ this.coreContainer = cc;
+ this.deleteActor = actor;
+ }
+
+ @Override
+ public void configure(final InactiveShardRemoverConfig cfg) {
+ this.scheduleIntervalSeconds = cfg.scheduleIntervalSeconds;
+ this.maxDeletesPerCycle = cfg.maxDeletesPerCycle;
+ this.ttlSeconds = cfg.ttlSeconds;
+ }
+
+ @Override
+ public String getName() {
+ return PLUGIN_NAME;
+ }
+
+ @Override
+ public State getState() {
+ return state;
+ }
+
+ @Override
+ public void start() throws Exception {
+ state = State.STARTING;
+ executor = Executors.newScheduledThreadPool(1, new SolrNamedThreadFactory(PLUGIN_NAME));
+ executor.scheduleAtFixedRate(
+ this::deleteInactiveSlices,
+ scheduleIntervalSeconds,
+ scheduleIntervalSeconds,
+ TimeUnit.SECONDS);
+ state = State.RUNNING;
+ }
+
+ @Override
+ public void stop() {
+ if (state == State.RUNNING) {
+ state = State.STOPPING;
+ executor.shutdownNow();
+ try {
+ if (!executor.awaitTermination(10, TimeUnit.SECONDS)) {
+ log.warn(
+ "Executor pool did not terminate within the specified timeout: {} {}",
+ 10,
+ TimeUnit.SECONDS);
+ }
+ } catch (InterruptedException e) {
+ log.warn("Failed to shut down the executor pool", e);
+ Thread.currentThread().interrupt();
+ }
+ }
+ state = State.STOPPED;
+ }
+
+ @VisibleForTesting
+ void deleteInactiveSlices() {
+ final ClusterState clusterState = coreContainer.getZkController().getClusterState();
+ Collection<Slice> inactiveSlices = new HashSet<>();
+ clusterState
+ .getCollectionsMap()
+ .forEach((k, v) -> inactiveSlices.addAll(collectInactiveSlices(v)));
+
+ if (log.isInfoEnabled()) {
+ log.info(
+ "Found {} inactive Shards to delete, {} will be deleted",
+ inactiveSlices.size(),
+ Math.max(inactiveSlices.size(), maxDeletesPerCycle));
+ }
+
+ inactiveSlices.stream().limit(maxDeletesPerCycle).forEach(this::deleteShard);
+ }
+
+ private Collection<Slice> collectInactiveSlices(final DocCollection docCollection) {
+ final Collection<Slice> slices = new HashSet<>(docCollection.getSlices());
+ slices.removeAll(docCollection.getActiveSlices());
+ return slices.stream().filter(this::isExpired).collect(Collectors.toList());
+ }
+
+ private void deleteShard(final Slice s) {
+ final String asyncId = s.getCollection() + ";" + s.getName() + ";DELETESHARD";
Review Comment:
Since no process / user is waiting for the response, I think it's simpler to just call it synchronously.
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: issues-unsubscribe@solr.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: issues-unsubscribe@solr.apache.org
For additional commands, e-mail: issues-help@solr.apache.org
Re: [PR] SOLR-16403: Cluster Singleton to remove inactive Shards [solr]
Posted by "pjmcarthur (via GitHub)" <gi...@apache.org>.
pjmcarthur commented on PR #1926:
URL: https://github.com/apache/solr/pull/1926#issuecomment-1992614083
We now have this running in production and have been able to see it working as expected, so I'm taking the PR out of draft status.
@dsmiley fyi.
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: issues-unsubscribe@solr.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: issues-unsubscribe@solr.apache.org
For additional commands, e-mail: issues-help@solr.apache.org
Re: [PR] SOLR-16403: Cluster Singleton to remove inactive Shards [solr]
Posted by "dsmiley (via GitHub)" <gi...@apache.org>.
dsmiley merged PR #1926:
URL: https://github.com/apache/solr/pull/1926
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: issues-unsubscribe@solr.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: issues-unsubscribe@solr.apache.org
For additional commands, e-mail: issues-help@solr.apache.org
Re: [PR] SOLR-16403: Cluster Singleton to remove inactive Shards [solr]
Posted by "dsmiley (via GitHub)" <gi...@apache.org>.
dsmiley commented on code in PR #1926:
URL: https://github.com/apache/solr/pull/1926#discussion_r1522243305
##########
solr/core/src/java/org/apache/solr/cluster/maintenance/InactiveShardRemover.java:
##########
@@ -0,0 +1,219 @@
+/*
+ * 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.maintenance;
+
+import com.google.common.annotations.VisibleForTesting;
+import java.lang.invoke.MethodHandles;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.Objects;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.TimeUnit;
+import java.util.stream.Collectors;
+import org.apache.solr.api.ConfigurablePlugin;
+import org.apache.solr.client.solrj.SolrResponse;
+import org.apache.solr.client.solrj.request.CollectionAdminRequest;
+import org.apache.solr.cloud.ClusterSingleton;
+import org.apache.solr.common.cloud.ClusterState;
+import org.apache.solr.common.cloud.DocCollection;
+import org.apache.solr.common.cloud.Slice;
+import org.apache.solr.common.cloud.ZkStateReader;
+import org.apache.solr.common.util.ExecutorUtil;
+import org.apache.solr.common.util.SolrNamedThreadFactory;
+import org.apache.solr.core.CoreContainer;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * This Cluster Singleton can be configured to periodically find and remove {@link
+ * org.apache.solr.common.cloud.Slice.State#INACTIVE} Shards that are left behind after a Shard is
+ * split
+ */
+public class InactiveShardRemover
+ implements ClusterSingleton, ConfigurablePlugin<InactiveShardRemoverConfig> {
+
+ private static final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
+
+ public static final String PLUGIN_NAME = ".inactive-shard-remover";
+
+ static class DeleteActor {
+
+ private final CoreContainer coreContainer;
+
+ DeleteActor(final CoreContainer coreContainer) {
+ this.coreContainer = coreContainer;
+ }
+
+ void delete(final Slice slice) {
+ CollectionAdminRequest.DeleteShard deleteRequest =
+ CollectionAdminRequest.deleteShard(slice.getCollection(), slice.getName());
+ try {
+ SolrResponse response =
+ coreContainer.getZkController().getSolrCloudManager().request(deleteRequest);
+ if (response.getException() != null) {
+ throw response.getException();
+ }
+ } catch (Exception e) {
+ log.warn("An exception occurred when deleting an inactive shard", e);
+ }
+ }
+ }
+
+ private State state = State.STOPPED;
+
+ private final CoreContainer coreContainer;
+
+ private final DeleteActor deleteActor;
+
+ private ScheduledExecutorService executor;
+
+ private long scheduleIntervalSeconds;
+
+ private long ttlSeconds;
+
+ private int maxDeletesPerCycle;
+
+ /** Constructor invoked via Reflection */
+ public InactiveShardRemover(final CoreContainer cc) {
+ this(cc, new DeleteActor(cc));
+ }
+
+ public InactiveShardRemover(final CoreContainer cc, final DeleteActor deleteActor) {
+ this.coreContainer = cc;
+ this.deleteActor = deleteActor;
+ }
+
+ @Override
+ public void configure(final InactiveShardRemoverConfig cfg) {
+ Objects.requireNonNull(cfg, "config must be specified");
+ cfg.validate();
+ this.scheduleIntervalSeconds = cfg.scheduleIntervalSeconds;
+ this.maxDeletesPerCycle = cfg.maxDeletesPerCycle;
+ this.ttlSeconds = cfg.ttlSeconds;
+ }
+
+ @Override
+ public String getName() {
+ return PLUGIN_NAME;
+ }
+
+ @Override
+ public State getState() {
+ return state;
+ }
+
+ @Override
+ public void start() throws Exception {
+ state = State.STARTING;
+ executor = Executors.newScheduledThreadPool(1, new SolrNamedThreadFactory(PLUGIN_NAME));
+ executor.scheduleAtFixedRate(
+ this::deleteInactiveSlices,
+ scheduleIntervalSeconds,
+ scheduleIntervalSeconds,
+ TimeUnit.SECONDS);
+ state = State.RUNNING;
+ }
+
+ @Override
+ public void stop() {
+ if (state == State.RUNNING) {
+ state = State.STOPPING;
+ ExecutorUtil.shutdownNowAndAwaitTermination(executor);
+ }
+ state = State.STOPPED;
+ }
+
+ @VisibleForTesting
+ void deleteInactiveSlices() {
+ final ClusterState clusterState = coreContainer.getZkController().getClusterState();
+ Collection<Slice> inactiveSlices =
+ clusterState.getCollectionsMap().values().stream()
Review Comment:
Such an innocent looking thing but `getCollectionsMap()` has been a CPU performance problem at the 10s of thousands of collections scale. Fine for now but eventually it'd be nice to have an alternative. For example, perhaps each node could tend to this inactive shard removing task for its own cores, ignoring all the others. And/or maybe a shard split schedules this on a pertinent node. Any way, we have something in-hand here that works, which addresses a gap since the autoscaling framework's removal in Solr 8.
##########
solr/core/src/java/org/apache/solr/cluster/maintenance/InactiveShardRemover.java:
##########
@@ -0,0 +1,219 @@
+/*
+ * 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.maintenance;
+
+import com.google.common.annotations.VisibleForTesting;
+import java.lang.invoke.MethodHandles;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.Objects;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.TimeUnit;
+import java.util.stream.Collectors;
+import org.apache.solr.api.ConfigurablePlugin;
+import org.apache.solr.client.solrj.SolrResponse;
+import org.apache.solr.client.solrj.request.CollectionAdminRequest;
+import org.apache.solr.cloud.ClusterSingleton;
+import org.apache.solr.common.cloud.ClusterState;
+import org.apache.solr.common.cloud.DocCollection;
+import org.apache.solr.common.cloud.Slice;
+import org.apache.solr.common.cloud.ZkStateReader;
+import org.apache.solr.common.util.ExecutorUtil;
+import org.apache.solr.common.util.SolrNamedThreadFactory;
+import org.apache.solr.core.CoreContainer;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * This Cluster Singleton can be configured to periodically find and remove {@link
+ * org.apache.solr.common.cloud.Slice.State#INACTIVE} Shards that are left behind after a Shard is
+ * split
+ */
+public class InactiveShardRemover
+ implements ClusterSingleton, ConfigurablePlugin<InactiveShardRemoverConfig> {
+
+ private static final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
+
+ public static final String PLUGIN_NAME = ".inactive-shard-remover";
+
+ static class DeleteActor {
+
+ private final CoreContainer coreContainer;
+
+ DeleteActor(final CoreContainer coreContainer) {
+ this.coreContainer = coreContainer;
+ }
+
+ void delete(final Slice slice) {
+ CollectionAdminRequest.DeleteShard deleteRequest =
+ CollectionAdminRequest.deleteShard(slice.getCollection(), slice.getName());
+ try {
+ SolrResponse response =
+ coreContainer.getZkController().getSolrCloudManager().request(deleteRequest);
+ if (response.getException() != null) {
+ throw response.getException();
+ }
+ } catch (Exception e) {
+ log.warn("An exception occurred when deleting an inactive shard", e);
+ }
+ }
+ }
+
+ private State state = State.STOPPED;
+
+ private final CoreContainer coreContainer;
+
+ private final DeleteActor deleteActor;
+
+ private ScheduledExecutorService executor;
+
+ private long scheduleIntervalSeconds;
+
+ private long ttlSeconds;
+
+ private int maxDeletesPerCycle;
+
+ /** Constructor invoked via Reflection */
+ public InactiveShardRemover(final CoreContainer cc) {
+ this(cc, new DeleteActor(cc));
+ }
+
+ public InactiveShardRemover(final CoreContainer cc, final DeleteActor deleteActor) {
+ this.coreContainer = cc;
+ this.deleteActor = deleteActor;
+ }
+
+ @Override
+ public void configure(final InactiveShardRemoverConfig cfg) {
+ Objects.requireNonNull(cfg, "config must be specified");
+ cfg.validate();
+ this.scheduleIntervalSeconds = cfg.scheduleIntervalSeconds;
+ this.maxDeletesPerCycle = cfg.maxDeletesPerCycle;
+ this.ttlSeconds = cfg.ttlSeconds;
+ }
+
+ @Override
+ public String getName() {
+ return PLUGIN_NAME;
+ }
+
+ @Override
+ public State getState() {
+ return state;
+ }
+
+ @Override
+ public void start() throws Exception {
+ state = State.STARTING;
+ executor = Executors.newScheduledThreadPool(1, new SolrNamedThreadFactory(PLUGIN_NAME));
+ executor.scheduleAtFixedRate(
+ this::deleteInactiveSlices,
+ scheduleIntervalSeconds,
+ scheduleIntervalSeconds,
+ TimeUnit.SECONDS);
+ state = State.RUNNING;
+ }
+
+ @Override
+ public void stop() {
+ if (state == State.RUNNING) {
+ state = State.STOPPING;
+ ExecutorUtil.shutdownNowAndAwaitTermination(executor);
+ }
+ state = State.STOPPED;
+ }
+
+ @VisibleForTesting
+ void deleteInactiveSlices() {
+ final ClusterState clusterState = coreContainer.getZkController().getClusterState();
+ Collection<Slice> inactiveSlices =
+ clusterState.getCollectionsMap().values().stream()
+ .flatMap(v -> collectInactiveSlices(v).stream())
+ .collect(Collectors.toSet());
+
+ if (log.isInfoEnabled()) {
+ log.info(
+ "Found {} inactive Shards to delete, {} will be deleted",
+ inactiveSlices.size(),
+ Math.min(inactiveSlices.size(), maxDeletesPerCycle));
+ }
+
+ inactiveSlices.stream().limit(maxDeletesPerCycle).forEach(this::deleteShard);
+ }
+
+ private Collection<Slice> collectInactiveSlices(final DocCollection docCollection) {
+ final Collection<Slice> slices = new HashSet<>(docCollection.getSlices());
+ slices.removeAll(docCollection.getActiveSlices());
+ return slices.stream().filter(this::isExpired).collect(Collectors.toSet());
+ }
+
+ private void deleteShard(final Slice s) {
+ deleteActor.delete(s);
+ }
+
+ /**
+ * An Inactive Shard is expired if it has not undergone a state change in the period of time
+ * defined by {@link InactiveShardRemover#ttlSeconds}. If it is expired, it is eligible for
+ * removal.
+ */
+ private boolean isExpired(final Slice slice) {
+
+ final String collectionName = slice.getCollection();
+ final String sliceName = slice.getName();
+
+ if (slice.getState() != Slice.State.INACTIVE) {
+ return false;
+ }
+
+ final String lastChangeTimestamp = slice.getStr(ZkStateReader.STATE_TIMESTAMP_PROP);
+ if (lastChangeTimestamp == null || lastChangeTimestamp.isEmpty()) {
+ log.warn(
+ "Collection {} Shard {} has no last change timestamp and will not be deleted",
+ collectionName,
+ sliceName);
+ return false;
+ }
+
+ final long epochTimestamp;
Review Comment:
I really wish time units were clearer here. Is this Ns or Ms; just a variable name suffix would help.
##########
solr/core/src/java/org/apache/solr/cluster/maintenance/package-info.java:
##########
@@ -0,0 +1,19 @@
+/*
+ * 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.
+ */
+
+/** Cluster Singleton plugins that can are used to perform maintenance tasks within the cluster. */
Review Comment:
```suggestion
/** Cluster Singleton plugins that are used to perform maintenance tasks within the cluster. */
```
##########
solr/core/src/test/org/apache/solr/cluster/maintenance/InactiveShardRemoverTest.java:
##########
@@ -0,0 +1,231 @@
+/*
+ * 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.maintenance;
+
+import static org.apache.solr.client.solrj.SolrRequest.METHOD.POST;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Map;
+import java.util.concurrent.TimeUnit;
+import org.apache.solr.client.solrj.SolrServerException;
+import org.apache.solr.client.solrj.request.CollectionAdminRequest;
+import org.apache.solr.client.solrj.request.V2Request;
+import org.apache.solr.client.solrj.request.beans.PluginMeta;
+import org.apache.solr.client.solrj.response.V2Response;
+import org.apache.solr.cloud.ShardTestUtil;
+import org.apache.solr.cloud.SolrCloudTestCase;
+import org.apache.solr.common.SolrException;
+import org.apache.solr.common.cloud.DocCollection;
+import org.apache.solr.common.cloud.Slice;
+import org.apache.solr.common.util.TimeSource;
+import org.apache.solr.core.CoreContainer;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+public class InactiveShardRemoverTest extends SolrCloudTestCase {
+
+ @BeforeClass
+ public static void setupCluster() throws Exception {
+ configureCluster(1)
+ .addConfig(
+ "conf", TEST_PATH().resolve("configsets").resolve("cloud-minimal").resolve("conf"))
+ .configure();
+ }
+
+ @AfterClass
+ public static void tearDownCluster() throws Exception {
+ cluster.shutdown();
+ }
+
+ @Test
+ public void testDeleteInactiveShard() throws Exception {
+
+ addPlugin(new InactiveShardRemoverConfig(1, 0, 2));
+ try {
+ final String collectionName = "testDeleteInactiveShard";
+ createCollection(collectionName, 1);
+
+ final String sliceName =
+ new ArrayList<>(getCollectionState(collectionName).getSlices()).get(0).getName();
+ ShardTestUtil.setSliceState(cluster, collectionName, sliceName, Slice.State.INACTIVE);
+
+ waitForState(
+ "Waiting for inactive shard to be deleted",
+ collectionName,
+ clusterShape(0, 0),
+ 5,
+ TimeUnit.SECONDS);
+ } finally {
+ removePlugin();
+ }
+ }
+
+ @Test
+ public void testTtl() throws Exception {
+
+ final int ttlSeconds = 1 + random().nextInt(5);
+ final TimeSource timeSource = cluster.getOpenOverseer().getSolrCloudManager().getTimeSource();
Review Comment:
side comment: I think TimeSource ought to be a global singleton static thing so it's clear how to get it from absolutely anywhere. Such singletons can be evil but I think it's warranted for something so universal as tracking time.
##########
solr/core/src/test/org/apache/solr/cluster/maintenance/InactiveShardRemoverTest.java:
##########
@@ -0,0 +1,231 @@
+/*
+ * 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.maintenance;
+
+import static org.apache.solr.client.solrj.SolrRequest.METHOD.POST;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Map;
+import java.util.concurrent.TimeUnit;
+import org.apache.solr.client.solrj.SolrServerException;
+import org.apache.solr.client.solrj.request.CollectionAdminRequest;
+import org.apache.solr.client.solrj.request.V2Request;
+import org.apache.solr.client.solrj.request.beans.PluginMeta;
+import org.apache.solr.client.solrj.response.V2Response;
+import org.apache.solr.cloud.ShardTestUtil;
+import org.apache.solr.cloud.SolrCloudTestCase;
+import org.apache.solr.common.SolrException;
+import org.apache.solr.common.cloud.DocCollection;
+import org.apache.solr.common.cloud.Slice;
+import org.apache.solr.common.util.TimeSource;
+import org.apache.solr.core.CoreContainer;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+public class InactiveShardRemoverTest extends SolrCloudTestCase {
+
+ @BeforeClass
+ public static void setupCluster() throws Exception {
+ configureCluster(1)
+ .addConfig(
+ "conf", TEST_PATH().resolve("configsets").resolve("cloud-minimal").resolve("conf"))
+ .configure();
+ }
+
+ @AfterClass
+ public static void tearDownCluster() throws Exception {
+ cluster.shutdown();
+ }
Review Comment:
not needed
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: issues-unsubscribe@solr.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: issues-unsubscribe@solr.apache.org
For additional commands, e-mail: issues-help@solr.apache.org
Re: [PR] SOLR-16403: Cluster Singleton to remove inactive Shards [solr]
Posted by "pjmcarthur (via GitHub)" <gi...@apache.org>.
pjmcarthur commented on code in PR #1926:
URL: https://github.com/apache/solr/pull/1926#discussion_r1522131521
##########
solr/test-framework/src/java/org/apache/solr/cloud/SolrCloudTestCase.java:
##########
@@ -385,4 +389,39 @@ public static Map<String, String> mapReplicasToReplicaType(DocCollection collect
}
return replicaTypeMap;
}
+
+ protected void setSliceState(String collection, String slice, Slice.State state)
Review Comment:
I made no modifications to the method, but I have moved it now to it's own file so that it can be reused more easily. I agree that SolrCloudTestCase really wasn't the right place for it.
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: issues-unsubscribe@solr.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: issues-unsubscribe@solr.apache.org
For additional commands, e-mail: issues-help@solr.apache.org
Re: [PR] SOLR-16403: Cluster Singleton to remove inactive Shards [solr]
Posted by "pjmcarthur (via GitHub)" <gi...@apache.org>.
pjmcarthur commented on PR #1926:
URL: https://github.com/apache/solr/pull/1926#issuecomment-1992707134
Test failure is reproducible on the main branch with
`gradlew :solr:core:test --tests "org.apache.solr.cloud.OverseerStatusTest.test" -Ptests.jvms=96 "-Ptests.jvmargs=-XX:TieredStopAtLevel=1 -XX:+UseParallelGC -XX:ActiveProcessorCount=1 -XX:ReservedCodeCacheSize=120m" -Ptests.seed=9491A2C35A439099 -Ptests.timeoutSuite=600000! -Ptests.file.encoding=US-ASCII`
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: issues-unsubscribe@solr.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: issues-unsubscribe@solr.apache.org
For additional commands, e-mail: issues-help@solr.apache.org