mirror of
https://github.com/VolmitSoftware/Iris.git
synced 2026-10-11 19:14:11 +00:00
Reduced generation routing contention
- Ready generation bindings acquire their lease in one metadata lock pass - Shared routing metadata uses nonfair lock handoffs under worker contention - Single-runtime release avoids allocating an empty retirement list - Concurrent route tests verify shutdown drains and rejects active workers
This commit is contained in:
+7
-1
@@ -51,7 +51,7 @@ public final class GenerationHistoryRuntimeRouter implements AutoCloseable {
|
||||
this.runtimeFactory = Objects.requireNonNull(runtimeFactory, "activation runtime factory");
|
||||
this.bindings = new LinkedHashMap<>();
|
||||
this.retiringBindings = new LinkedHashMap<>();
|
||||
this.stateLock = new ReentrantLock(true);
|
||||
this.stateLock = new ReentrantLock();
|
||||
this.inactive = stateLock.newCondition();
|
||||
this.operationDepth = ThreadLocal.withInitial(() -> 0);
|
||||
this.scopedRoute = new ThreadLocal<>();
|
||||
@@ -449,6 +449,9 @@ public final class GenerationHistoryRuntimeRouter implements AutoCloseable {
|
||||
loader = true;
|
||||
}
|
||||
entry.leases++;
|
||||
if (!entry.loading && entry.loadFailure == null && entry.binding != null) {
|
||||
return new RuntimeLease(this, entry, entry.binding);
|
||||
}
|
||||
} finally {
|
||||
stateLock.unlock();
|
||||
}
|
||||
@@ -584,6 +587,9 @@ public final class GenerationHistoryRuntimeRouter implements AutoCloseable {
|
||||
}
|
||||
|
||||
private List<RuntimeRetirement> collectEvictionsLocked() {
|
||||
if (bindings.size() <= 1) {
|
||||
return List.of();
|
||||
}
|
||||
ArrayList<RuntimeRetirement> retired = new ArrayList<>();
|
||||
while (bindings.size() > 1) {
|
||||
RuntimeCacheEntry candidate = null;
|
||||
|
||||
+45
@@ -389,6 +389,33 @@ public final class GenerationHistoryRuntimeRouterTest {
|
||||
verify(engine, never()).closeDetachedGenerationRuntime(active);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void closeCompletesWhileWorkersContinuouslyAcquireReadyRoutes() throws Exception {
|
||||
Path world = temporaryFolder.newFolder("router-contended-close-world").toPath();
|
||||
GenerationHistory history = createHistory(world, createPack("router-contended-close-pack", "alpha"));
|
||||
IrisEngine engine = mock(IrisEngine.class);
|
||||
FakeRuntimeFactory runtimes = new FakeRuntimeFactory();
|
||||
IrisEngine.GenerationRuntimeBinding active = runtimes.binding(history, history.activeActivation());
|
||||
when(engine.getActiveGenerationRuntimeBinding()).thenReturn(active);
|
||||
ExecutorService executor = Executors.newFixedThreadPool(9);
|
||||
CountDownLatch routing = new CountDownLatch(8);
|
||||
CompletableFuture<?>[] workers = new CompletableFuture<?>[8];
|
||||
try (GenerationHistoryRuntimeRouter router = GenerationHistoryRuntimeRouter.attach(
|
||||
engine, history, (ignored, x, z) -> signature(x, z), runtimes)) {
|
||||
for (int i = 0; i < workers.length; i++) {
|
||||
workers[i] = CompletableFuture.runAsync(() -> routeUntilClosed(router, routing), executor);
|
||||
}
|
||||
assertTrue(routing.await(5, TimeUnit.SECONDS));
|
||||
CompletableFuture.runAsync(router::close, executor).get(5, TimeUnit.SECONDS);
|
||||
CompletableFuture.allOf(workers).get(5, TimeUnit.SECONDS);
|
||||
verify(engine).detachGenerationHistoryRuntimeRouter(router);
|
||||
verify(engine, never()).closeDetachedGenerationRuntime(active);
|
||||
} finally {
|
||||
executor.shutdownNow();
|
||||
assertTrue(executor.awaitTermination(5, TimeUnit.SECONDS));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void closeLeavesTheSingleDefaultRuntimeOwnedByTheEngine() throws Exception {
|
||||
Path world = temporaryFolder.newFolder("router-close-current-world").toPath();
|
||||
@@ -894,6 +921,24 @@ public final class GenerationHistoryRuntimeRouterTest {
|
||||
router.close();
|
||||
}
|
||||
|
||||
private static void routeUntilClosed(GenerationHistoryRuntimeRouter router, CountDownLatch routing) {
|
||||
boolean started = false;
|
||||
while (!Thread.currentThread().isInterrupted()) {
|
||||
try (GenerationHistoryRuntimeRouter.RuntimeRoute route = router.openRoute(0, 0)) {
|
||||
assertEquals(1L, route.activation().activationId());
|
||||
if (!started) {
|
||||
started = true;
|
||||
routing.countDown();
|
||||
}
|
||||
} catch (IOException failure) {
|
||||
throw new AssertionError(failure);
|
||||
} catch (IllegalStateException closed) {
|
||||
assertEquals("Generation-history runtime router is closed.", closed.getMessage());
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private AtomicReference<IrisEngine.GenerationRuntimeBinding> installScopeTracking(IrisEngine engine) {
|
||||
AtomicReference<IrisEngine.GenerationRuntimeBinding> scoped = new AtomicReference<>();
|
||||
when(engine.openGenerationRuntimeScope(any())).thenAnswer(invocation -> {
|
||||
|
||||
Reference in New Issue
Block a user