rishabhdaim commented on code in PR #3071:
URL: https://github.com/apache/jackrabbit-oak/pull/3071#discussion_r3766822147


##########
oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/SegmentId.java:
##########
@@ -221,6 +233,19 @@ void unloaded() {
         this.segment = null;
     }
 
+    /**
+     * Like {@link #unloaded()}, but only clears the memoised segment if it is 
still
+     * {@code expected}. Use this for asynchronous removal notifications, 
where {@code expected}
+     * may already have been superseded by a concurrent {@link 
#loaded(Segment)}.
+     *
+     * @param expected the segment that was removed; the field is left 
untouched if it no longer
+     *                 holds this value
+     * @return {@code true} if {@code expected} was still memoised and has now 
been cleared
+     */
+    boolean unloadIfCurrent(@NotNull Segment expected) {
+        return SEGMENT.compareAndSet(this, expected, null);
+    }

Review Comment:
   fixed in 
https://github.com/apache/jackrabbit-oak/pull/3071/commits/9a664888e298537fe6feefb04fdca234204336a5



##########
oak-core-spi/src/test/java/org/apache/jackrabbit/oak/cache/impl/CacheBuilderMaintenanceTest.java:
##########
@@ -0,0 +1,369 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.jackrabbit.oak.cache.impl;
+
+import java.time.Duration;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicReference;
+
+import org.apache.jackrabbit.oak.cache.api.Cache;
+import org.apache.jackrabbit.oak.cache.api.CacheBuilder;
+import org.apache.jackrabbit.oak.cache.api.EvictionCause;
+import org.apache.jackrabbit.oak.cache.api.LoadingCache;
+import org.junit.After;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+
+/**
+ * Tests that Caffeine cache maintenance (eviction, removal notification) is 
dispatched
+ * off the calling thread, per OAK-12290.
+ */
+public class CacheBuilderMaintenanceTest {
+
+    private static final long TIMEOUT_SECONDS = 10;
+
+    /**
+     * The toggle is process-wide static state, so reset it around every test 
- a test that leaked
+     * inline maintenance would silently change the behaviour asserted by 
every later test in the
+     * same JVM.
+     */
+    @Before
+    public void enableOak12290Toggle() {
+        CacheBuilder.FT_OAK_12290_ASYNC_CACHE_MAINTENANCE_ENABLED.set(true);
+    }
+
+    @After
+    public void resetOak12290Toggle() {
+        CacheBuilder.FT_OAK_12290_ASYNC_CACHE_MAINTENANCE_ENABLED.set(true);
+    }
+
+    /** Maintenance triggered by a write must not be executed by the writing 
thread. */
+    @Test
+    public void evictionNotificationRunsOffCallerThread() throws 
InterruptedException {
+        AtomicReference<Thread> evictionThread = new AtomicReference<>();
+        CountDownLatch evicted = new CountDownLatch(1);
+
+        Cache<String, String> cache = CacheBuilder.<String, String>newBuilder()
+                .maximumSize(1)
+                .evictionListener((k, v, cause) -> {
+                    if (cause == EvictionCause.SIZE) {
+                        evictionThread.set(Thread.currentThread());
+                        evicted.countDown();
+                    }
+                })
+                .build();
+
+        cache.put("k1", "v1");
+        cache.put("k2", "v2");
+
+        Assert.assertTrue("size-based eviction was never notified",
+                evicted.await(TIMEOUT_SECONDS, TimeUnit.SECONDS));
+        Assert.assertNotSame("cache maintenance must not run on the calling 
thread",
+                Thread.currentThread(), evictionThread.get());
+    }
+
+    /**
+     * A slow maintenance callback must not stall the writer. With inline 
maintenance the
+     * writer runs the callback itself while holding the eviction lock, so 
{@code put()}
+     * cannot return until the callback finishes.
+     */
+    @Test(timeout = TIMEOUT_SECONDS * 1000)
+    public void slowMaintenanceDoesNotBlockCallerThread() throws 
InterruptedException {
+        CountDownLatch release = new CountDownLatch(1);
+        CountDownLatch maintenanceDone = new CountDownLatch(1);
+
+        Cache<String, String> cache = CacheBuilder.<String, String>newBuilder()
+                .maximumSize(1)
+                .evictionListener((k, v, cause) -> {
+                    if (cause == EvictionCause.SIZE) {
+                        try {
+                            release.await(TIMEOUT_SECONDS, TimeUnit.SECONDS);
+                        } catch (InterruptedException e) {
+                            Thread.currentThread().interrupt();
+                        }
+                        maintenanceDone.countDown();
+                    }
+                })
+                .build();
+
+        cache.put("k1", "v1");
+        // returns only if the blocked maintenance callback runs on another 
thread
+        cache.put("k2", "v2");
+
+        release.countDown();
+        Assert.assertTrue("maintenance callback never completed",
+                maintenanceDone.await(TIMEOUT_SECONDS, TimeUnit.SECONDS));
+    }
+
+    /** Disabling the toggle restores the previous inline-maintenance 
behaviour. */
+    @Test
+    public void toggleDisabledRunsMaintenanceInline() {
+        AtomicReference<Thread> evictionThread = new AtomicReference<>();
+
+        CacheBuilder.FT_OAK_12290_ASYNC_CACHE_MAINTENANCE_ENABLED.set(false);
+        Cache<String, String> cache = CacheBuilder.<String, String>newBuilder()
+                .maximumSize(1)
+                .evictionListener((k, v, cause) -> {
+                    if (cause == EvictionCause.SIZE) {
+                        evictionThread.set(Thread.currentThread());
+                    }
+                })
+                .build();
+
+        cache.put("k1", "v1");
+        cache.put("k2", "v2");
+
+        Assert.assertSame("maintenance should run inline when the toggle is 
off",
+                Thread.currentThread(), evictionThread.get());
+    }
+
+    /**
+     * Maintenance must run on Oak's own named pool, not on {@code 
ForkJoinPool.commonPool()} -
+     * the common pool is shared with the hosting application and can be 
configured with zero
+     * workers, in which case submitted tasks are queued and never run.
+     */
+    @Test
+    public void maintenanceRunsOnOakOwnedThread() throws InterruptedException {
+        AtomicReference<String> threadName = new AtomicReference<>();
+        CountDownLatch evicted = new CountDownLatch(1);
+
+        Cache<String, String> cache = CacheBuilder.<String, String>newBuilder()
+                .maximumSize(1)
+                .evictionListener((k, v, cause) -> {
+                    if (cause == EvictionCause.SIZE) {
+                        threadName.set(Thread.currentThread().getName());
+                        evicted.countDown();
+                    }
+                })
+                .build();
+
+        cache.put("k1", "v1");
+        cache.put("k2", "v2");
+
+        Assert.assertTrue("size-based eviction was never notified",
+                evicted.await(TIMEOUT_SECONDS, TimeUnit.SECONDS));
+        Assert.assertTrue("maintenance ran on an unexpected thread: " + 
threadName.get(),
+                threadName.get().startsWith("oak-cache-maintenance-"));

Review Comment:
   fixed in 
https://github.com/apache/jackrabbit-oak/pull/3071/commits/9a664888e298537fe6feefb04fdca234204336a5



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to