use of org.infinispan.util.concurrent.DataOperationOrderer in project infinispan by infinispan.
the class EvictionWithConcurrentOperationsTest method testEvictionDuring.
/**
* Tests that an entry was written to container, but before it releases its orderer it is evicted
*/
void testEvictionDuring(String key, Callable<Object> callable, Consumer<Object> valueConsumer, Consumer<Object> finalResultConsumer, boolean blockOnCompletion) throws TimeoutException, InterruptedException, ExecutionException {
// We use this checkpoint to hold the orderer lock during the write - which means the eviction will have to handle
// it appropriately
CheckPoint operationCheckPoint = new CheckPoint("operation");
operationCheckPoint.triggerForever(blockOnCompletion ? Mocks.AFTER_RELEASE : Mocks.BEFORE_RELEASE);
DataOperationOrderer original;
if (blockOnCompletion) {
// Blocks just before releasing the orderer
original = Mocks.blockingMock(operationCheckPoint, DataOperationOrderer.class, cache, (stub, m) -> stub.when(m).completeOperation(eq(key), any(), any()));
} else {
// Blocks just after acquiring orderer
original = Mocks.blockingMock(operationCheckPoint, DataOperationOrderer.class, cache, (stub, m) -> stub.when(m).orderOn(eq(key), any()));
}
// Put the key which will wait on releasing the orderer at the end
Future<Object> operationFuture = fork(callable);
// Confirm everything is complete except releasing orderer
operationCheckPoint.awaitStrict(Mocks.BEFORE_INVOCATION, 10, TimeUnit.SECONDS);
// Replace the original so the eviction doesn't get blocked by the other check point
TestingUtil.replaceComponent(cache, DataOperationOrderer.class, original, true);
// We use this checkpoint to wait until the eviction is in process (that is that it has the caffeine lock
// and has to wait until the prior orderer above completes
CheckPoint evictionCheckPoint = new CheckPoint("eviction");
evictionCheckPoint.triggerForever(Mocks.BEFORE_RELEASE);
Mocks.blockingMock(evictionCheckPoint, DataOperationOrderer.class, cache, (stub, m) -> stub.when(m).orderOn(eq(key), any()));
// Put another key, which will evict our original key
Future<Object> evictFuture = fork(() -> cache.put("other-key", "other-value"));
// Now wait for the eviction to retrieve the orderer - but don't let it continue
evictionCheckPoint.awaitStrict(Mocks.AFTER_INVOCATION, 10, TimeUnit.SECONDS);
// Let the put complete
operationCheckPoint.trigger(blockOnCompletion ? Mocks.BEFORE_RELEASE : Mocks.AFTER_RELEASE);
// be preventing the actual operation from completing - thus we free the eviction sooner
if (!blockOnCompletion) {
evictionCheckPoint.triggerForever(Mocks.AFTER_RELEASE);
}
// And ensure the operation complete
valueConsumer.accept(operationFuture.get(10, TimeUnit.SECONDS));
// Finally let the eviction to complete if it wasn't above
evictionCheckPoint.triggerForever(Mocks.AFTER_RELEASE);
evictFuture.get(10, TimeUnit.SECONDS);
finalResultConsumer.accept(cache.get(key));
}
use of org.infinispan.util.concurrent.DataOperationOrderer in project infinispan by infinispan.
the class EvictionWithPassivationAndConcurrentOperationsTest method testWriteDuringEviction.
// This test differs from testEvictionDuringWrite in that it simulates an eviction and acquires the
// caffeine lock, but is unable to acquire the orderer as it is already taken by a write operation. In this case
// the eviction has removed the entry and the write puts it back - however the passivation should be skipped
public void testWriteDuringEviction() throws Exception {
String key = "evicted-key";
String initialValue = "value";
cache.put(key, initialValue);
// Use delayFuture1 to stop eviction from acquiring the orderer
// It blocks eviction from acquiring orderer - but has entry lock
DataOperationOrderer orderer = extractComponent(cache, DataOperationOrderer.class);
CompletableFuture<DataOperationOrderer.Operation> delayFuture1 = acquireOrderer(orderer, key, null);
log.tracef("delayFuture1=%s", delayFuture1.toString());
// This will be stuck evicting the key until it can get the orderer
Future<Object> putFuture = fork(() -> cache.put("other-key", "other-value"));
eventually(() -> orderer.getCurrentStage(key) != delayFuture1);
CompletionStage<DataOperationOrderer.Operation> putOtherKeyPassivationStage = orderer.getCurrentStage(key);
String newValue = "value-2";
Future<Object> evictedKeyPutFuture = fork(() -> cache.put(key, newValue));
// Should be blocked waiting on Caffeine lock - but has the orderer
TestingUtil.assertNotDone(evictedKeyPutFuture);
assertFalse(putOtherKeyPassivationStage.toCompletableFuture().isDone());
// Let the eviction finish, which will let the put happen
orderer.completeOperation(key, delayFuture1, DataOperationOrderer.Operation.READ);
putFuture.get(10, SECONDS);
assertEquals(initialValue, evictedKeyPutFuture.get(10, SECONDS));
assertInMemory(key, newValue);
PassivationPersistenceManager ppm = (PassivationPersistenceManager) extractComponent(cache, PersistenceManager.class);
eventuallyEquals(0, ppm::pendingPassivations);
assertEquals(2L, extractComponent(cache, PassivationManager.class).getPassivations());
}
use of org.infinispan.util.concurrent.DataOperationOrderer in project infinispan by infinispan.
the class EvictionWithPassivationAndConcurrentOperationsTest method testEvictionDuringWriteWithConcurrentRead.
public void testEvictionDuringWriteWithConcurrentRead() throws TimeoutException, InterruptedException, ExecutionException {
String key = "evicted-key";
String value = "value";
// Simulate a write orderer operation to acquire the write orderer for evicted-key
// Holding the orderer blocks prevents another passivation or activation of the same key
DataOperationOrderer orderer = extractComponent(cache, DataOperationOrderer.class);
CompletableFuture<DataOperationOrderer.Operation> delayFuture1 = acquireOrderer(orderer, key, null);
log.tracef("delayFuture1=%s", delayFuture1.toString());
// Put the key which will wait on releasing the orderer at the end
Future<Object> putEvictedKeyFuture = fork(() -> cache.getAdvancedCache().withFlags(Flag.SKIP_CACHE_LOAD).put(key, value));
// Confirm the entry has been inserted in the data container so we can evict
eventually(() -> orderer.getCurrentStage(key) != delayFuture1);
CompletionStage<DataOperationOrderer.Operation> putEvictedKeyActivationStage = orderer.getCurrentStage(key);
// Acquire the write orderer for evicted-key again after put(evicted-key) releases it
CompletableFuture<DataOperationOrderer.Operation> delayFuture2 = acquireOrderer(orderer, key, putEvictedKeyActivationStage);
log.tracef("delayFuture2=%s", delayFuture2.toString());
// Let put(evicted-key) acquire the orderer and activate evicted-key
orderer.completeOperation(key, delayFuture1, DataOperationOrderer.Operation.READ);
putEvictedKeyFuture.get(10, SECONDS);
assertTrue(putEvictedKeyActivationStage.toCompletableFuture().isDone());
// delayFuture2 blocks the eviction of evicted-key, but it does not prevent put(other-key) from finishing
cache.put("other-key", "other-value");
CompletionStage<DataOperationOrderer.Operation> putOtherKeyPassivationStage = orderer.getCurrentStage(key);
assertNotSame(delayFuture2, putOtherKeyPassivationStage);
// Acquire the write orderer for evicted-key again after put(other-key) releases it
CompletableFuture<DataOperationOrderer.Operation> delayFuture3 = acquireOrderer(orderer, key, putOtherKeyPassivationStage);
log.tracef("delayFuture3=%s", delayFuture3.toString());
// delayFuture2 is still holding evicted-key's orderer
// Start get(evicted-key); it cannot complete yet, but it does register a new orderer stage
Future<Object> getFuture = fork(() -> cache.get(key));
eventually(() -> orderer.getCurrentStage(key) != putOtherKeyPassivationStage);
CompletionStage<DataOperationOrderer.Operation> getEvictedKeyActivationStage = orderer.getCurrentStage(key);
assertFalse(getFuture.isDone());
// Complete delayFuture2 to release the orderer, it will be acquired by put(other-key)
orderer.completeOperation(key, delayFuture2, putEvictedKeyActivationStage.toCompletableFuture().join());
// get(evicted-key) can't finish yet because of delayFuture3
TestingUtil.assertNotDone(getFuture);
// Let get(evicted-key) acquire the orderer and finish the activation
orderer.completeOperation(key, delayFuture3, putOtherKeyPassivationStage.toCompletableFuture().join());
assertEquals(value, getFuture.get(10, SECONDS));
// Wait for the activation to finish
eventuallyEquals(null, () -> orderer.getCurrentStage(key));
assertTrue(getEvictedKeyActivationStage.toCompletableFuture().isDone());
// #1 evicted-key evicted by other-key from write
// #2 other-key evicted by evicted-key from the get
assertEquals(2L, extractComponent(cache, PassivationManager.class).getPassivations());
// #1 evicted key activated from the get
assertEquals(1L, extractComponent(cache, ActivationManager.class).getActivationCount());
assertEquals(0L, extractComponent(cache, ActivationManager.class).getPendingActivationCount());
}
Aggregations