use of io.pravega.client.state.Revision in project pravega by pravega.
the class SynchronizerTest method testConcurrentFetchUpdatesAfterTruncation.
@Test(timeout = 20000)
@SuppressWarnings("unchecked")
public void testConcurrentFetchUpdatesAfterTruncation() {
String streamName = "streamName";
String scope = "scope";
// Mock of the RevisionedStreamClient.
RevisionedStreamClient<UpdateOrInit<RevisionedImpl>> revisionedStreamClient = mock(RevisionedStreamClient.class);
final Segment segment = new Segment(scope, streamName, 0L);
@Cleanup StateSynchronizerImpl<RevisionedImpl> syncA = new StateSynchronizerImpl<>(segment, revisionedStreamClient);
Revision firstMark = new RevisionImpl(segment, 10L, 1);
Revision secondMark = new RevisionImpl(segment, 20L, 2);
final AbstractMap.SimpleImmutableEntry<Revision, UpdateOrInit<RevisionedImpl>> entry = new AbstractMap.SimpleImmutableEntry<>(secondMark, new UpdateOrInit<>(new RegularUpdate("x")));
// Mock iterators to simulate concurrent revisionedStreamClient.readFrom(firstMark) call.
Iterator<Entry<Revision, UpdateOrInit<RevisionedImpl>>> iterator1 = Collections.<Entry<Revision, UpdateOrInit<RevisionedImpl>>>singletonList(entry).iterator();
Iterator<Entry<Revision, UpdateOrInit<RevisionedImpl>>> iterator2 = Collections.<Entry<Revision, UpdateOrInit<RevisionedImpl>>>singletonList(entry).iterator();
// Latch to ensure both the thread encounter truncation exception.
CountDownLatch truncationLatch = new CountDownLatch(2);
// Latch to ensure both the threads invoke read attempt reading from same revision.
// This will simulate the race condition where the in-memory state is newer than the state returned by RevisionedStreamClient.
CountDownLatch raceLatch = new CountDownLatch(2);
// Setup Mock
when(revisionedStreamClient.getMark()).thenReturn(firstMark);
when(revisionedStreamClient.readFrom(firstMark)).thenAnswer(invocation -> {
truncationLatch.countDown();
// wait until the other thread encounters the TruncationDataException.
truncationLatch.await();
throw new TruncatedDataException();
}).thenAnswer(invocation -> {
throw new TruncatedDataException();
}).thenAnswer(invocation -> {
truncationLatch.countDown();
// wait until the other thread attempts to fetch updates from SSS post truncation and updates internal state.
raceLatch.await();
return iterator1;
}).thenAnswer(invocation -> {
raceLatch.countDown();
return iterator2;
});
// Return an iterator whose hasNext is false.
when(revisionedStreamClient.readFrom(secondMark)).thenAnswer(invocation -> {
// release the waiting thread which is fetching updates from SSS when the internal state is already updated.
raceLatch.countDown();
return iterator2;
});
// Simulate concurrent invocations of fetchUpdates API.
@Cleanup("shutdownNow") ScheduledExecutorService exec = ExecutorServiceHelpers.newScheduledThreadPool(2, "test-pool");
CompletableFuture<Void> cf1 = CompletableFuture.supplyAsync(() -> {
syncA.fetchUpdates();
return null;
}, exec);
CompletableFuture<Void> cf2 = CompletableFuture.supplyAsync(() -> {
syncA.fetchUpdates();
return null;
}, exec);
// Wait until the completion of both the fetchUpdates() API.
CompletableFuture.allOf(cf1, cf2).join();
assertEquals("x", syncA.getState().getValue());
}
use of io.pravega.client.state.Revision in project pravega by pravega.
the class StateSynchronizerImpl method fetchUpdates.
@Override
public void fetchUpdates() {
Revision revision = getRevisionToReadFrom(true);
log.trace("Fetching updates after {} ", revision);
try {
val iter = client.readFrom(revision);
while (iter.hasNext()) {
Entry<Revision, UpdateOrInit<StateT>> entry = iter.next();
log.trace("Found entry {} ", entry.getValue());
if (entry.getValue().isInit()) {
InitialUpdate<StateT> init = entry.getValue().getInit();
updateCurrentState(init.create(segment.getScopedStreamName(), entry.getKey()));
} else {
applyUpdates(entry.getKey().asImpl(), entry.getValue().getUpdates());
}
}
} catch (TruncatedDataException e) {
log.info("{} encountered truncation on segment {}, Details: {}", this, segment, e.getMessage());
RETRY_INDEFINITELY.retryingOn(TruncatedDataException.class).throwingOn(RuntimeException.class).run(() -> handleTruncation());
}
}
use of io.pravega.client.state.Revision in project pravega by pravega.
the class StateSynchronizerImpl method compact.
@Override
public void compact(Function<StateT, InitialUpdate<StateT>> compactor) {
AtomicReference<Revision> compactedVersion = new AtomicReference<Revision>(null);
conditionallyWrite(state -> {
InitialUpdate<StateT> init = compactor.apply(state);
if (init == null) {
compactedVersion.set(null);
return null;
} else {
compactedVersion.set(state.getRevision());
return new UpdateOrInit<>(init);
}
});
Revision newMark = compactedVersion.get();
if (newMark != null) {
Revision oldMark = client.getMark();
if (oldMark == null || oldMark.compareTo(newMark) < 0) {
client.compareAndSetMark(oldMark, newMark);
log.info("Compacted state is written at {} the oldMark is {}", newMark, oldMark);
}
if (oldMark != null) {
client.truncateToRevision(oldMark);
}
}
}
use of io.pravega.client.state.Revision in project pravega by pravega.
the class StateSynchronizerImpl method handleTruncation.
private Void handleTruncation() {
Revision revision = getRevisionToReadFrom(false);
log.info("{} encountered truncation, attempting to read from revision {}", this, revision);
boolean foundInit = false;
val iter = client.readFrom(revision);
Revision currentRevision = null;
while (!foundInit && iter.hasNext()) {
Entry<Revision, UpdateOrInit<StateT>> entry = iter.next();
currentRevision = entry.getKey();
if (entry.getValue().isInit()) {
log.trace("Found entry {} ", entry.getValue());
InitialUpdate<StateT> init = entry.getValue().getInit();
foundInit = true;
updateCurrentState(init.create(segment.getScopedStreamName(), currentRevision));
}
}
if (!foundInit) {
throw new IllegalStateException(format("Data was truncated but there is no Init state after the truncation point. Last read revision is %s", currentRevision));
}
fetchUpdates();
return null;
}
use of io.pravega.client.state.Revision in project pravega by pravega.
the class SerializationTest method testUpdateDistanceToTail.
@Test
@SuppressWarnings("unchecked")
public void testUpdateDistanceToTail() throws Exception {
UpdateDistanceToTailSerializer serializer = new UpdateDistanceToTail.UpdateDistanceToTailSerializer();
UpdateDistanceToTail update = new UpdateDistanceToTail(createString(), r.nextLong(), createSegmentRangeMap());
UpdateDistanceToTail deserialized = serializer.deserialize(serializer.serialize(update));
assertEquals(deserialized, update);
UpdateDistanceToTailSerializer oldSerializer = new UpdateDistanceToTailSerializer() {
@Override
protected void declareVersions() {
version(0).revision(0, this::write00, this::read00);
}
};
UpdateDistanceToTail oldStyleUpdate = serializer.deserialize(oldSerializer.serialize(update));
assertEquals(update.getReaderId(), oldStyleUpdate.getReaderId());
assertEquals(update.getDistanceToTail(), oldStyleUpdate.getDistanceToTail());
assertEquals(null, oldStyleUpdate.getLastReadPositions());
// Change the state to reflect the update
val segmentToOffsets = ImmutableMap.of(new SegmentWithRange(new Segment("scope", "stream", 0), 0.0, 1.0), 0L);
ReaderGroupState state = new ReaderGroupState("_RGTest", mock(Revision.class), mock(ReaderGroupConfig.class), segmentToOffsets, mock(Map.class), false);
// ensure no exceptions are thrown.
oldStyleUpdate.update(state);
}
Aggregations