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

Reply via email to