github-advanced-security[bot] commented on code in PR #20741:
URL: https://github.com/apache/druid/pull/20741#discussion_r4382501036
##########
indexing-service/src/test/java/org/apache/druid/indexing/seekablestream/supervisor/SeekableStreamSupervisorStateTest.java:
##########
@@ -632,6 +632,290 @@
verifyAll();
}
+ @Test
+ public void
testPendingCompletionTimeout_afterScaling_killsReadingTaskGroupsOverlappingPartitions()
+ {
+ // taskCount is now 3, so the actively reading task groups are {0, 3}, {1}
and {2}
+ final SeekableStreamSupervisorIOConfig ioConfig =
setupSpecForPendingCompletionTest(4);
+
+ // Tasks of the pending completion task groups that were created when
taskCount was 2: {0, 2} and {1, 3}
+ expectRunningTask("failedTask");
+ expectRunningTask("publishingTask");
+ taskQueue.shutdown(EasyMock.eq("failedTask"), EasyMock.anyString(),
EasyMock.anyObject());
+ EasyMock.expectLastCall().once();
+
+ // Task groups 0 and 2 read the partitions of the failed task group and
must be killed. Task group 1 does not.
+ expectRunningTask("readingTask0");
+ expectRunningTask("readingTask1");
+ expectRunningTask("readingTask2");
+ taskQueue.shutdown(EasyMock.eq("readingTask0"), EasyMock.anyString(),
EasyMock.anyObject());
+ EasyMock.expectLastCall().once();
+ taskQueue.shutdown(EasyMock.eq("readingTask2"), EasyMock.anyString(),
EasyMock.anyObject());
+ EasyMock.expectLastCall().once();
+ expectCurrentTask("readingTask1", 1, Map.of("1", "150"), ioConfig);
+
+ replayAll();
+
+ final TestSeekableStreamSupervisor supervisor = new
TestSeekableStreamSupervisor();
+ supervisor.start();
+ supervisor.addDiscoveredTaskToPendingCompletionTaskGroups(0, "failedTask",
Map.of("0", "100", "2", "200"));
+ supervisor.addDiscoveredTaskToPendingCompletionTaskGroups(1,
"publishingTask", Map.of("1", "100", "3", "300"));
+ addActivelyReadingTaskGroup(supervisor, 0, Map.of("0", "150", "3", "350"),
Set.of("readingTask0"));
+ addActivelyReadingTaskGroup(supervisor, 1, Map.of("1", "150"),
Set.of("readingTask1"));
+ addActivelyReadingTaskGroup(supervisor, 2, Map.of("2", "250"),
Set.of("readingTask2"));
+ supervisor.getPartitionOffsets().putAll(Map.of("0", "150", "1", "150",
"2", "250", "3", "350"));
+
+ supervisor.getPendingCompletionTaskGroups(0).get(0).completionTimeout =
DateTimes.nowUtc().minusMinutes(1);
+ supervisor.runInternal();
+ verifyAll();
+
+ // Only the partitions of the failed task group are re-read from the stream
+ Assertions.assertEquals(supervisor.getNotSetMarker(),
supervisor.getPartitionOffsets().get("0"));
+ Assertions.assertEquals(supervisor.getNotSetMarker(),
supervisor.getPartitionOffsets().get("2"));
+ Assertions.assertEquals("150", supervisor.getPartitionOffsets().get("1"));
+ Assertions.assertEquals("350", supervisor.getPartitionOffsets().get("3"));
+
+ Assertions.assertEquals(Set.of("readingTask1"),
supervisor.getActiveTaskGroup(1).taskIds());
+ Assertions.assertEquals(Map.of("0", "10", "3", "350"),
supervisor.getActiveTaskGroup(0).startingSequences);
+ Assertions.assertEquals(Map.of("2", "10"),
supervisor.getActiveTaskGroup(2).startingSequences);
+
+
Assertions.assertTrue(supervisor.getPendingCompletionTaskGroups(0).isEmpty());
+ Assertions.assertEquals(1,
supervisor.getPendingCompletionTaskGroups(1).size());
+ }
+
+ @Test
+ public void
testPendingCompletionTimeout_stopsOnlyLaterTaskGroupsOverlappingPartitions()
+ {
+ final SeekableStreamSupervisorIOConfig ioConfig =
setupSpecForPendingCompletionTest(4);
+
+ expectRunningTask("failedTask");
+ expectRunningTask("disjointTask");
+ expectRunningTask("overlappingTask");
+ taskQueue.shutdown(EasyMock.eq("failedTask"), EasyMock.anyString(),
EasyMock.anyObject());
+ EasyMock.expectLastCall().once();
+ EasyMock.expect(indexTaskClient.stopAsync("overlappingTask", false))
+ .andReturn(Futures.immediateFuture(true)).once();
+
+ // Task group 2 reads the partition of the failed task group, and task
group 0 reads partition 3 from the end
+ // offsets of the stopped task group
+ expectRunningTask("readingTask0");
+ expectRunningTask("readingTask1");
+ expectRunningTask("readingTask2");
+ taskQueue.shutdown(EasyMock.eq("readingTask0"), EasyMock.anyString(),
EasyMock.anyObject());
+ EasyMock.expectLastCall().once();
+ taskQueue.shutdown(EasyMock.eq("readingTask2"), EasyMock.anyString(),
EasyMock.anyObject());
+ EasyMock.expectLastCall().once();
+ expectCurrentTask("readingTask1", 1, Map.of("1", "150"), ioConfig);
+
+ replayAll();
+
+ final TestSeekableStreamSupervisor supervisor = new
TestSeekableStreamSupervisor();
+ supervisor.start();
+ // After a scale action, the later task groups in the same list may handle
a different partition set
+ supervisor.addDiscoveredTaskToPendingCompletionTaskGroups(0, "failedTask",
Map.of("2", "200"));
+ supervisor.addDiscoveredTaskToPendingCompletionTaskGroups(0,
"disjointTask", Map.of("1", "100"));
+ supervisor.addDiscoveredTaskToPendingCompletionTaskGroups(0,
"overlappingTask", Map.of("2", "250", "3", "350"));
+ addActivelyReadingTaskGroup(supervisor, 0, Map.of("0", "150", "3", "400"),
Set.of("readingTask0"));
+ addActivelyReadingTaskGroup(supervisor, 1, Map.of("1", "150"),
Set.of("readingTask1"));
+ addActivelyReadingTaskGroup(supervisor, 2, Map.of("2", "300"),
Set.of("readingTask2"));
+ supervisor.getPartitionOffsets().putAll(Map.of("0", "150", "1", "150",
"2", "300", "3", "400"));
+
+ supervisor.getPendingCompletionTaskGroups(0).get(0).completionTimeout =
DateTimes.nowUtc().minusMinutes(1);
+ supervisor.runInternal();
+ verifyAll();
+
+ // The partitions of the failed and the stopped task groups are re-read
from the stream
+ Assertions.assertEquals(supervisor.getNotSetMarker(),
supervisor.getPartitionOffsets().get("2"));
+ Assertions.assertEquals(supervisor.getNotSetMarker(),
supervisor.getPartitionOffsets().get("3"));
+ Assertions.assertEquals("150", supervisor.getPartitionOffsets().get("0"));
+ Assertions.assertEquals("150", supervisor.getPartitionOffsets().get("1"));
+
+ Assertions.assertEquals(Set.of("readingTask1"),
supervisor.getActiveTaskGroup(1).taskIds());
+ Assertions.assertEquals(Map.of("0", "150", "3", "10"),
supervisor.getActiveTaskGroup(0).startingSequences);
+ Assertions.assertEquals(Map.of("2", "10"),
supervisor.getActiveTaskGroup(2).startingSequences);
+
+ // The task group that does not overlap the failed task group keeps
publishing
+ Assertions.assertEquals(1,
supervisor.getPendingCompletionTaskGroups(0).size());
+ Assertions.assertEquals(Set.of("disjointTask"),
supervisor.getPendingCompletionTaskGroups(0).get(0).taskIds());
+ }
+
+ @Test
+ public void
testPendingCompletionTimeout_killsTaskGroupAssignedUnreadPartition_notTaskGroupWithSameId()
+ {
+ // The task groups are {0, 3}, {1, 4} and {2, 5}
+ final SeekableStreamSupervisorIOConfig ioConfig =
setupSpecForPendingCompletionTest(6);
+
+ expectRunningTask("failedTask");
+ taskQueue.shutdown(EasyMock.eq("failedTask"), EasyMock.anyString(),
EasyMock.anyObject());
+ EasyMock.expectLastCall().once();
+
+ // Task group 2 is assigned the partition of the failed task group and
must be killed. Task group 0 keeps running
+ // even though it has the same groupId as the failed task group, since it
does not read that partition.
+ expectRunningTask("readingTask0");
+ expectRunningTask("readingTask1");
+ expectRunningTask("readingTask2");
+ taskQueue.shutdown(EasyMock.eq("readingTask2"), EasyMock.anyString(),
EasyMock.anyObject());
+ EasyMock.expectLastCall().once();
+ expectCurrentTask("readingTask0", 0, Map.of("0", "150", "3", "350"),
ioConfig);
+ expectCurrentTask("readingTask1", 1, Map.of("1", "150", "4", "450"),
ioConfig);
+
+ replayAll();
+
+ final TestSeekableStreamSupervisor supervisor = new
TestSeekableStreamSupervisor();
+ supervisor.start();
+ // The failed task group was created when partition 2 was assigned to task
group 0, e.g. before Kinesis shards
+ // were reassigned
+ supervisor.addDiscoveredTaskToPendingCompletionTaskGroups(0, "failedTask",
Map.of("2", "200"));
+ addActivelyReadingTaskGroup(supervisor, 0, Map.of("0", "150", "3", "350"),
Set.of("readingTask0"));
+ addActivelyReadingTaskGroup(supervisor, 1, Map.of("1", "150", "4", "450"),
Set.of("readingTask1"));
+ // Task group 2 is not reading partition 2, as for a closed Kinesis shard
whose end offset was never published
+ addActivelyReadingTaskGroup(supervisor, 2, Map.of("5", "550"),
Set.of("readingTask2"));
+ supervisor.getPartitionOffsets().putAll(Map.of("0", "150", "1", "150",
"3", "350", "4", "450", "5", "550"));
+
+ supervisor.getPendingCompletionTaskGroups(0).get(0).completionTimeout =
DateTimes.nowUtc().minusMinutes(1);
+ supervisor.runInternal();
+ verifyAll();
+
+ Assertions.assertEquals(Set.of("readingTask0"),
supervisor.getActiveTaskGroup(0).taskIds());
+ Assertions.assertEquals(Set.of("readingTask1"),
supervisor.getActiveTaskGroup(1).taskIds());
+ // Task group 2 is recreated to re-read partition 2
+ Assertions.assertEquals(Map.of("2", "10", "5", "550"),
supervisor.getActiveTaskGroup(2).startingSequences);
+
Assertions.assertTrue(supervisor.getPendingCompletionTaskGroups(0).isEmpty());
+ }
+
+ @Test
+ public void
testPendingCompletionTimeout_afterPublishing_keepsReadingTaskGroupsStartedFromCommittedOffsets()
+ {
+ final SeekableStreamSupervisorIOConfig ioConfig =
setupSpecForPendingCompletionTest(4);
+
+ // The failed task group published its end offsets, but did not complete
within the completion timeout, e.g.
+ // because handoff took too long
+ EasyMock.reset(indexerMetadataStorageCoordinator);
+
EasyMock.expect(indexerMetadataStorageCoordinator.retrieveDataSourceMetadata(SUPERVISOR_ID)).andReturn(
+ new TestSeekableStreamDataSourceMetadata(
+ new SeekableStreamEndSequenceNumbers<>(STREAM, Map.of("0", "150",
"1", "150", "2", "250", "3", "350"))
+ )
+ ).anyTimes();
+
+ expectRunningTask("failedTask");
+ taskQueue.shutdown(EasyMock.eq("failedTask"), EasyMock.anyString(),
EasyMock.anyObject());
+ EasyMock.expectLastCall().once();
+
+ // None of the actively reading task groups are killed, since they started
from the committed offsets
+ expectRunningTask("readingTask0");
+ expectRunningTask("readingTask1");
+ expectRunningTask("readingTask2");
+ expectCurrentTask("readingTask0", 0, Map.of("0", "150", "3", "350"),
ioConfig);
+ expectCurrentTask("readingTask1", 1, Map.of("1", "150"), ioConfig);
+ expectCurrentTask("readingTask2", 2, Map.of("2", "250"), ioConfig);
+
+ replayAll();
+
+ final TestSeekableStreamSupervisor supervisor = new
TestSeekableStreamSupervisor();
+ supervisor.start();
+ supervisor.addDiscoveredTaskToPendingCompletionTaskGroups(0, "failedTask",
Map.of("0", "100", "2", "200"));
+ addActivelyReadingTaskGroup(supervisor, 0, Map.of("0", "150", "3", "350"),
Set.of("readingTask0"));
+ addActivelyReadingTaskGroup(supervisor, 1, Map.of("1", "150"),
Set.of("readingTask1"));
+ addActivelyReadingTaskGroup(supervisor, 2, Map.of("2", "250"),
Set.of("readingTask2"));
+ supervisor.getPartitionOffsets().putAll(Map.of("0", "150", "1", "150",
"2", "250", "3", "350"));
+
+ supervisor.getPendingCompletionTaskGroups(0).get(0).completionTimeout =
DateTimes.nowUtc().minusMinutes(1);
+ supervisor.runInternal();
+ verifyAll();
+
+ Assertions.assertEquals(Set.of("readingTask0"),
supervisor.getActiveTaskGroup(0).taskIds());
+ Assertions.assertEquals(Set.of("readingTask1"),
supervisor.getActiveTaskGroup(1).taskIds());
+ Assertions.assertEquals(Set.of("readingTask2"),
supervisor.getActiveTaskGroup(2).taskIds());
+
Assertions.assertTrue(supervisor.getPendingCompletionTaskGroups(0).isEmpty());
+ }
+
+ /**
+ * Sets up a supervisor with taskCount 3 reading partitions 0 to {@code
numPartitions - 1}, so that the task groups
+ * are {0, 3, ...}, {1, 4, ...} and {2, 5, ...}.
+ */
+ private SeekableStreamSupervisorIOConfig
setupSpecForPendingCompletionTest(int numPartitions)
+ {
+ final SeekableStreamSupervisorIOConfig ioConfig =
createSupervisorIOConfig(3, null);
+
+ EasyMock.reset(spec);
+ EasyMock.expect(spec.getId()).andReturn(SUPERVISOR_ID).anyTimes();
+
EasyMock.expect(spec.getSupervisorStateManagerConfig()).andReturn(supervisorConfig).anyTimes();
+
EasyMock.expect(spec.getDataSchema()).andReturn(getDataSchema()).anyTimes();
Review Comment:
## CodeQL / Deprecated method or constructor invocation
Invoking [SeekableStreamSupervisorSpec.getDataSchema](1) should be avoided
because it has been deprecated.
[Show more
details](https://github.com/apache/druid/security/code-scanning/698)
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: [email protected]
For queries about this service, please contact Infrastructure at:
[email protected]
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]