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

ctubbsii pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/accumulo-classloaders.git


The following commit(s) were added to refs/heads/main by this push:
     new a83e25a  tests concurrent deletes of files in resources dir (#65)
a83e25a is described below

commit a83e25a3c15c9a39fea2e10c8216c2f2a6b99225
Author: Keith Turner <[email protected]>
AuthorDate: Thu Feb 5 14:51:38 2026 -0500

    tests concurrent deletes of files in resources dir (#65)
---
 .../LocalCachingContextClassLoaderFactoryTest.java | 86 +++++++++++++++++++++-
 1 file changed, 85 insertions(+), 1 deletion(-)

diff --git 
a/modules/local-caching-classloader/src/test/java/org/apache/accumulo/classloader/lcc/LocalCachingContextClassLoaderFactoryTest.java
 
b/modules/local-caching-classloader/src/test/java/org/apache/accumulo/classloader/lcc/LocalCachingContextClassLoaderFactoryTest.java
index 1c6515a..7d68d3b 100644
--- 
a/modules/local-caching-classloader/src/test/java/org/apache/accumulo/classloader/lcc/LocalCachingContextClassLoaderFactoryTest.java
+++ 
b/modules/local-caching-classloader/src/test/java/org/apache/accumulo/classloader/lcc/LocalCachingContextClassLoaderFactoryTest.java
@@ -29,6 +29,7 @@ import static 
org.apache.accumulo.classloader.lcc.TestUtils.updateContextDefinit
 import static org.junit.jupiter.api.Assertions.assertEquals;
 import static org.junit.jupiter.api.Assertions.assertNotEquals;
 import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertNotSame;
 import static org.junit.jupiter.api.Assertions.assertThrows;
 import static org.junit.jupiter.api.Assertions.assertTrue;
 import static org.junit.jupiter.api.Assertions.fail;
@@ -47,6 +48,10 @@ import java.util.LinkedHashSet;
 import java.util.List;
 import java.util.Map;
 import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Executors;
+import java.util.concurrent.Future;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.regex.PatternSyntaxException;
 import java.util.stream.Collectors;
 
@@ -57,6 +62,7 @@ import org.apache.accumulo.classloader.lcc.util.LocalStore;
 import org.apache.accumulo.core.conf.ConfigurationCopy;
 import 
org.apache.accumulo.core.spi.common.ContextClassLoaderFactory.ContextClassLoaderException;
 import org.apache.accumulo.core.util.ConfigurationImpl;
+import org.apache.accumulo.core.util.Timer;
 import org.apache.hadoop.fs.FileSystem;
 import org.apache.hadoop.hdfs.MiniDFSCluster;
 import org.eclipse.jetty.server.Server;
@@ -64,7 +70,10 @@ import org.junit.jupiter.api.AfterAll;
 import org.junit.jupiter.api.BeforeAll;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.io.CleanupMode;
 import org.junit.jupiter.api.io.TempDir;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
 import com.google.gson.JsonSyntaxException;
 
@@ -73,6 +82,8 @@ public class LocalCachingContextClassLoaderFactoryTest {
   protected static final int MONITOR_INTERVAL_SECS = 5;
   // MD5 sum for "bad"
   private static final String BAD_MD5 = "bae60998ffe4923b131e3d6e4c19993e";
+  private static final Logger log =
+      LoggerFactory.getLogger(LocalCachingContextClassLoaderFactoryTest.class);
   private static MiniDFSCluster hdfs;
   private static FileSystem fs;
   private static Server jetty;
@@ -92,7 +103,7 @@ public class LocalCachingContextClassLoaderFactoryTest {
   private LocalCachingContextClassLoaderFactory FACTORY;
   private Path baseCacheDir;
 
-  @TempDir
+  @TempDir(cleanup = CleanupMode.ON_SUCCESS)
   private Path tempDir;
 
   @BeforeAll
@@ -826,4 +837,77 @@ public class LocalCachingContextClassLoaderFactoryTest {
     // ensure it works now
     FACTORY.getClassLoader(updatedDefUrl2.toString());
   }
+
+  @Test
+  public void testConcurrentDeletes() throws Exception {
+
+    var executor = Executors.newCachedThreadPool();
+
+    AtomicBoolean stop = new AtomicBoolean(false);
+
+    // create a background task that continually concurrently deletes files in 
the resources dir
+    var deleteFuture = executor.submit(() -> {
+      while (!stop.get()) {
+        var resourcesDir = 
tempDir.resolve("base").resolve("resources").toFile();
+        assertTrue(resourcesDir.exists() && resourcesDir.isDirectory());
+        var files = resourcesDir.listFiles();
+        for (var file : files) {
+          assertTrue(file.delete());
+        }
+        Thread.sleep(100);
+      }
+      return null;
+    });
+
+    var def = ContextDefinition.create(100, "SHA-512", jarAOrigLocation, 
jarBOrigLocation,
+        jarCOrigLocation, jarDOrigLocation);
+
+    List<Future<?>> futures = new ArrayList<>();
+
+    // create 10 threads that are continually creating new classloaders, the 
deletes should cause
+    // hard link creations to fail sometimes
+    for (int i = 0; i < 10; i++) {
+      int threadNum = i;
+      var future = executor.submit(() -> {
+        Timer timer = Timer.startNew();
+        int j = 0;
+        ClassLoader lastCl = null;
+        while (!timer.hasElapsed(10, TimeUnit.SECONDS)) {
+          var contextFile = tempDir.resolve("context-cd-" + threadNum + "_" + 
j + ".json");
+          Files.writeString(contextFile, def.toJson());
+          var contextUrl = contextFile.toUri().toURL().toExternalForm();
+
+          final ClassLoader cl = FACTORY.getClassLoader(contextUrl);
+          // This test is assuming that each call above creates a new 
classloader which in turn
+          // creates new hard links. This is checking that assumption in case 
the impl changes and
+          // this test needs to be reevaluated.
+          assertNotSame(cl, lastCl);
+          lastCl = cl;
+          testClassLoads(cl, classA);
+          testClassLoads(cl, classB);
+          testClassLoads(cl, classC);
+          testClassLoads(cl, classD);
+          j++;
+        }
+
+        return null;
+      });
+      futures.add(future);
+    }
+
+    for (var future : futures) {
+      future.get();
+    }
+
+    stop.set(true);
+    // ensure the delete task had no errors
+    deleteFuture.get();
+    executor.shutdown();
+
+    var workingDir = tempDir.resolve("base").resolve("working").toFile();
+    var filesList = workingDir.listFiles();
+    var workingDirs = filesList == null ? 0 : filesList.length;
+    // check that many hard link directories were created
+    assertTrue(workingDirs > 50, () -> "workingDirs:" + workingDirs);
+  }
 }

Reply via email to