/** * Creates a new instance of the AsyncStorageWrapper class. * * @param syncStorage A SyncStorage instance that will be wrapped. * @param executor An Executor for async operations. */ public AsyncStorageWrapper(SyncStorage syncStorage, Executor executor) { this.syncStorage = Preconditions.checkNotNull(syncStorage, "syncStorage"); this.executor = Preconditions.checkNotNull(executor, "executor"); this.taskProcessor = new MultiKeySequentialProcessor<>(this.executor); this.closed = new AtomicBoolean(); }
/** * Executes the given Callable asynchronously and returns a CompletableFuture that will be completed with the result. * @param operation The Callable to execute. * @param segmentNames The names of the Segments involved in this operation (for sequencing purposes). */ private <R> CompletableFuture<R> supplyAsync(Callable<R> operation, String... segmentNames) { Exceptions.checkNotClosed(this.closed.get(), this); return this.taskProcessor.add(Arrays.asList(segmentNames), () -> execute(operation)); }
@Override public void close() { if (!this.closed.getAndSet(true)) { this.conditionalUpdateProcessor.close(); this.cacheManager.unregister(this.cache); this.cache.close(); this.recoveryTracker.close(); } }
executeAfterIfNeeded(existingTasks, toRun, result); executeNowIfNeeded(existingTasks, toRun, result); result.whenComplete((r, ex) -> cleanupFilter(keyFilter)); return result;
executeAfterIfNeeded(existingTasks, toRun, result); executeNowIfNeeded(existingTasks, toRun, result); result.whenComplete((r, ex) -> cleanup(keys)); return result;
/** * Tests the processor with a key filter that doesn't match any keys currently executing. */ @Test public void testAddFilterEmpty() throws Exception { final int key1 = 1; @Cleanup val proc = new MultiKeySequentialProcessor<Integer>(executorService()); val toRunFilter = new CompletableFuture<Integer>(); val resultFilter = proc.addWithFilter(key -> key == key1, () -> toRunFilter); // We setup one task to begin with. val result1 = proc.add(Collections.singleton(key1), () -> { Assert.assertTrue("Not expecting individual task to execute yet.", resultFilter.isDone()); return CompletableFuture.completedFuture(1); }); // Complete the task. Verify filter task did not complete. toRunFilter.complete(0); toRunFilter.get(TIMEOUT_MILLIS, TimeUnit.MILLISECONDS); result1.get(TIMEOUT_MILLIS, TimeUnit.MILLISECONDS); Assert.assertEquals("Unexpected result from filter run.", (int) resultFilter.join(), 0); Assert.assertEquals("Unexpected result from task 1.", (int) result1.join(), 1); }
/** * Tests the processor using two keys and verifies the tasks are executed in parallel. */ @Test public void testAddParallelism() throws Exception { final int key1 = 1; final int key2 = 2; @Cleanup val proc = new MultiKeySequentialProcessor<Integer>(executorService()); val toRun1 = new CompletableFuture<Integer>(); val result1 = proc.add(Collections.singleton(key1), () -> toRun1); val toRun2 = new CompletableFuture<Integer>(); val result2 = proc.add(Collections.singleton(key2), () -> toRun2); Assert.assertFalse("Not expecting anything to be done yet.", result1.isDone() || result2.isDone()); // Complete second run. If it completes, we have verified that it hasn't been blocked on the first one. toRun2.complete(10); result2.get(TIMEOUT_MILLIS, TimeUnit.MILLISECONDS); Assert.assertEquals("Unexpected result from second key run.", result2.join(), toRun2.join()); Assert.assertFalse("Not expecting first task to be done yet.", result1.isDone()); // Complete the first run. toRun1.complete(20); result1.get(TIMEOUT_MILLIS, TimeUnit.MILLISECONDS); Assert.assertEquals("Unexpected result from first key run.", result1.join(), toRun1.join()); }
/** * Tests the ability to cancel ongoing tasks when the processor is closed. */ @Test public void testClose() { final int key = 1; @Cleanup val proc = new MultiKeySequentialProcessor<Integer>(executorService()); val toRun = new CompletableFuture<Integer>(); val result = proc.add(Collections.singleton(key), () -> toRun); proc.close(); AssertExtensions.assertThrows( "Task not cancelled.", result::join, ex -> ex instanceof ObjectClosedException); Assert.assertFalse("Not expecting inner blocker task to be done.", toRun.isDone()); } }
return this.recoveryTracker.waitIfNeeded(segment, () -> this.conditionalUpdateProcessor.addWithFilter( conditionKey -> conditionKey.getKey() == segment.getSegmentId(), () -> {
/** * Gets a value representing the number of segments that currently have at least an ongoing task running. */ @VisibleForTesting int getSegmentWithOngoingOperationsCount() { return this.taskProcessor.getCurrentTaskCount(); }
final int key3 = 3; @Cleanup val proc = new MultiKeySequentialProcessor<Integer>(executorService()); val result1 = proc.add(Collections.singleton(key1), () -> toRun1); val result2 = proc.add(Collections.singleton(key2), () -> toRun2); val result3 = proc.add(Collections.singleton(key3), () -> toRun3); val resultFilter = proc.addWithFilter(key -> key == key1 || key == key2, () -> { Assert.assertTrue("Not expecting filter task to execute yet.", result1.isDone() && result2.isDone()); return toRunFilter; val result4 = proc.add(Collections.singleton(key1), () -> { Assert.assertTrue("Not expecting fourth task to execute yet.", resultFilter.isDone()); return CompletableFuture.completedFuture(4);
final int count = 10000; @Cleanup val proc = new MultiKeySequentialProcessor<Integer>(executorService()); val running = new AtomicBoolean(false); val previousRun = new AtomicReference<CompletableFuture<Integer>>(); val thisRun = new CompletableFuture<Integer>(); val pr = previousRun.getAndSet(thisRun); results.add(proc.add(Collections.singleton(key), () -> { if (!running.compareAndSet(false, true)) { Assert.fail("Concurrent execution detected.");
final int key3 = 3; @Cleanup val proc = new MultiKeySequentialProcessor<Integer>(executorService()); val result1 = proc.add(Collections.singleton(key1), () -> toRun1); val result2 = proc.add(Collections.singleton(key2), () -> toRun2); Assert.assertFalse("Not expecting anything to be done yet.", result1.isDone() || result2.isDone()); val result3 = proc.add(Arrays.asList(key1, key2, key3), () -> { Assert.assertTrue("Not expecting third task to execute yet.", result1.isDone() && result2.isDone()); return CompletableFuture.completedFuture(3); val result4 = proc.add(Collections.singleton(key1), () -> { Assert.assertTrue("Not expecting fourth task to execute yet.", result3.isDone()); return CompletableFuture.completedFuture(4); val result5 = proc.add(Collections.singleton(key3), () -> { Assert.assertTrue("Not expecting fifth task to execute yet.", result3.isDone()); return CompletableFuture.completedFuture(5);
/** * Creates a new instance of the ContainerKeyIndex class. * * @param containerId Id of the SegmentContainer this instance is associated with. * @param cacheFactory A {@link CacheFactory} that can be used to create Cache instances. * @param cacheManager A {@link CacheManager} that can be used to manage Cache instances. * @param executor Executor for async operations. */ ContainerKeyIndex(int containerId, @NonNull CacheFactory cacheFactory, @NonNull CacheManager cacheManager, @NonNull ScheduledExecutorService executor) { this.cache = new ContainerKeyCache(containerId, cacheFactory); this.cacheManager = cacheManager; this.cacheManager.register(this.cache); this.executor = executor; this.indexReader = new IndexReader(executor); this.conditionalUpdateProcessor = new MultiKeySequentialProcessor<>(this.executor); this.recoveryTracker = new RecoveryTracker(); this.closed = new AtomicBoolean(); }
return this.conditionalUpdateProcessor.add( keys, () -> validateConditionalUpdate(segment, batch, timer)