You are viewing a plain text version of this content. The canonical link for it is here.
Posted to oak-commits@jackrabbit.apache.org by th...@apache.org on 2025/11/19 09:32:11 UTC

(jackrabbit-oak) branch trunk updated: OAK-12012 Sporadic SessionSaveDelayerConfigTest failure (#2619)

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

thomasm pushed a commit to branch trunk
in repository https://gitbox.apache.org/repos/asf/jackrabbit-oak.git


The following commit(s) were added to refs/heads/trunk by this push:
     new f879578d5d OAK-12012 Sporadic SessionSaveDelayerConfigTest failure (#2619)
f879578d5d is described below

commit f879578d5d7659066555cd6345f10cd2c1bf6621
Author: Thomas Mueller <th...@apache.org>
AuthorDate: Wed Nov 19 10:32:06 2025 +0100

    OAK-12012 Sporadic SessionSaveDelayerConfigTest failure (#2619)
---
 .../oak/jcr/session/SessionSaveDelayerConfig.java  | 17 ++++-
 .../jcr/session/SessionSaveDelayerConfigTest.java  | 74 +++++++++++++---------
 2 files changed, 61 insertions(+), 30 deletions(-)

diff --git a/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/session/SessionSaveDelayerConfig.java b/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/session/SessionSaveDelayerConfig.java
index 88376fd7fd..05b3cfc448 100644
--- a/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/session/SessionSaveDelayerConfig.java
+++ b/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/session/SessionSaveDelayerConfig.java
@@ -200,10 +200,25 @@ public class SessionSaveDelayerConfig {
             this.maxSavesPerSecond = maxSavesPerSecond;
         }
 
+        /**
+         * Get the number of nanoseconds to delay the next operation.
+         *
+         * @return the time in nanoseconds
+         */
         public long getDelayNanos() {
+            return getDelayNanos(0);
+        }
+
+        /**
+         * Get the number of nanoseconds to delay the next operation.
+         *
+         * @param timeMillis the injected current time, or 0 to read it using System.currentTimeMillis() if needed
+         * @return the time in nanoseconds
+         */
+        public long getDelayNanos(long timeMillis) {
             long totalDelayNanos = baseDelayNanos;
             if (maxSavesPerSecond > 0) {
-                long currentTime = System.currentTimeMillis();
+                long currentTime = timeMillis != 0 ? timeMillis : System.currentTimeMillis();
                 double intervalMs = 1000.0 / maxSavesPerSecond;
                 long lastMatchTime = lastMatch.get();
                 if (lastMatchTime > 0) {
diff --git a/oak-jcr/src/test/java/org/apache/jackrabbit/oak/jcr/session/SessionSaveDelayerConfigTest.java b/oak-jcr/src/test/java/org/apache/jackrabbit/oak/jcr/session/SessionSaveDelayerConfigTest.java
index 2e0120ff6c..3ca1da0434 100644
--- a/oak-jcr/src/test/java/org/apache/jackrabbit/oak/jcr/session/SessionSaveDelayerConfigTest.java
+++ b/oak-jcr/src/test/java/org/apache/jackrabbit/oak/jcr/session/SessionSaveDelayerConfigTest.java
@@ -68,13 +68,15 @@ public class SessionSaveDelayerConfigTest {
         List<SessionSaveDelayerConfig.DelayEntry> entries = config.getEntries();
         assertEquals(2, entries.size());
 
+        long timeMillis = System.currentTimeMillis();
+
         SessionSaveDelayerConfig.DelayEntry first = entries.get(0);
-        assertEquals(500_000L, first.getDelayNanos());
+        assertEquals(500_000L, first.getDelayNanos(timeMillis));
         assertEquals("worker-\\d+", first.getThreadNamePattern().pattern());
         assertNull(first.getStackTracePattern());
 
         SessionSaveDelayerConfig.DelayEntry second = entries.get(1);
-        assertEquals(1_000_000L, second.getDelayNanos());
+        assertEquals(1_000_000L, second.getDelayNanos(timeMillis));
         assertEquals("thread-.*", second.getThreadNamePattern().pattern());
         assertNotNull(second.getStackTracePattern());
         assertEquals(".*SomeClass.*", second.getStackTracePattern().pattern());
@@ -137,9 +139,11 @@ public class SessionSaveDelayerConfigTest {
         List<SessionSaveDelayerConfig.DelayEntry> entries = config.getEntries();
         // Only the first entry should be valid (has both delay and threadNameRegex)
         assertEquals(1, entries.size());
-        
+
+        long timeMillis = System.currentTimeMillis();
+
         SessionSaveDelayerConfig.DelayEntry entry = entries.get(0);
-        assertEquals(1000_000L, entry.getDelayNanos());
+        assertEquals(1000_000L, entry.getDelayNanos(timeMillis));
         assertEquals("thread-.*", entry.getThreadNamePattern().pattern());
         assertNull(entry.getStackTracePattern());
     }
@@ -217,7 +221,7 @@ public class SessionSaveDelayerConfigTest {
                 "}";
 
         SessionSaveDelayerConfig config = SessionSaveDelayerConfig.fromJson(json);
-        
+
         assertEquals("{\n"
                 + "  \"entries\": [{\n"
                 + "    \"delayMillis\": 1.0, \"threadNameRegex\": \"thread-.*\", \"stackTraceRegex\": \".*SomeClass.*\"\n"
@@ -243,13 +247,15 @@ public class SessionSaveDelayerConfigTest {
         List<SessionSaveDelayerConfig.DelayEntry> entries = config.getEntries();
         assertEquals(1, entries.size());
 
+        long timeMillis = System.currentTimeMillis();
+
         SessionSaveDelayerConfig.DelayEntry entry = entries.get(0);
-        assertEquals(2_000_000L, entry.getDelayNanos());
+        assertEquals(2_000_000L, entry.getDelayNanos(timeMillis));
 
         // Test case-insensitive thread name matching
         assertTrue(entry.matches("pool-1-thread-5", null, "at com.example.Service.save()"));
         assertTrue(entry.matches("POOL-2-THREAD-10", null, "at com.example.Service.update()"));
-        
+
         // Test stack trace pattern matching
         assertTrue(entry.matches("pool-1-thread-1", null, "at com.example.Repository.delete(Repository.java:100)"));
         assertFalse(entry.matches("pool-1-thread-1", null, "at com.example.Service.get()"));
@@ -273,8 +279,10 @@ public class SessionSaveDelayerConfigTest {
         List<SessionSaveDelayerConfig.DelayEntry> entries = config.getEntries();
         assertEquals(1, entries.size());
 
+        long timeMillis = System.currentTimeMillis();
+
         SessionSaveDelayerConfig.DelayEntry entry = entries.get(0);
-        assertEquals(1_000_000L, entry.getDelayNanos());
+        assertEquals(1_000_000L, entry.getDelayNanos(timeMillis));
 
         // Test userDataPattern matching
         assertTrue(entry.matches("any-thread", "admin", null));
@@ -365,7 +373,7 @@ public class SessionSaveDelayerConfigTest {
                 "}";
 
         SessionSaveDelayerConfig config = SessionSaveDelayerConfig.fromJson(json);
-        
+
         // Test that first matching entry is used
         assertEquals(100_000L, config.getDelayNanos("thread-1", "admin", null));
         assertEquals(200_000L, config.getDelayNanos("thread-1", "guest123", null));
@@ -446,7 +454,7 @@ public class SessionSaveDelayerConfigTest {
         assertFalse(entry.matches("any-thread", "user", null));
     }
 
-    @Test 
+    @Test
     public void testGetDelayNanosWithUserDataPattern() {
         String json = "{\n" +
                 "  \"entries\": [\n" +
@@ -463,15 +471,15 @@ public class SessionSaveDelayerConfigTest {
                 "}";
 
         SessionSaveDelayerConfig config = SessionSaveDelayerConfig.fromJson(json);
-        
+
         // When userData matches first entry's pattern
         assertEquals(1_000_000L, config.getDelayNanos("thread-1", "admin", null));
         assertEquals(1_000_000L, config.getDelayNanos("thread-1", "admin123", null));
-        
+
         // When userData doesn't match first entry but matches second (no userData pattern)
         assertEquals(500_000L, config.getDelayNanos("thread-1", "user", null));
         assertEquals(500_000L, config.getDelayNanos("thread-1", "guest", null));
-        
+
         // When userData is null, first entry shouldn't match but second should
         assertEquals(500_000L, config.getDelayNanos("thread-1", null, null));
     }
@@ -498,14 +506,16 @@ public class SessionSaveDelayerConfigTest {
         assertEquals(100_000L, entry.getBaseDelayNanos());
         assertEquals(2.0, entry.getMaxSavesPerSecond(), 0.001);
 
+        long timeMillis = System.currentTimeMillis();
+
         // First call should only have base delay
-        long firstDelay = entry.getDelayNanos();
+        long firstDelay = entry.getDelayNanos(timeMillis);
         assertEquals(100_000L, firstDelay);
 
         // Second call immediately should have additional rate limit delay
         // With 2 saves per second, minimum interval is 500ms
-        long secondDelay = entry.getDelayNanos();
-        assertTrue("Second delay should be >= base delay + rate limit delay", 
+        long secondDelay = entry.getDelayNanos(timeMillis);
+        assertTrue("Second delay should be >= base delay + rate limit delay, was " + secondDelay,
                 secondDelay >= 100_000L + 400_000_000L); // ~500ms in nanos
     }
 
@@ -530,10 +540,12 @@ public class SessionSaveDelayerConfigTest {
         SessionSaveDelayerConfig.DelayEntry entry = entries.get(0);
         assertEquals(0.0, entry.getMaxSavesPerSecond(), 0.001);
 
+        long timeMillis = System.currentTimeMillis();
+
         // All calls should only have base delay since rate limiting is disabled
-        assertEquals(100_000L, entry.getDelayNanos());
-        assertEquals(100_000L, entry.getDelayNanos());
-        assertEquals(100_000L, entry.getDelayNanos());
+        assertEquals(100_000L, entry.getDelayNanos(timeMillis));
+        assertEquals(100_000L, entry.getDelayNanos(timeMillis));
+        assertEquals(100_000L, entry.getDelayNanos(timeMillis));
     }
 
     @Test
@@ -589,18 +601,20 @@ public class SessionSaveDelayerConfigTest {
         SessionSaveDelayerConfig config = SessionSaveDelayerConfig.fromJson(json);
         SessionSaveDelayerConfig.DelayEntry entry = config.getEntries().get(0);
 
+        long timeMillis = System.currentTimeMillis();
+
         // With 10 saves per second, minimum interval is 100ms
-        long firstDelay = entry.getDelayNanos();
+        long firstDelay = entry.getDelayNanos(timeMillis);
         assertEquals(0L, firstDelay); // No base delay
 
-        long secondDelay = entry.getDelayNanos();
-        assertTrue("Should have rate limit delay", secondDelay >= 90_000_000L); // ~100ms in nanos
+        long secondDelay = entry.getDelayNanos(timeMillis);
+        assertTrue("Should have rate limit delay, was " + secondDelay, secondDelay >= 90_000_000L); // ~100ms in nanos
     }
 
 
 
     @Test
-    public void testRateLimitingCombinedWithUserDataPattern() {
+    public void testRateLimitingCombinedWithUserDataPattern() throws InterruptedException {
         String json = "{\n" +
                 "  \"entries\": [\n" +
                 "    {\n" +
@@ -619,12 +633,14 @@ public class SessionSaveDelayerConfigTest {
         assertTrue(entry.matches("thread-1", "admin", null));
         assertFalse(entry.matches("thread-1", "user", null));
 
+        long timeMillis = System.currentTimeMillis();
+
         // Rate limiting should work for matching entries
-        long firstDelay = entry.getDelayNanos();
+        long firstDelay = entry.getDelayNanos(timeMillis);
         assertEquals(100_000L, firstDelay);
 
-        long secondDelay = entry.getDelayNanos();
-        assertTrue("Should have rate limit delay", secondDelay >= 1_000_000_000L); // ~1000ms
+        long secondDelay = entry.getDelayNanos(timeMillis);
+        assertTrue("Should have rate limit delay, was " + secondDelay, secondDelay >= 1_000_000_000L); // ~1000ms
     }
 
     @Test
@@ -679,13 +695,13 @@ public class SessionSaveDelayerConfigTest {
                 "}";
 
         SessionSaveDelayerConfig config = SessionSaveDelayerConfig.fromJson(json);
-        
+
         // First call should only have base delay
         long firstDelay = config.getDelayNanos("thread-1", null, null);
         assertEquals(100_000L, firstDelay);
 
         // Second call should have additional rate limit delay
         long secondDelay = config.getDelayNanos("thread-1", null, null);
-        assertTrue("Should have rate limit delay", secondDelay >= 400_000_000L);
+        assertTrue("Should have rate limit delay, was " + secondDelay, secondDelay >= 400_000_000L);
     }
-} 
\ No newline at end of file
+}
\ No newline at end of file