use of io.pravega.client.control.impl.ReaderGroupConfigRejectedException in project pravega by pravega.
the class ReaderGroupImpl method resetReaderGroup.
@Override
public void resetReaderGroup(ReaderGroupConfig config) {
log.info("Reset ReaderGroup {} to {}", getGroupName(), config);
synchronizer.fetchUpdates();
while (true) {
val currentConfig = synchronizer.getState().getConfig();
// We only move into the block if the state transition has happened successfully.
if (stateTransition(currentConfig, new UpdatingConfig(true))) {
if (currentConfig.getReaderGroupId() == ReaderGroupConfig.DEFAULT_UUID && currentConfig.getGeneration() == ReaderGroupConfig.DEFAULT_GENERATION) {
// Migration code path, for moving a ReaderGroup from version < 0.9 to 0.9+
final ReaderGroupConfig updateConfig = ReaderGroupConfig.cloneConfig(config, UUID.randomUUID(), 0L);
final long nextGen = Futures.getThrowingException(controller.createReaderGroup(scope, getGroupName(), updateConfig).thenCompose(conf -> {
if (!conf.getReaderGroupId().equals(updateConfig.getReaderGroupId())) {
return controller.updateReaderGroup(scope, groupName, ReaderGroupConfig.cloneConfig(updateConfig, conf.getReaderGroupId(), conf.getGeneration()));
} else {
// ReaderGroup IDs matched so our create was updated on Controller
return CompletableFuture.completedFuture(conf.getGeneration());
}
}));
updateConfigInStateSynchronizer(updateConfig, nextGen);
} else {
// normal code path
// Use the latest generation and reader group Id.
ReaderGroupConfig newConfig = ReaderGroupConfig.cloneConfig(config, currentConfig.getReaderGroupId(), currentConfig.getGeneration());
long newGen = Futures.exceptionallyExpecting(controller.updateReaderGroup(scope, groupName, newConfig), e -> Exceptions.unwrap(e) instanceof ReaderGroupConfigRejectedException, -1L).join();
if (newGen == -1) {
log.debug("Synchronize reader group with the one present on controller.");
synchronizeReaderGroupConfig();
continue;
}
updateConfigInStateSynchronizer(newConfig, newGen);
}
return;
}
}
}
use of io.pravega.client.control.impl.ReaderGroupConfigRejectedException in project pravega by pravega.
the class ReaderGroupImplTest method laggingResetReaderGroup.
@Test
@SuppressWarnings("unchecked")
public void laggingResetReaderGroup() {
UUID rgId = UUID.randomUUID();
ReaderGroupConfig config1 = ReaderGroupConfig.builder().startFromStreamCuts(ImmutableMap.<Stream, StreamCut>builder().put(createStream("s1"), createStreamCut("s1", 2)).put(createStream("s2"), createStreamCut("s2", 3)).build()).build();
config1 = ReaderGroupConfig.cloneConfig(config1, rgId, 0L);
ReaderGroupConfig config2 = ReaderGroupConfig.builder().startFromStreamCuts(ImmutableMap.<Stream, StreamCut>builder().put(createStream("s3"), createStreamCut("s3", 2)).put(createStream("s4"), createStreamCut("s4", 3)).build()).build();
config2 = ReaderGroupConfig.cloneConfig(config2, rgId, 1L);
when(state.getConfig()).thenReturn(config1, config2);
when(synchronizer.getState()).thenReturn(state);
CompletableFuture<Long> badFuture = new CompletableFuture<>();
badFuture.completeExceptionally(new ReaderGroupConfigRejectedException("handle"));
// The controller has config2
when(controller.updateReaderGroup(eq(SCOPE), eq(GROUP_NAME), argThat(new ReaderGroupConfigMatcher(config1)))).thenReturn(badFuture);
when(controller.updateReaderGroup(eq(SCOPE), eq(GROUP_NAME), argThat(new ReaderGroupConfigMatcher(ReaderGroupConfig.cloneConfig(config1, config1.getReaderGroupId(), config2.getGeneration()))))).thenReturn(CompletableFuture.completedFuture(2L));
when(controller.getReaderGroupConfig(SCOPE, GROUP_NAME)).thenReturn(CompletableFuture.completedFuture(config2));
readerGroup.resetReaderGroup(config1);
verify(synchronizer, times(1)).fetchUpdates();
verify(controller, times(1)).updateReaderGroup(eq(SCOPE), eq(GROUP_NAME), argThat(new ReaderGroupConfigMatcher(config1)));
verify(controller, times(1)).updateReaderGroup(eq(SCOPE), eq(GROUP_NAME), argThat(new ReaderGroupConfigMatcher(ReaderGroupConfig.cloneConfig(config1, config1.getReaderGroupId(), config2.getGeneration()))));
verify(controller, times(1)).getReaderGroupConfig(SCOPE, GROUP_NAME);
verify(synchronizer, times(4)).updateState(any(StateSynchronizer.UpdateGenerator.class));
}
use of io.pravega.client.control.impl.ReaderGroupConfigRejectedException in project pravega by pravega.
the class ReaderGroupImplTest method testAsyncResetReaderGroup.
@Test(timeout = 10000L)
@SuppressWarnings("unchecked")
public void testAsyncResetReaderGroup() {
UUID rgId = UUID.randomUUID();
ReaderGroupConfig config1 = ReaderGroupConfig.builder().startFromStreamCuts(ImmutableMap.<Stream, StreamCut>builder().put(createStream("s1"), createStreamCut("s1", 2)).build()).build();
config1 = ReaderGroupConfig.cloneConfig(config1, rgId, 0L);
ReaderGroupConfig config2 = ReaderGroupConfig.builder().startFromStreamCuts(ImmutableMap.<Stream, StreamCut>builder().put(createStream("s2"), createStreamCut("s2", 2)).build()).build();
config2 = ReaderGroupConfig.cloneConfig(config2, rgId, 0L);
ReaderGroupConfig config3 = ReaderGroupConfig.builder().startFromStreamCuts(ImmutableMap.<Stream, StreamCut>builder().put(createStream("s3"), createStreamCut("s3", 2)).build()).build();
config3 = ReaderGroupConfig.cloneConfig(config3, rgId, 0L);
AtomicInteger x = new AtomicInteger(0);
CompletableFuture<Void> wait = new CompletableFuture<>();
CompletableFuture<Void> signal = new CompletableFuture<>();
// The client's config.
AtomicReference<ReaderGroupConfig> atomicConfig = new AtomicReference<>(config1);
// The controller's config.
AtomicReference<ReaderGroupConfig> atomicConfigController = new AtomicReference<>(config1);
// return the client's config when calling state.getConfig.
doAnswer(a -> atomicConfig.get()).when(state).getConfig();
when(synchronizer.getState()).thenReturn(state);
// return controllerConfig when calling getReaderGroupConfig.
doAnswer(a -> CompletableFuture.completedFuture(atomicConfigController.get())).when(controller).getReaderGroupConfig(anyString(), anyString());
// update the client config to the controller config whenever we update the StateSync.
doAnswer(a -> {
atomicConfig.set(atomicConfigController.get());
return null;
}).when(synchronizer).updateState(any(StateSynchronizer.UpdateGenerator.class));
// update the controller config with the incremented generation if the generations match.
doAnswer(a -> {
ReaderGroupConfig c = a.getArgument(2);
// the first one to call needs to wait until the second call has been completed.
if (x.getAndIncrement() == 0) {
signal.complete(null);
wait.join();
}
if (c.getGeneration() == atomicConfigController.get().getGeneration()) {
long incGen = c.getGeneration() + 1;
atomicConfigController.set(ReaderGroupConfig.cloneConfig(c, c.getReaderGroupId(), incGen));
return CompletableFuture.completedFuture(incGen);
} else {
CompletableFuture<Long> badFuture = new CompletableFuture<>();
badFuture.completeExceptionally(new ReaderGroupConfigRejectedException("handle"));
return badFuture;
}
}).when(controller).updateReaderGroup(anyString(), anyString(), any(ReaderGroupConfig.class));
// run the first call async.
final ReaderGroupConfig newConf = config2;
CompletableFuture<Void> future = CompletableFuture.runAsync(() -> readerGroup.resetReaderGroup(newConf));
// Once the first call has reached the controller.updateReaderGroup step then signal so he waits.
signal.join();
// start the second call.
readerGroup.resetReaderGroup(config3);
// Once the second is completed stop the wait so the first call can go ahead.
wait.complete(null);
// wait for the first call to complete.
assertTrue(Futures.await(future));
// assert the generation was incremented twice due to two updates.
assertEquals(2L, atomicConfig.get().getGeneration());
assertEquals(2L, atomicConfigController.get().getGeneration());
// assert the first call happened last and the streams are s2.
assertTrue(atomicConfig.get().getStartingStreamCuts().keySet().stream().anyMatch(s -> s.getStreamName().equals("s2")));
assertTrue(atomicConfigController.get().getStartingStreamCuts().keySet().stream().anyMatch(s -> s.getStreamName().equals("s2")));
}
use of io.pravega.client.control.impl.ReaderGroupConfigRejectedException in project pravega by pravega.
the class LocalControllerTest method testUpdateReaderGroup.
@Test(timeout = 10000)
public void testUpdateReaderGroup() throws ExecutionException, InterruptedException {
final Segment seg0 = new Segment("scope", "stream1", 0L);
final Segment seg1 = new Segment("scope", "stream1", 1L);
ImmutableMap<Segment, Long> startStreamCut = ImmutableMap.of(seg0, 10L, seg1, 10L);
Map<Stream, StreamCut> startSC = ImmutableMap.of(Stream.of("scope", "stream1"), new StreamCutImpl(Stream.of("scope", "stream1"), startStreamCut));
ImmutableMap<Segment, Long> endStreamCut = ImmutableMap.of(seg0, 200L, seg1, 300L);
Map<Stream, StreamCut> endSC = ImmutableMap.of(Stream.of("scope", "stream1"), new StreamCutImpl(Stream.of("scope", "stream1"), endStreamCut));
ReaderGroupConfig rgConfig = ReaderGroupConfig.builder().automaticCheckpointIntervalMillis(30000L).groupRefreshTimeMillis(20000L).maxOutstandingCheckpointRequest(2).retentionType(ReaderGroupConfig.StreamDataRetention.AUTOMATIC_RELEASE_AT_LAST_CHECKPOINT).startingStreamCuts(startSC).endingStreamCuts(endSC).build();
ReaderGroupConfig config = ReaderGroupConfig.cloneConfig(rgConfig, UUID.randomUUID(), 0L);
when(this.mockControllerService.updateReaderGroup(anyString(), anyString(), any(), anyLong())).thenReturn(CompletableFuture.completedFuture(Controller.UpdateReaderGroupResponse.newBuilder().setStatus(Controller.UpdateReaderGroupResponse.Status.SUCCESS).setGeneration(1L).build()));
Assert.assertNotNull(this.testController.updateReaderGroup("scope", "subscriber", config).join());
when(this.mockControllerService.updateReaderGroup(anyString(), anyString(), any(), anyLong())).thenReturn(CompletableFuture.completedFuture(Controller.UpdateReaderGroupResponse.newBuilder().setStatus(Controller.UpdateReaderGroupResponse.Status.FAILURE).build()));
assertThrows("Expected ControllerFailureException", () -> this.testController.updateReaderGroup("scope", "subscriber", config).join(), ex -> ex instanceof ControllerFailureException);
when(this.mockControllerService.updateReaderGroup(anyString(), anyString(), any(), anyLong())).thenReturn(CompletableFuture.completedFuture(Controller.UpdateReaderGroupResponse.newBuilder().setStatus(Controller.UpdateReaderGroupResponse.Status.INVALID_CONFIG).build()));
assertThrows("Expected ReaderGroupConfigRejectedException", () -> this.testController.updateReaderGroup("scope", "subscriber", config).join(), ex -> ex instanceof ReaderGroupConfigRejectedException);
when(this.mockControllerService.updateReaderGroup(anyString(), anyString(), any(), anyLong())).thenReturn(CompletableFuture.completedFuture(Controller.UpdateReaderGroupResponse.newBuilder().setStatus(Controller.UpdateReaderGroupResponse.Status.RG_NOT_FOUND).build()));
assertThrows("Expected IllegalArgumentException", () -> this.testController.updateReaderGroup("scope", "subscriber", config).join(), ex -> ex instanceof IllegalArgumentException);
when(this.mockControllerService.updateReaderGroup(anyString(), anyString(), any(), anyLong())).thenReturn(CompletableFuture.completedFuture(Controller.UpdateReaderGroupResponse.newBuilder().setStatusValue(-1).build()));
assertThrows("Expected ControllerFailureException", () -> this.testController.updateReaderGroup("scope", "subscriber", config).join(), ex -> ex instanceof ControllerFailureException);
}
Aggregations