use of io.pravega.segmentstore.storage.cache.CacheFullException in project pravega by pravega.
the class MemoryStateUpdater method addToReadIndex.
/**
* Registers the given operation in the ReadIndex.
*
* @param operation The operation to register.
* @throws CacheFullException If the operation could not be added to the {@link ReadIndex} due to the cache being
* full and unable to evict anything to make room for more.
* @throws ServiceHaltException If any unexpected exception occurred that prevented the operation from being
* added to the {@link ReadIndex}. Unexpected exceptions are all exceptions other than those declared in this
* method or that indicate we are shutting down or that the segment has been deleted.
*/
private void addToReadIndex(StorageOperation operation) throws ServiceHaltException, CacheFullException {
try {
if (operation instanceof StreamSegmentAppendOperation) {
// Record a StreamSegmentAppendOperation. Just in case, we also support this type of operation, but we need to
// log a warning indicating so. This means we do not optimize memory properly, and we end up storing data
// in two different places.
StreamSegmentAppendOperation appendOperation = (StreamSegmentAppendOperation) operation;
this.readIndex.append(appendOperation.getStreamSegmentId(), appendOperation.getStreamSegmentOffset(), appendOperation.getData());
} else if (operation instanceof MergeSegmentOperation) {
// Record a MergeSegmentOperation. We call beginMerge here, and the StorageWriter will call completeMerge.
MergeSegmentOperation mergeOperation = (MergeSegmentOperation) operation;
this.readIndex.beginMerge(mergeOperation.getStreamSegmentId(), mergeOperation.getStreamSegmentOffset(), mergeOperation.getSourceSegmentId());
} else {
assert !(operation instanceof CachedStreamSegmentAppendOperation) : "attempted to add a CachedStreamSegmentAppendOperation to the ReadIndex";
}
} catch (ObjectClosedException | StreamSegmentNotExistsException ex) {
// The Segment is in the process of being deleted. We usually end up in here because a concurrent delete
// request has updated the metadata while we were executing.
log.warn("Not adding operation '{}' to ReadIndex because it refers to a deleted StreamSegment.", operation);
} catch (CacheFullException ex) {
// Record the operation that we couldn't add and re-throw the exception as we cannot do anything about it here.
log.warn("Not adding operation '{}' to ReadIndex because the Cache is full.", operation);
throw ex;
} catch (Exception ex) {
throw new ServiceHaltException(String.format("Unable to add operation '%s' to ReadIndex.", operation), ex);
}
}
use of io.pravega.segmentstore.storage.cache.CacheFullException in project pravega by pravega.
the class AttributeIndexTests method testCacheWriteFailure.
/**
* Tests the case when updates/reads fail due to a Cache failure (i.e., Cache Full). This should not interfere with
* whatever operation triggered it and it should not affect ongoing operations on the index.
*/
@Test
public void testCacheWriteFailure() throws Exception {
val exceptionMakers = Arrays.<Supplier<RuntimeException>>asList(IntentionalException::new, () -> new CacheFullException("intentional"));
val attributeId = AttributeId.randomUUID();
val attributeSegmentName = NameUtils.getAttributeSegmentName(SEGMENT_NAME);
val initialValue = 0L;
val finalValue = 1L;
for (val exceptionMaker : exceptionMakers) {
@Cleanup val context = new TestContext(DEFAULT_CONFIG);
populateSegments(context);
val idx = context.index.forSegment(SEGMENT_ID, TIMEOUT).join();
// Populate the index with some initial value.
val pointer1 = idx.update(Collections.singletonMap(attributeId, initialValue), TIMEOUT).join();
// For the next insertion, simulate some cache exception.
val insertInvoked = new AtomicBoolean(false);
context.cacheStorage.beforeInsert = buffer -> {
insertInvoked.set(true);
throw exceptionMaker.get();
};
// Update the value. The cache insertion should fail because of the exception above.
val pointer2 = idx.update(Collections.singletonMap(attributeId, finalValue), TIMEOUT).join();
TestUtils.await(() -> context.storage.getStreamSegmentInfo(attributeSegmentName, TIMEOUT).join().getStartOffset() > pointer1, 5, TIMEOUT.toMillis());
Assert.assertTrue(insertInvoked.get());
AssertExtensions.assertGreaterThan("", pointer1, pointer2);
// If the cache insertion would have actually failed, then the following read call would fail (it would try
// to read from the truncated portion (that's why we have the asserts above).
// First, setup a read interceptor to ensure we actually want to read from Storage.
val readCount = new AtomicInteger(0);
context.storage.readInterceptor = (segment, offset, length, wrappedStorage) -> {
readCount.incrementAndGet();
return CompletableFuture.completedFuture(null);
};
val value = idx.get(Collections.singletonList(attributeId), TIMEOUT).join();
Assert.assertEquals(finalValue, (long) value.get(attributeId));
AssertExtensions.assertGreaterThan("Expected Storage reads.", 0, readCount.get());
}
}
use of io.pravega.segmentstore.storage.cache.CacheFullException in project pravega by pravega.
the class ContainerKeyCacheTests method testCacheUpdateFailure.
/**
* Test a case when the cache storage throws errors while attempting to update.
*/
@Test
public void testCacheUpdateFailure() {
val segmentId = 0;
val offset1 = 0L;
val offset2 = 1L;
val spiedStorage = Mockito.spy(this.cacheStorage);
@Cleanup val keyCache = new ContainerKeyCache(spiedStorage);
val expectedResult = new HashMap<TestKey, CacheBucketOffset>();
val keyHash = newSimpleHash();
// Perform an initial insert and verify it.
keyCache.updateSegmentIndexOffset(segmentId, offset1);
val updateResult1 = keyCache.includeExistingKey(segmentId, keyHash, offset1);
Assert.assertEquals("Unexpected result from includeExistingKey() for new insertion.", offset1, updateResult1);
expectedResult.put(new TestKey(segmentId, keyHash), new CacheBucketOffset(offset1, false));
checkCache(expectedResult, keyCache);
// For the second insert, fail the cache update and verify that the whole entry has been evicted.
val replaceAddress = new AtomicInteger(-1);
Mockito.doAnswer(arg1 -> {
replaceAddress.set(arg1.getArgument(0));
throw new CacheFullException("cache full");
}).when(spiedStorage).replace(Mockito.anyInt(), Mockito.any());
val deleteAddress = new AtomicInteger(-1);
Mockito.doAnswer(arg1 -> {
deleteAddress.set(arg1.getArgument(0));
return arg1.callRealMethod();
}).when(spiedStorage).delete(Mockito.anyInt());
val updateResult2 = keyCache.includeExistingKey(segmentId, keyHash, offset2);
Assert.assertEquals("Unexpected result from includeExistingKey() for new insertion.", offset2, updateResult2);
// Set the expected value to null - that indicates it shouldn't be in the cache.
expectedResult.put(new TestKey(segmentId, keyHash), null);
checkCache(expectedResult, keyCache);
Assert.assertNotEquals("Replacement was not attempted.", -1, replaceAddress.get());
Assert.assertEquals("Deletion was for wrong entry.", replaceAddress.get(), deleteAddress.get());
}
Aggregations