This is an automated email from the ASF dual-hosted git repository.
dsmiley pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/solr.git
The following commit(s) were added to refs/heads/main by this push:
new 19fbd97f02d SOLR-18142: Fix CloudSolrClient cache state refresh;
regression. (#4176)
19fbd97f02d is described below
commit 19fbd97f02d01d927405efcdf59176cb56209304
Author: David Smiley <[email protected]>
AuthorDate: Sat Mar 7 15:58:01 2026 -0500
SOLR-18142: Fix CloudSolrClient cache state refresh; regression. (#4176)
CloudSolrClient- fixed state refresh race; didn't refresh. Regression from
9.10.1/10.0
Found by flaky test:
CloudSolrClientCacheTest.testStaleStateRetryWaitsAfterSkipFailure
---
changelog/unreleased/SOLR-18142.yml | 7 +++
.../solr/client/solrj/impl/CloudSolrClient.java | 62 ++++++++++------------
2 files changed, 34 insertions(+), 35 deletions(-)
diff --git a/changelog/unreleased/SOLR-18142.yml
b/changelog/unreleased/SOLR-18142.yml
new file mode 100644
index 00000000000..b8b20bfea78
--- /dev/null
+++ b/changelog/unreleased/SOLR-18142.yml
@@ -0,0 +1,7 @@
+title: CloudSolrClient- fixed state refresh race; didn't refresh. Regression
from 9.10.1/10.0.
+type: fixed
+authors:
+ - name: David Smiley
+links:
+ - name: SOLR-18142
+ url: https://issues.apache.org/jira/browse/SOLR-18142
diff --git
a/solr/solrj/src/java/org/apache/solr/client/solrj/impl/CloudSolrClient.java
b/solr/solrj/src/java/org/apache/solr/client/solrj/impl/CloudSolrClient.java
index 1a41044b793..df7034acf83 100644
--- a/solr/solrj/src/java/org/apache/solr/client/solrj/impl/CloudSolrClient.java
+++ b/solr/solrj/src/java/org/apache/solr/client/solrj/impl/CloudSolrClient.java
@@ -105,7 +105,7 @@ public abstract class CloudSolrClient extends SolrClient {
private final boolean directUpdatesToLeadersOnly;
private final RequestReplicaListTransformerGenerator requestRLTGenerator;
private final boolean parallelUpdates;
- private ExecutorService threadPool =
+ private final ExecutorService threadPool =
ExecutorUtil.newMDCAwareCachedThreadPool(
new SolrNamedThreadFactory("CloudSolrClient ThreadPool"));
@@ -642,9 +642,8 @@ public abstract class CloudSolrClient extends SolrClient {
public void close() {
closed = true;
collectionRefreshes.clear();
- if (this.threadPool != null && !ExecutorUtil.isShutdown(this.threadPool)) {
+ if (!ExecutorUtil.isShutdown(this.threadPool)) {
ExecutorUtil.shutdownAndAwaitTermination(this.threadPool);
- this.threadPool = null;
}
}
@@ -1658,41 +1657,34 @@ public abstract class CloudSolrClient extends
SolrClient {
}
private CompletableFuture<DocCollection> triggerCollectionRefresh(String
collection) {
- if (closed) {
- ExpiringCachedDocCollection cacheEntry =
collectionStateCache.peek(collection);
- DocCollection cached = cacheEntry == null ? null : cacheEntry.cached;
- return CompletableFuture.completedFuture(cached);
- }
- return collectionRefreshes.computeIfAbsent(
+ return collectionRefreshes.compute(
collection,
- key -> {
- ExecutorService executor = threadPool;
- CompletableFuture<DocCollection> future;
- if (executor == null || ExecutorUtil.isShutdown(executor)) {
- future = new CompletableFuture<>();
- try {
- future.complete(loadDocCollection(key));
- } catch (Throwable t) {
- future.completeExceptionally(t);
- }
+ (key, existingFuture) -> {
+ // A refresh is still in progress; return it.
+ if (existingFuture != null && !existingFuture.isDone()) {
+ return existingFuture;
+ }
+ // No refresh is in-progress, so trigger it.
+
+ if (ExecutorUtil.isShutdown(threadPool)) {
+ assert closed; // see close() for the sequence
+ ExpiringCachedDocCollection cacheEntry =
collectionStateCache.peek(key);
+ DocCollection cached = cacheEntry == null ? null :
cacheEntry.cached;
+ return CompletableFuture.completedFuture(cached);
} else {
- future =
- CompletableFuture.supplyAsync(
- () -> {
- stateRefreshSemaphore.acquireUninterruptibly();
- try {
- return loadDocCollection(key);
- } finally {
- stateRefreshSemaphore.release();
- }
- },
- executor);
+ return CompletableFuture.supplyAsync(
+ () -> {
+ stateRefreshSemaphore.acquireUninterruptibly();
+ try {
+ return loadDocCollection(key);
+ } finally {
+ stateRefreshSemaphore.release();
+ // Remove the entry in case of many collections
+ collectionRefreshes.remove(key);
+ }
+ },
+ threadPool);
}
- future.whenCompleteAsync(
- (result, error) -> {
- collectionRefreshes.remove(key, future);
- });
- return future;
});
}