This is an automated email from the ASF dual-hosted git repository. mlbiscoc pushed a commit to branch branch_9x in repository https://gitbox.apache.org/repos/asf/solr.git
The following commit(s) were added to refs/heads/branch_9x by this push: new 764aff6cf5d SOLR-17863: fix per segment fingerprint cache race (#3477) 764aff6cf5d is described below commit 764aff6cf5d5a3827a2c1376c5c89719abf86d21 Author: Luke Kot-Zaniewski <lkotzanie...@bloomberg.net> AuthorDate: Thu Aug 21 12:57:34 2025 -0400 SOLR-17863: fix per segment fingerprint cache race (#3477) Fix race condition in SolrCore's fingerprint cache which caused leader election to hang --- solr/CHANGES.txt | 2 ++ .../src/java/org/apache/solr/core/SolrCore.java | 14 ++++++------ .../solr/update/SolrIndexFingerprintTest.java | 26 +++++++++------------- 3 files changed, 19 insertions(+), 23 deletions(-) diff --git a/solr/CHANGES.txt b/solr/CHANGES.txt index 3bcb1f4385c..d562358f6e1 100644 --- a/solr/CHANGES.txt +++ b/solr/CHANGES.txt @@ -28,6 +28,8 @@ Bug Fixes * SOLR-17789: When Solr forwards/proxies requests to another node that can service the request, it needs to pass authorization headers. (Timo Crabbé) +* SOLR-17863: Fix race condition in SolrCore's fingerprint cache which caused leader election to hang. (Luke Kot-Zaniewski, Matthew Biscocho) + Dependency Upgrades --------------------- (No changes) diff --git a/solr/core/src/java/org/apache/solr/core/SolrCore.java b/solr/core/src/java/org/apache/solr/core/SolrCore.java index 7e0c636dcbc..0f1568de92f 100644 --- a/solr/core/src/java/org/apache/solr/core/SolrCore.java +++ b/solr/core/src/java/org/apache/solr/core/SolrCore.java @@ -21,6 +21,8 @@ import static org.apache.solr.handler.admin.MetricsHandler.PROMETHEUS_METRICS_WT import com.codahale.metrics.Counter; import com.codahale.metrics.Timer; +import com.github.benmanes.caffeine.cache.Cache; +import com.github.benmanes.caffeine.cache.Caffeine; import java.io.Closeable; import java.io.File; import java.io.FileNotFoundException; @@ -55,7 +57,6 @@ import java.util.Set; import java.util.Spliterator; import java.util.Spliterators; import java.util.UUID; -import java.util.WeakHashMap; import java.util.concurrent.Callable; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.CopyOnWriteArrayList; @@ -246,6 +247,8 @@ public class SolrCore implements SolrInfoBean, Closeable { private IndexReaderFactory indexReaderFactory; private final Codec codec; private final ConfigSet configSet; + private final Cache<IndexReader.CacheKey, IndexFingerprint> perSegmentFingerprintCache = + Caffeine.newBuilder().weakKeys().build(); // singleton listener for all packages used in schema private final CircuitBreakerRegistry circuitBreakerRegistry; @@ -276,9 +279,6 @@ public class SolrCore implements SolrInfoBean, Closeable { return startTime; } - private final Map<IndexReader.CacheKey, IndexFingerprint> perSegmentFingerprintCache = - new WeakHashMap<>(); - public long getStartNanoTime() { return startNanoTime; } @@ -2146,7 +2146,7 @@ public class SolrCore implements SolrInfoBean, Closeable { } IndexFingerprint f = null; - f = perSegmentFingerprintCache.get(cacheHelper.getKey()); + f = perSegmentFingerprintCache.getIfPresent(cacheHelper.getKey()); // fingerprint is either not cached or if we want fingerprint only up to a version less than // maxVersionEncountered in the segment, or documents were deleted from segment for which // fingerprint was cached @@ -2186,8 +2186,8 @@ public class SolrCore implements SolrInfoBean, Closeable { } if (log.isDebugEnabled()) { log.debug( - "Cache Size: {}, Segments Size:{}", - perSegmentFingerprintCache.size(), + "Approximate perSegmentFingerprintCache Size: {}, Segments Size:{}", + perSegmentFingerprintCache.estimatedSize(), searcher.getTopReaderContext().leaves().size()); } return f; diff --git a/solr/core/src/test/org/apache/solr/update/SolrIndexFingerprintTest.java b/solr/core/src/test/org/apache/solr/update/SolrIndexFingerprintTest.java index a2c7897e82e..eead5d7441f 100644 --- a/solr/core/src/test/org/apache/solr/update/SolrIndexFingerprintTest.java +++ b/solr/core/src/test/org/apache/solr/update/SolrIndexFingerprintTest.java @@ -17,6 +17,7 @@ package org.apache.solr.update; import java.io.IOException; +import java.util.stream.IntStream; import org.apache.lucene.index.FilterLeafReader; import org.apache.lucene.index.LeafReader; import org.apache.solr.SolrTestCaseJ4; @@ -28,7 +29,7 @@ public class SolrIndexFingerprintTest extends SolrTestCaseJ4 { @BeforeClass public static void beforeTests() throws Exception { - initCore("solrconfig.xml", "schema.xml"); + initCore("solrconfig-nomergepolicyfactory.xml", "schema.xml"); } @Test @@ -36,21 +37,14 @@ public class SolrIndexFingerprintTest extends SolrTestCaseJ4 { long maxVersion = Long.MAX_VALUE; SolrCore core = h.getCore(); - // Create a set of 3 segments - assertU(adoc("id", "101")); - assertU(adoc("id", "102")); - assertU(adoc("id", "103")); - assertU(commit()); - - assertU(adoc("id", "104")); - assertU(adoc("id", "105")); - assertU(adoc("id", "106")); - assertU(commit()); - - assertU(adoc("id", "107")); - assertU(adoc("id", "108")); - assertU(adoc("id", "109")); - assertU(commit()); + int numDocs = RANDOM_MULTIPLIER == 1 ? 3 : 500; + // Create a set of many segments (to catch race conditions, i.e. SOLR-17863) + IntStream.range(0, numDocs) + .forEach( + i -> { + assertU(adoc("id", "" + i)); + assertU(commit()); + }); try (var searcher = core.getSearcher().get()) { // Compute fingerprint sequentially to compare with parallel computation