-
-
Notifications
You must be signed in to change notification settings - Fork 4.2k
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
Crons: Run monitor tasks check_missing
+ check_timeout
via topic boundaries OR clock pulses
#53661
Comments
check_missing
+ check_timeout
via kafka clock pulsescheck_missing
+ check_timeout
via topic boundaries OR clock pulses
This is a partial implementation of GH-53661. This implements the "High Volume" mode, where check-ins from the consumer are essentially the 'clock pulses' that are used to dispatch the monitor tasks each minute. This change does NOT actually dispatch the tasks, but it does send some telemetry each time the tasks would be dispatched, as well as capture error conditions when the clock skips.
…tch (#54204) This is a partial implementation of GH-53661. This implements the "High Volume" mode, where check-ins from the consumer are essentially the 'clock pulses' that are used to dispatch the monitor tasks each minute. This change does NOT actually dispatch the tasks, but it does send some telemetry each time the tasks would be dispatched, as well as capture error conditions when the clock skips.
Regarding the two modes:
If you add the pulse in all environments you are guaranteed to have at least one message per minute everywhere, unless the consumer is backlogging, and just run in high volume mode. |
Regarding using Kafka as a clock. If one partition is further back from another and the more recent one is triggering the task to fill in missing checkins you may be filling in check ins that are still stuck in the slower partition but not missing. There are two ways to address this and we do for subscriptions:
|
re: locks.
|
Thanks for the feedback @fpacifici
Thanks for this feedback, I've addressed this here: #54341
This is good, yes. I will follow up and implement this.
Also a good catch, we didn't take into consideration a partial partition backlog. I will follow up with @wedamija about this tomorrow and we'll come up with a plan. |
Instead of taking a lock when ticking the monitor tasks trigger clock, we can use redis' `GETSET` command to retrieve the current value and set a new value atomically. This addresses @fpacifici's feedback here #53661 (comment)
This is a follow up to GH-54204 as suggested by @fpacifici #53661 (comment) > Can we just have the pulse message running in both modes and treat > everything as high volume mode? Instead of having two modes, we can simply always use the same logic for dispatching the monitor tasks on the minute roll-over, using the consumer as a clock. Previously the worry here was that in low-volume check-in situations nothing would drive the clock and we would need to have an external clock, with a different way to dispatch the tasks. But there is no need for a different way to dispatch the tasks, we can have an external clock that pulses messages into the topic and we can simply use the same logic already implemented to use the topic messages as a clock. This change removes the concept of "high volume" / "low volume" and adds the concept of a "clock_pulse" message to the consumer. In a follow up PR we will introduce the celery beat task which produces the clock_pulse messages.
This is a follow up to GH-54204 as suggested by @fpacifici #53661 (comment) > Can we just have the pulse message running in both modes and treat > everything as high volume mode? Instead of having two modes, we can simply always use the same logic for dispatching the monitor tasks on the minute roll-over, using the consumer as a clock. Previously the worry here was that in low-volume check-in situations nothing would drive the clock and we would need to have an external clock, with a different way to dispatch the tasks. But there is no need for a different way to dispatch the tasks, we can have an external clock that pulses messages into the topic and we can simply use the same logic already implemented to use the topic messages as a clock. This change removes the concept of "high volume" / "low volume" and adds the concept of a "clock_pulse" message to the consumer. In a follow up PR we will introduce the celery beat task which produces the clock_pulse messages.
This is a follow up to GH-54204 as suggested by @fpacifici #53661 (comment) > Can we just have the pulse message running in both modes and treat > everything as high volume mode? Instead of having two modes, we can simply always use the same logic for dispatching the monitor tasks on the minute roll-over, using the consumer as a clock. Previously the worry here was that in low-volume check-in situations nothing would drive the clock and we would need to have an external clock, with a different way to dispatch the tasks. But there is no need for a different way to dispatch the tasks, we can have an external clock that pulses messages into the topic and we can simply use the same logic already implemented to use the topic messages as a clock. This change removes the concept of "high volume" / "low volume" and adds the concept of a "clock_pulse" message to the consumer. In a follow up PR we will introduce the celery beat task which produces the clock_pulse messages.
This is the last piece of GH-53661 to ensure tasks are triggered in scenarios where there is not enough volume.
This is now completed! |
The problem
Currently we have two tasks which must run once per minute. These task are responsible for creating missed check-ins for monitors which did not receive check-ins on time, and for marking check-ins as timed-out when they pass their
timeout_at
window.There are two problems with these tasks
If we backlog our consumer queue monitors may be marked as missed or in-progress check-ins may be marked as timed out since the tasks may correctly run on time, but check-ins which should have been processed on time may have been backlogged. (we should be able to completely solve this)
It is possible that we are late to marking missed or timed-out check-ins due to periodic tasks being skipped during deploys. This is an artifact of how the celery beat runner is stopped and restarted during deploys. (we can improve this, but not necessarily solve it)
(This will not be completely solved by this issue)
Solution
Instead of running our tasks via Celery's Period Tasks, we can provide a way to have our tasks be triggered via our consumer, in-time with our kafka topic. This will avoid our tasks being run out even when there is a backlog.
I propose we have two modes which this should be done in, due to constraints of SASS, single tenant, and on-premise.
(The mode would be configured with a
settings
flag)Mode one: "High volume mode" -- When we are guaranteed to have a high volume of monitor check-ins from our producer. In this scenario we compute the timestamp of the message with seconds removed (floored to the minute) and compare that with the previously stored value. This would be stored in Redis. Any time we roll over the minute we hold a lock and schedule the two tasks (
check_missing
/check_timeout
) for immediate execution via celery task runners. Importantly will receive a timestamp from the messages as their reference time and will use that to determine what monitors should be marked as missed or check-ins marked as timed out.Why this mode?
Due to the high volume we're able to accurately trigger our tasks on minute markers purely based on our kafka topic. With high volume, this is the most accurate way to schedule our two monitor tasks for execution.
Mode two: "low volume mode" -- When we do not have a high volume of check-ins, such as single tenant or on premise. In this scenario we have another periodic celery task which produces a clock pulse message once per minute and puts the message in the
ingest-monitors
topic. These messages should include the timestamp of when the pulse was generated. Ideally these pulses are generated on the minute exactly wall clock time so they are put into the queue in order.Our
monitor_consumer
arroyo processor would then discriminate on clock pulse messages and schedule the two tasks (check_missing
/check_timeout
) for immediate execution via celery task runners. Importantly will receive a timestamp from the messages as their reference time and will use that to determine what monitors should be marked as missed or check-ins marked as timed out.Why this mode?
When volume of check-ins is not high we are not able to simply use our messages as a clock. Instead we must have an external clock pulse that is guaranteed to trigger so we can ensure even in a backlog situation we have messages in the kafka queue that are in time with when the tasks should be run.
Task execution order
Another important aspect is that when we do backlog and we push many of our two tasks into queue for celery to process, each set of tasks SHOULD happen in order and NOT at the same time. To guarantee this we need to create a new worker queue for each task with the size of 1, so each task runs sequentially.
Notes
next_checkin
value is generated based on the current time, it MUST use the reference time, so future messages in the backlog are correctly processed.Tasks
The text was updated successfully, but these errors were encountered: