-
Notifications
You must be signed in to change notification settings - Fork 14.9k
MINOR: Decouple timer and executor from CoordinatorRuntime #21350
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
- Replace CoordinatorRuntime and TopicPartition dependencies with a functional Scheduler interface - Remove unnecessary generic type parameter S - Use var for local variable declarations - Simplify tests by mocking the Scheduler instead of CoordinatorRuntime
This patch extracts EventBasedCoordinatorTimer from CoordinatorRuntime into a standalone CoordinatorTimerImpl class, following the same pattern as CoordinatorExecutorImpl. The new class uses a Scheduler functional interface to decouple from CoordinatorRuntime internals. This also adds comprehensive unit tests for CoordinatorTimerImpl.
Extract the identical WriteOperation and Scheduler interfaces from CoordinatorExecutorImpl and CoordinatorTimerImpl into a shared CoordinatorShardScheduler interface. This provides a common internal API for shard-scoped components to schedule write operations through the coordinator runtime.
Reorder constructor parameters to place the scheduler last, matching the parameter order in CoordinatorTimerImpl.
|
@squah-confluent Could you please review? |
chia7712
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@dajac thanks for this great refactor
| @Override | ||
| public void schedule( | ||
| String key, | ||
| long delay, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Have you considered using a Duration to replace both delay and unit?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have not considered it. It will consider it separately and raise a PR if it makes sense to do it.
| }; | ||
|
|
||
| log.debug("Registering timer {} with delay of {}ms.", key, unit.toMillis(delay)); | ||
| var prevTask = tasks.put(key, task); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
var prevTask = tasks.put(key, task);
if (!tasks.remove(key, this))
Is it possible that two different threads change the tasks concurrently after this decoupling?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No, the threading model is still the same.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
the threading model is still the same.
If scheduler.scheduleWriteOperation returns a failed CompletableFuture, the exceptionally block will be executed by the timer thread. Does it break the threading model?
| // Execute the timeout operation. | ||
| return operation.generateRecords(); | ||
| } | ||
| ).exceptionally(ex -> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What if scheduleWriteOperation returns a failed future? Will the task not be removed?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good catch. We should remove it here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed it.
| log.debug("Scheduling write event {} for timer {}.", event.name, key); | ||
| try { | ||
| enqueueLast(event); | ||
| } catch (NotCoordinatorException ex) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The exceptions that come out of scheduleWriteOperation/enqueueLast are very confusing. I think it would help if we were a bit more thorough with the @throwss in the javadocs, not necessarily in this PR.
I think the seems that both the future can complete with RejectedExecutionException handler within the exceptionally above will never be hit because scheduleWriteOperation will throw it directly?RejectedExecutionException and scheduleWriteOperation can throw it directly.
This patch fixes two issues in CoordinatorTimerImpl: 1. Synchronous exceptions from scheduleWriteOperation were not caught, leaving tasks in the map. Fixed by wrapping the scheduler lambda with try-catch in CoordinatorRuntime.CoordinatorContext to convert exceptions to failed futures. 2. Tasks were not removed in the exceptionally handler when the write operation failed. Fixed by adding defensive task cleanup as the first step in the exceptionally handler, following the same pattern as CoordinatorExecutorImpl.
Yes, I agree.
|
| try { | ||
| return scheduleWriteOperation( | ||
| operationName, | ||
| tp, | ||
| coordinator -> operation.generate() | ||
| ); | ||
| } catch (Throwable t) { | ||
| return CompletableFuture.failedFuture(t); | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I handle synchronous exceptions here so components can only work with the future. It is simpler this way.
squah-confluent
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for the refactor!
agreed. It would be cool to handle both in one fell swoop. The method returning |
This patch refactors the coordinator runtime to improve modularity by
decoupling the timer and executor components:
standalone CoordinatorTimerImpl;
components (timer, executor) to schedule write operations back to the
runtime within their shard's scope;
dependency with the new scheduler interface;
Reviewers: Sean Quah squah@confluent.io, Chia-Ping Tsai
chia7712@gmail.com