use of io.pravega.shared.watermarks.Watermark in project pravega by pravega.
the class WatermarkingTest method watermarkingTests.
@Test
public void watermarkingTests() throws Exception {
final ClientConfig clientConfig = Utils.buildClientConfig(controllerURI);
@Cleanup ConnectionFactory connectionFactory = new SocketConnectionFactoryImpl(clientConfig);
ControllerImpl controller = new ControllerImpl(ControllerImplConfig.builder().clientConfig(clientConfig).build(), connectionFactory.getInternalExecutor());
// create 2 writers
@Cleanup EventStreamClientFactory clientFactory = EventStreamClientFactory.withScope(SCOPE, clientConfig);
JavaSerializer<Long> javaSerializer = new JavaSerializer<>();
@Cleanup EventStreamWriter<Long> writer1 = clientFactory.createEventWriter(STREAM, javaSerializer, EventWriterConfig.builder().build());
@Cleanup EventStreamWriter<Long> writer2 = clientFactory.createEventWriter(STREAM, javaSerializer, EventWriterConfig.builder().build());
AtomicBoolean stopFlag = new AtomicBoolean(false);
// write events
writeEvents(writer1, stopFlag);
writeEvents(writer2, stopFlag);
// scale the stream several times so that we get complex positions
Stream streamObj = Stream.of(SCOPE, STREAM);
scale(controller, streamObj);
@Cleanup ClientFactoryImpl syncClientFactory = new ClientFactoryImpl(SCOPE, new ControllerImpl(ControllerImplConfig.builder().clientConfig(clientConfig).build(), connectionFactory.getInternalExecutor()), connectionFactory);
String markStream = NameUtils.getMarkStreamForStream(STREAM);
RevisionedStreamClient<Watermark> watermarkReader = syncClientFactory.createRevisionedStreamClient(markStream, new WatermarkSerializer(), SynchronizerConfig.builder().build());
LinkedBlockingQueue<Watermark> watermarks = new LinkedBlockingQueue<>();
fetchWatermarks(watermarkReader, watermarks, stopFlag);
AssertExtensions.assertEventuallyEquals(true, () -> watermarks.size() >= 2, 100000);
// scale down one controller instance.
Futures.getAndHandleExceptions(controllerInstance.scaleService(1), ExecutionException::new);
// wait until at least 2 more watermarks are emitted
AssertExtensions.assertEventuallyEquals(true, () -> watermarks.size() >= 4, 100000);
stopFlag.set(true);
Watermark watermark0 = watermarks.take();
Watermark watermark1 = watermarks.take();
Watermark watermark2 = watermarks.take();
Watermark watermark3 = watermarks.take();
assertTrue(watermark0.getLowerTimeBound() <= watermark0.getUpperTimeBound());
assertTrue(watermark1.getLowerTimeBound() <= watermark1.getUpperTimeBound());
assertTrue(watermark2.getLowerTimeBound() <= watermark2.getUpperTimeBound());
assertTrue(watermark3.getLowerTimeBound() <= watermark3.getUpperTimeBound());
// verify that watermarks are increasing in time.
assertTrue(watermark0.getLowerTimeBound() < watermark1.getLowerTimeBound());
assertTrue(watermark1.getLowerTimeBound() < watermark2.getLowerTimeBound());
assertTrue(watermark2.getLowerTimeBound() < watermark3.getLowerTimeBound());
// use watermark as lower and upper bounds.
Map<Segment, Long> positionMap0 = watermark0.getStreamCut().entrySet().stream().collect(Collectors.toMap(x -> new Segment(SCOPE, STREAM, x.getKey().getSegmentId()), Map.Entry::getValue));
StreamCut streamCutStart = new StreamCutImpl(streamObj, positionMap0);
Map<Stream, StreamCut> start = Collections.singletonMap(streamObj, streamCutStart);
Map<Segment, Long> positionMap2 = watermark2.getStreamCut().entrySet().stream().collect(Collectors.toMap(x -> new Segment(SCOPE, STREAM, x.getKey().getSegmentId()), Map.Entry::getValue));
StreamCut streamCutEnd = new StreamCutImpl(streamObj, positionMap2);
Map<Stream, StreamCut> end = Collections.singletonMap(streamObj, streamCutEnd);
@Cleanup ReaderGroupManager readerGroupManager = new ReaderGroupManagerImpl(SCOPE, controller, syncClientFactory);
String readerGroup = "rg";
readerGroupManager.createReaderGroup(readerGroup, ReaderGroupConfig.builder().stream(streamObj).startingStreamCuts(start).endingStreamCuts(end).build());
// create reader on the stream
@Cleanup final EventStreamReader<Long> reader = clientFactory.createReader("myreader", readerGroup, javaSerializer, ReaderConfig.builder().build());
// read events from the reader.
// verify that events read belong to the bound
EventRead<Long> event = reader.readNextEvent(10000L);
AtomicReference<TimeWindow> currentTimeWindow = new AtomicReference<>();
AssertExtensions.assertEventuallyEquals(true, () -> {
currentTimeWindow.set(reader.getCurrentTimeWindow(streamObj));
return currentTimeWindow.get() != null && currentTimeWindow.get().getLowerTimeBound() != null && currentTimeWindow.get().getUpperTimeBound() != null;
}, 100000);
log.info("current time window = {}", currentTimeWindow.get());
while (event.getEvent() != null) {
Long time = event.getEvent();
log.info("event read = {}", time);
event.getPosition();
assertTrue(time >= currentTimeWindow.get().getLowerTimeBound());
event = reader.readNextEvent(10000L);
if (event.isCheckpoint()) {
event = reader.readNextEvent(10000L);
}
}
}
use of io.pravega.shared.watermarks.Watermark in project pravega by pravega.
the class EventStreamReaderTest method testTimeWindow.
@Test
public void testTimeWindow() throws SegmentSealedException {
String scope = "scope";
String streamName = "stream";
Stream stream = Stream.of(scope, streamName);
String groupName = "readerGroup";
String readerGroupStream = NameUtils.getStreamForReaderGroup(groupName);
String markStream = NameUtils.getMarkStreamForStream(streamName);
// Create factories
MockSegmentStreamFactory segmentStreamFactory = new MockSegmentStreamFactory();
@Cleanup MockClientFactory clientFactory = new MockClientFactory(scope, segmentStreamFactory);
MockController controller = (MockController) clientFactory.getController();
@Cleanup InlineExecutor executor = new InlineExecutor();
// Create streams
controller.createScope(scope).join();
controller.createStream(scope, streamName, StreamConfiguration.builder().scalingPolicy(ScalingPolicy.fixed(2)).build());
controller.createStream(scope, readerGroupStream, StreamConfiguration.builder().scalingPolicy(ScalingPolicy.fixed(1)).build());
// Reader group state synchronizer
ReaderGroupConfig config = ReaderGroupConfig.builder().disableAutomaticCheckpoints().stream(stream).build();
StateSynchronizer<ReaderGroupState> sync = clientFactory.createStateSynchronizer(readerGroupStream, new ReaderGroupStateUpdatesSerializer(), new ReaderGroupStateInitSerializer(), SynchronizerConfig.builder().build());
// Watermark reader/writer
@Cleanup RevisionedStreamClient<Watermark> markWriter = clientFactory.createRevisionedStreamClient(markStream, new WatermarkSerializer(), SynchronizerConfig.builder().build());
@Cleanup WatermarkReaderImpl markReader = new WatermarkReaderImpl(stream, markWriter, executor);
// Initialize reader group state
Map<SegmentWithRange, Long> segments = ReaderGroupImpl.getSegmentsForStreams(controller, config);
sync.initialize(new ReaderGroupState.ReaderGroupStateInit(config, segments, getEndSegmentsForStreams(config), false));
// Data segment writers
Segment segment1 = new Segment(scope, streamName, 0);
Segment segment2 = new Segment(scope, streamName, 1);
@Cleanup SegmentOutputStream stream1 = segmentStreamFactory.createOutputStreamForSegment(segment1, segmentSealedCallback, writerConfig, DelegationTokenProviderFactory.createWithEmptyToken());
@Cleanup SegmentOutputStream stream2 = segmentStreamFactory.createOutputStreamForSegment(segment2, segmentSealedCallback, writerConfig, DelegationTokenProviderFactory.createWithEmptyToken());
// Write stream data
writeInt(stream1, 1);
writeInt(stream2, 2);
writeInt(stream2, 2);
writeInt(stream2, 2);
// Write mark data
val r1 = new SegmentWithRange(segment1, 0, 0.5).convert();
val r2 = new SegmentWithRange(segment2, 0.5, 1).convert();
markWriter.writeUnconditionally(new Watermark(0L, 99L, ImmutableMap.of(r1, 0L, r2, 0L)));
markWriter.writeUnconditionally(new Watermark(100L, 199L, ImmutableMap.of(r1, 12L, r2, 0L)));
markWriter.writeUnconditionally(new Watermark(200L, 299L, ImmutableMap.of(r1, 12L, r2, 12L)));
markWriter.writeUnconditionally(new Watermark(300L, 399L, ImmutableMap.of(r1, 12L, r2, 24L)));
markWriter.writeUnconditionally(new Watermark(400L, 499L, ImmutableMap.of(r1, 12L, r2, 36L)));
// Create reader
AtomicLong clock = new AtomicLong();
ReaderGroupStateManager groupState = new ReaderGroupStateManager(scope, groupName, "reader1", sync, controller, clock::get);
groupState.initializeReader(0);
@Cleanup EventStreamReaderImpl<byte[]> reader = new EventStreamReaderImpl<>(segmentStreamFactory, segmentStreamFactory, new ByteArraySerializer(), groupState, new Orderer(), clock::get, ReaderConfig.builder().build(), ImmutableMap.of(stream, markReader), Mockito.mock(Controller.class));
clock.addAndGet(ReaderGroupStateManager.UPDATE_WINDOW.toNanos());
EventRead<byte[]> event = reader.readNextEvent(100);
assertEquals(2, readInt(event));
TimeWindow timeWindow = reader.getCurrentTimeWindow(Stream.of(scope, streamName));
assertEquals(0, timeWindow.getLowerTimeBound().longValue());
assertEquals(199, timeWindow.getUpperTimeBound().longValue());
clock.addAndGet(ReaderGroupStateManager.UPDATE_WINDOW.toNanos());
event = reader.readNextEvent(100);
assertEquals(1, readInt(event));
timeWindow = reader.getCurrentTimeWindow(Stream.of(scope, streamName));
assertEquals(0, timeWindow.getLowerTimeBound().longValue());
assertEquals(299, timeWindow.getUpperTimeBound().longValue());
clock.addAndGet(ReaderGroupStateManager.UPDATE_WINDOW.toNanos());
event = reader.readNextEvent(100);
assertEquals(2, readInt(event));
timeWindow = reader.getCurrentTimeWindow(Stream.of(scope, streamName));
assertEquals(200, timeWindow.getLowerTimeBound().longValue());
assertEquals(399, timeWindow.getUpperTimeBound().longValue());
clock.addAndGet(ReaderGroupStateManager.UPDATE_WINDOW.toNanos());
event = reader.readNextEvent(100);
assertEquals(2, readInt(event));
timeWindow = reader.getCurrentTimeWindow(Stream.of(scope, streamName));
assertEquals(300, timeWindow.getLowerTimeBound().longValue());
assertEquals(499, timeWindow.getUpperTimeBound().longValue());
clock.addAndGet(ReaderGroupStateManager.UPDATE_WINDOW.toNanos());
event = reader.readNextEvent(100);
assertEquals(null, event.getEvent());
timeWindow = reader.getCurrentTimeWindow(Stream.of(scope, streamName));
assertEquals(400, timeWindow.getLowerTimeBound().longValue());
assertEquals(null, timeWindow.getUpperTimeBound());
}
use of io.pravega.shared.watermarks.Watermark in project pravega by pravega.
the class WatermarkWorkflowTest method testRevisionedClientThrowsNoSuchSegmentException.
@Test(timeout = 30000L)
public void testRevisionedClientThrowsNoSuchSegmentException() {
String scope = "scope";
String streamName = "stream";
StreamImpl stream = new StreamImpl(scope, streamName);
String markStreamName = NameUtils.getMarkStreamForStream(streamName);
SynchronizerClientFactory clientFactory = spy(SynchronizerClientFactory.class);
ConcurrentHashMap<String, MockRevisionedStreamClient> revisionedStreamClientMap = new ConcurrentHashMap<>();
doAnswer(x -> {
String name = x.getArgument(0);
return revisionedStreamClientMap.compute(name, (s, rsc) -> new MockRevisionedStreamClient(() -> streamMetadataStore.getActiveSegments(scope, name, null, executor).join().get(0).segmentId()));
}).when(clientFactory).createRevisionedStreamClient(anyString(), any(), any());
@Cleanup PeriodicWatermarking periodicWatermarking = new PeriodicWatermarking(streamMetadataStore, bucketStore, sp -> clientFactory, executor, new RequestTracker(false));
streamMetadataStore.createScope(scope, null, executor).join();
streamMetadataStore.createStream(scope, streamName, StreamConfiguration.builder().scalingPolicy(ScalingPolicy.fixed(2)).timestampAggregationTimeout(10000L).build(), System.currentTimeMillis(), null, executor).join();
streamMetadataStore.createStream(scope, markStreamName, StreamConfiguration.builder().scalingPolicy(ScalingPolicy.fixed(1)).build(), System.currentTimeMillis(), null, executor).join();
streamMetadataStore.setState(scope, markStreamName, State.ACTIVE, null, executor).join();
streamMetadataStore.setState(scope, streamName, State.ACTIVE, null, executor).join();
// 1. note writer1 marks
// writer 1 reports segments 0, 1.
String writer1 = "writer1";
Map<Long, Long> map1 = ImmutableMap.of(0L, 100L, 1L, 100L);
streamMetadataStore.noteWriterMark(scope, streamName, writer1, 100L, map1, null, executor).join();
// 2. run watermarking workflow.
periodicWatermarking.watermark(stream).join();
assertTrue(periodicWatermarking.checkExistsInCache(stream));
// verify that a watermark has been emitted.
MockRevisionedStreamClient revisionedClient = revisionedStreamClientMap.get(markStreamName);
assertEquals(revisionedClient.watermarks.size(), 1);
Watermark watermark = revisionedClient.watermarks.get(0).getValue();
assertEquals(watermark.getLowerTimeBound(), 100L);
// delete and recreate stream and its mark stream
streamMetadataStore.deleteStream(scope, markStreamName, null, executor).join();
streamMetadataStore.deleteStream(scope, streamName, null, executor).join();
streamMetadataStore.createStream(scope, streamName, StreamConfiguration.builder().scalingPolicy(ScalingPolicy.fixed(2)).timestampAggregationTimeout(10000L).build(), System.currentTimeMillis(), null, executor).join();
streamMetadataStore.setState(scope, streamName, State.ACTIVE, null, executor).join();
streamMetadataStore.createStream(scope, markStreamName, StreamConfiguration.builder().scalingPolicy(ScalingPolicy.fixed(1)).build(), System.currentTimeMillis(), null, executor).join();
streamMetadataStore.setState(scope, markStreamName, State.ACTIVE, null, executor).join();
// 1. note writer1 marks
// writer 1 reports segments 0, 1.
map1 = ImmutableMap.of(2L, 10L, 3L, 10L);
streamMetadataStore.noteWriterMark(scope, streamName, writer1, 10L, map1, null, executor).join();
// 2. run watermarking workflow. this should fail and revisioned stream client should be invalidated in the cache.
periodicWatermarking.watermark(stream).join();
assertFalse(periodicWatermarking.checkExistsInCache(stream));
// 3. run watermarking workflow again.
periodicWatermarking.watermark(stream).join();
assertTrue(periodicWatermarking.checkExistsInCache(stream));
// verify that a watermark has been emitted.
revisionedClient = revisionedStreamClientMap.get(markStreamName);
assertEquals(revisionedClient.segment, 1L);
assertEquals(revisionedClient.watermarks.size(), 1);
watermark = revisionedClient.watermarks.get(0).getValue();
assertEquals(watermark.getLowerTimeBound(), 10L);
}
use of io.pravega.shared.watermarks.Watermark in project pravega by pravega.
the class WatermarkWorkflowTest method testWriterTimeout.
@Test(timeout = 30000L)
public void testWriterTimeout() {
SynchronizerClientFactory clientFactory = spy(SynchronizerClientFactory.class);
ConcurrentHashMap<String, MockRevisionedStreamClient> revisionedStreamClientMap = new ConcurrentHashMap<>();
doAnswer(x -> {
String streamName = x.getArgument(0);
return revisionedStreamClientMap.compute(streamName, (s, rsc) -> {
if (rsc != null) {
return rsc;
} else {
return new MockRevisionedStreamClient();
}
});
}).when(clientFactory).createRevisionedStreamClient(anyString(), any(), any());
StreamMetadataStore streamMetadataStoreSpied = spy(this.streamMetadataStore);
BucketStore bucketStoreSpied = spy(this.bucketStore);
@Cleanup PeriodicWatermarking periodicWatermarking = new PeriodicWatermarking(streamMetadataStoreSpied, bucketStoreSpied, sp -> clientFactory, executor, new RequestTracker(false));
String streamName = "stream";
String scope = "scope";
streamMetadataStoreSpied.createScope(scope, null, executor).join();
streamMetadataStoreSpied.createStream(scope, streamName, StreamConfiguration.builder().scalingPolicy(ScalingPolicy.fixed(3)).timestampAggregationTimeout(3000L).build(), System.currentTimeMillis(), null, executor).join();
streamMetadataStoreSpied.setState(scope, streamName, State.ACTIVE, null, executor).join();
// 2. note writer1, writer2, writer3 marks
// writer 1 reports segments 0, 1.
// writer 2 reports segments 1, 2,
// writer 3 reports segment 0, 2
String writer1 = "writer1";
streamMetadataStoreSpied.noteWriterMark(scope, streamName, writer1, 102L, ImmutableMap.of(0L, 100L, 1L, 0L, 2L, 0L), null, executor).join();
String writer2 = "writer2";
streamMetadataStoreSpied.noteWriterMark(scope, streamName, writer2, 101L, ImmutableMap.of(0L, 0L, 1L, 100L, 2L, 0L), null, executor).join();
String writer3 = "writer3";
streamMetadataStoreSpied.noteWriterMark(scope, streamName, writer3, 100L, ImmutableMap.of(0L, 0L, 1L, 0L, 2L, 100L), null, executor).join();
// 3. run watermarking workflow.
StreamImpl stream = new StreamImpl(scope, streamName);
periodicWatermarking.watermark(stream).join();
// verify that a watermark has been emitted.
MockRevisionedStreamClient revisionedClient = revisionedStreamClientMap.get(NameUtils.getMarkStreamForStream(streamName));
assertEquals(revisionedClient.watermarks.size(), 1);
// Don't report time from writer3
streamMetadataStoreSpied.noteWriterMark(scope, streamName, writer1, 200L, ImmutableMap.of(0L, 200L, 1L, 0L, 2L, 0L), null, executor).join();
streamMetadataStoreSpied.noteWriterMark(scope, streamName, writer2, 200L, ImmutableMap.of(0L, 0L, 1L, 200L, 2L, 0L), null, executor).join();
// no new watermark should be emitted, writers should be tracked for inactivity
periodicWatermarking.watermark(stream).join();
assertEquals(revisionedClient.watermarks.size(), 1);
verify(streamMetadataStoreSpied, never()).removeWriter(anyString(), anyString(), anyString(), any(), any(), any());
verify(bucketStoreSpied, never()).removeStreamFromBucketStore(any(), anyString(), anyString(), any());
// call again. Still no new watermark should be emitted as writers have not timed out
periodicWatermarking.watermark(stream).join();
assertEquals(revisionedClient.watermarks.size(), 1);
verify(streamMetadataStoreSpied, never()).removeWriter(anyString(), anyString(), anyString(), any(), any(), any());
verify(bucketStoreSpied, never()).removeStreamFromBucketStore(any(), anyString(), anyString(), any());
// call watermark after a delay of 5 more seconds. The writer3 should timeout because it has a timeout of 3 seconds.
Futures.delayedFuture(() -> periodicWatermarking.watermark(stream), 5000L, executor).join();
verify(streamMetadataStoreSpied, times(1)).removeWriter(anyString(), anyString(), anyString(), any(), any(), any());
verify(bucketStoreSpied, never()).removeStreamFromBucketStore(any(), anyString(), anyString(), any());
// watermark should be emitted. without considering writer3
assertEquals(revisionedClient.watermarks.size(), 2);
Watermark watermark = revisionedClient.watermarks.get(1).getValue();
assertEquals(watermark.getLowerTimeBound(), 200L);
assertEquals(watermark.getStreamCut().size(), 3);
assertEquals(getSegmentOffset(watermark, 0L), 200L);
assertEquals(getSegmentOffset(watermark, 1L), 200L);
assertEquals(getSegmentOffset(watermark, 2L), 100L);
// call watermark workflow again so that both writers are tracked for inactivity
periodicWatermarking.watermark(stream).join();
assertEquals(revisionedClient.watermarks.size(), 2);
verify(streamMetadataStoreSpied, times(1)).removeWriter(anyString(), anyString(), anyString(), any(), any(), any());
verify(bucketStoreSpied, never()).removeStreamFromBucketStore(any(), anyString(), anyString(), any());
// now introduce more delays and see all writers are removed and stream is discontinued from watermarking computation.
Futures.delayedFuture(() -> periodicWatermarking.watermark(stream), 5000L, executor).join();
// verify that stream is discontinued from tracking for watermarking
verify(streamMetadataStoreSpied, times(3)).removeWriter(anyString(), anyString(), anyString(), any(), any(), any());
verify(bucketStoreSpied, times(1)).removeStreamFromBucketStore(any(), anyString(), anyString(), any());
// call note time for writer3 and verify that watermark is emitted.
streamMetadataStoreSpied.noteWriterMark(scope, streamName, writer3, 300L, ImmutableMap.of(0L, 300L, 1L, 0L, 2L, 0L), null, executor).join();
periodicWatermarking.watermark(stream).join();
assertEquals(revisionedClient.watermarks.size(), 3);
watermark = revisionedClient.watermarks.get(2).getValue();
assertEquals(watermark.getLowerTimeBound(), 300L);
assertEquals(watermark.getStreamCut().size(), 3);
assertEquals(getSegmentOffset(watermark, 0L), 300L);
assertEquals(getSegmentOffset(watermark, 1L), 200L);
assertEquals(getSegmentOffset(watermark, 2L), 100L);
}
use of io.pravega.shared.watermarks.Watermark in project pravega by pravega.
the class PeriodicWatermarking method watermark.
/**
* This method computes and emits a new watermark for the given stream.
* It collects all the known writers for the given stream and includes only writers that are active (have reported
* their marks recently). If all active writers have reported marks greater than the previously emitted watermark,
* then new watermark is computed and emitted. If not, the window for considering writers as active is progressed.
* @param stream stream for which watermark should be computed.
* @return Returns a completableFuture which when completed will have completed another iteration of periodic watermark
* computation.
*/
public CompletableFuture<Void> watermark(Stream stream) {
String scope = stream.getScope();
String streamName = stream.getStreamName();
long requestId = requestIdGenerator.get();
String requestDescriptor = RequestTracker.buildRequestDescriptor("watermark", stream.getScope(), stream.getStreamName());
requestTracker.trackRequest(requestDescriptor, requestId);
OperationContext context = streamMetadataStore.createStreamContext(scope, streamName, requestId);
if (scope.equals(NameUtils.INTERNAL_SCOPE_NAME)) {
return CompletableFuture.completedFuture(null);
}
log.debug(requestId, "Periodic background processing for watermarking called for stream {}/{}", scope, streamName);
CompletableFuture<Map<String, WriterMark>> allWriterMarks = Futures.exceptionallyExpecting(streamMetadataStore.getAllWriterMarks(scope, streamName, context, executor), e -> Exceptions.unwrap(e) instanceof StoreException.DataNotFoundException, Collections.emptyMap());
return allWriterMarks.thenCompose(writers -> {
WatermarkClient watermarkClient = watermarkClientCache.getUnchecked(stream);
try {
watermarkClient.reinitialize();
} catch (Exception e) {
log.warn(requestId, "Watermarking client for stream {} threw exception {} during reinitialize.", stream, Exceptions.unwrap(e).getClass());
if (Exceptions.unwrap(e) instanceof NoSuchSegmentException) {
log.info(requestId, "Invalidating the watermark client in cache for stream {}.", stream);
watermarkClientCache.invalidate(stream);
}
throw e;
}
return streamMetadataStore.getConfiguration(scope, streamName, context, executor).thenCompose(config -> filterWritersAndComputeWatermark(scope, streamName, context, watermarkClient, writers, config));
}).exceptionally(e -> {
log.warn(requestId, "Exception thrown while trying to perform periodic watermark computation. Logging and ignoring.", e);
return null;
});
}
Aggregations