This is an automated email from the ASF dual-hosted git repository.
mlbiscoc 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 4659f9b1f05 SOLR-17863: fix per segment fingerprint cache race (#3477)
4659f9b1f05 is described below
commit 4659f9b1f053d091d06c47e6a9cc96e4bc6b2a5c
Author: Luke Kot-Zaniewski <[email protected]>
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 ab0755ef95e..6ce45b4dfd0 100644
--- a/solr/CHANGES.txt
+++ b/solr/CHANGES.txt
@@ -235,6 +235,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 b8ad0c5c18f..ce0b94aa220 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.FileNotFoundException;
import java.io.IOException;
@@ -54,7 +56,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;
@@ -240,6 +241,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;
@@ -270,9 +273,6 @@ public class SolrCore implements SolrInfoBean, Closeable {
return startTime;
}
- private final Map<IndexReader.CacheKey, IndexFingerprint>
perSegmentFingerprintCache =
- new WeakHashMap<>();
-
public long getStartNanoTime() {
return startNanoTime;
}
@@ -2157,7 +2157,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
@@ -2197,8 +2197,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