This is an automated email from the ASF dual-hosted git repository.

frankgh pushed a commit to branch trunk
in repository https://gitbox.apache.org/repos/asf/cassandra-sidecar.git


The following commit(s) were added to refs/heads/trunk by this push:
     new 56e2bb0f CASSSIDECAR-349: Thread race issue when adding excluded 
metric in FilteringMetricRegistry (#263)
56e2bb0f is described below

commit 56e2bb0fd63785768039b28c35eed3c0fb6c1b4a
Author: Francisco Guerrero <[email protected]>
AuthorDate: Mon Sep 29 14:03:23 2025 -0700

    CASSSIDECAR-349: Thread race issue when adding excluded metric in 
FilteringMetricRegistry (#263)
    
    Patch by Francisco Guerrero; reviewed by Yifan Cai for CASSSIDECAR-349
---
 CHANGES.txt                                        |  1 +
 .../sidecar/metrics/FilteringMetricRegistry.java   | 14 +++++++++++---
 .../metrics/FilteringMetricRegistryTest.java       | 22 ++++++++++++++++++++--
 3 files changed, 32 insertions(+), 5 deletions(-)

diff --git a/CHANGES.txt b/CHANGES.txt
index 35e85a5d..07c4e007 100644
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -1,5 +1,6 @@
 0.3.0
 -----
+ * Thread race issue when adding excluded metric in FilteringMetricRegistry 
(CASSSIDECAR-349)
  * Add support for stateless JWT authentication using public keys 
(CASSSIDECAR-334)
  * Improve FilteringMetricRegistry implementation (CASSSIDECAR-347)
  * Add lifecycle APIs for starting and stopping Cassandra (CASSSIDECAR-266)
diff --git 
a/server/src/main/java/org/apache/cassandra/sidecar/metrics/FilteringMetricRegistry.java
 
b/server/src/main/java/org/apache/cassandra/sidecar/metrics/FilteringMetricRegistry.java
index ddebb085..835363a8 100644
--- 
a/server/src/main/java/org/apache/cassandra/sidecar/metrics/FilteringMetricRegistry.java
+++ 
b/server/src/main/java/org/apache/cassandra/sidecar/metrics/FilteringMetricRegistry.java
@@ -22,6 +22,7 @@ import java.util.Collections;
 import java.util.HashMap;
 import java.util.Map;
 import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.function.Function;
 import java.util.function.Predicate;
 
@@ -283,15 +284,22 @@ public class FilteringMetricRegistry extends 
MetricRegistry
      */
     private Metric addExcludedMetricIfNotExists(String name, Function<String, 
? extends Metric> mappingFunction)
     {
-        return excludedMetrics.computeIfAbsent(name, k -> {
+        AtomicBoolean registeredExcludedMetric = new AtomicBoolean(false);
+        Metric excludedMetric = excludedMetrics.computeIfAbsent(name, k -> {
+            registeredExcludedMetric.set(true);
+            return mappingFunction.apply(k);
+        });
+
+        if (registeredExcludedMetric.get())
+        {
             // allMetrics needs to be recomputed when an excluded metric is 
registered
             synchronized (this)
             {
                 allMetrics = null;
             }
+        }
 
-            return mappingFunction.apply(k);
-        });
+        return excludedMetric;
     }
 
     /**
diff --git 
a/server/src/test/java/org/apache/cassandra/sidecar/metrics/FilteringMetricRegistryTest.java
 
b/server/src/test/java/org/apache/cassandra/sidecar/metrics/FilteringMetricRegistryTest.java
index d8585374..9b8aa0c0 100644
--- 
a/server/src/test/java/org/apache/cassandra/sidecar/metrics/FilteringMetricRegistryTest.java
+++ 
b/server/src/test/java/org/apache/cassandra/sidecar/metrics/FilteringMetricRegistryTest.java
@@ -21,13 +21,16 @@ package org.apache.cassandra.sidecar.metrics;
 import java.nio.file.Path;
 import java.util.Arrays;
 import java.util.Collections;
+import java.util.List;
 import java.util.Map;
 import java.util.Set;
 import java.util.UUID;
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.ExecutorService;
 import java.util.concurrent.Executors;
+import java.util.concurrent.ThreadLocalRandom;
 import java.util.concurrent.TimeUnit;
+import java.util.function.BiConsumer;
 import java.util.regex.Pattern;
 
 import org.junit.jupiter.api.AfterEach;
@@ -62,6 +65,14 @@ import static 
org.assertj.core.api.Assertions.assertThatThrownBy;
 class FilteringMetricRegistryTest
 {
     private static final MetricRegistry NO_OP_METRIC_REGISTRY = new 
NoopMetricRegistry();
+    private static final List<BiConsumer<FilteringMetricRegistry, String>> 
METRIC_REGISTRY_STRING_BI_CONSUMER = List.of(
+    (r, metricName) -> r.register(metricName, new ThroughputMeter()),
+    FilteringMetricRegistry::meter,
+    FilteringMetricRegistry::gauge,
+    FilteringMetricRegistry::counter,
+    FilteringMetricRegistry::histogram,
+    FilteringMetricRegistry::timer
+    );
     @TempDir
     private Path confPath;
 
@@ -338,11 +349,13 @@ class FilteringMetricRegistryTest
             pool.submit(() -> {
                 try
                 {
+                    String metricName = "metric_" + finalI + "_" + ((finalI % 
2 == 0) ? "even" : "odd");
+                    BiConsumer<FilteringMetricRegistry, String> biConsumer = 
registerRandomMetric();
                     // Invoke register roughly at the same time
                     latch.countDown();
                     latch.await();
 
-                    registry.register("testMetricThroughputMeter_" + finalI + 
"_" + ((finalI % 2 == 0) ? "even" : "odd"), new ThroughputMeter());
+                    biConsumer.accept(registry, metricName);
                     assertThat(registry.getMetrics()).isNotEmpty();
                 }
                 catch (InterruptedException e)
@@ -365,7 +378,7 @@ class FilteringMetricRegistryTest
         Set<String> allMetricNames = allMetrics.keySet();
         for (int i = 0; i < nThreads; i++)
         {
-            String expectedMetricName = "testMetricThroughputMeter_" + i + "_" 
+ ((i % 2 == 0) ? "even" : "odd");
+            String expectedMetricName = "metric_" + i + "_" + ((i % 2 == 0) ? 
"even" : "odd");
             assertThat(allMetricNames).as("Expected metric %s", 
expectedMetricName).contains(expectedMetricName);
         }
     }
@@ -424,4 +437,9 @@ class FilteringMetricRegistryTest
         assertThat(allMetrics).as("About half the metrics are 
removed").hasSize(nThreads / 2);
         assertThat(registry.getIncludedMetrics()).hasSize(nThreads / 2);
     }
+
+    BiConsumer<FilteringMetricRegistry, String> registerRandomMetric()
+    {
+        return 
METRIC_REGISTRY_STRING_BI_CONSUMER.get(ThreadLocalRandom.current().nextInt(METRIC_REGISTRY_STRING_BI_CONSUMER.size()));
+    }
 }


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to