Basic information on functions and classes that execute some code triggered by some non-IO event.
utils::PeriodicTask, as the name implies, regularly calls user code. Keep in mind that the task is called on every machine in the cluster.
Well suited for:
Poorly suited for:
The update period can be changed without stopping the periodic task using utils::PeriodicTask::SetSettings().
Sample:
See Testsuite tasks for information about functional testing of such tasks.
A family of tools for background task execution on only one machine in a cluster. When dist lock starts, the lock worker tries to take a lock in the loop. If succeeded, a task is launched that executes the user code. In the background, dist lock tries to extend the lock. In case of loss of the lock, the user task is canceled.
The implementation has some protection against brain split (more than one instance believes that they hold the lock, and do work that requires a lock). Watchdog periodically checks whether we were able to extend the lock, and in case of an imminent loss of the lock, cancels the user task. The user task itself is responsible for stopping the work in case of cancellation (via engine::current_task::ShouldCancel()). If the task continues to work despite the cancellation, then a brain split will happen and the task will start to execute on multiple instances at the same time.
Lock implementation options:
It is well suited for:
Poorly suited if:
Sample:
See Testsuite tasks for information bout functional testing of such tasks.
If work in DoWork
takes a long time, then it is necessary to check for task cancellation at least once in lock-ttl
via engine::current_task::ShouldCancel(). Synchronization primitives engine::InterruptibleSleepFor, engine::SingleConsumerEvent, engine::Future, userver queues, requests to clients - support cancellations, that interrupt waiting when the task is canceled. After such a wait, you need to check whether the task has been canceled - synchronization primitives throw an exception or return false
from a wait operation, depending on the primitive. After engine::InterruptibleSleepFor you should call engine::current_task::ShouldCancel() manually.
In 'DoWorkyou can perform one step of the operation, or you can write a loop
while (!ShouldCancel())`. You can choose an approach based on this principle:
DoWork
, otherwise perform a single iteration.More detailed:
DoWork
, then the work has a chance to be distributed among the hosts (although there is no guarantee). If in doubt, it is better not to write a loop in ‘DoWork’.DistLocks create a single tracing::Span for the whole lifetime of worker. If a shorter Trace ID lifetime is required, then use tracing::Span::MakeSpan() to create a new Span inside the Worker::DoWork() loop iteration.
The custom coroutine that runs DoWork is always the same. If the cancellation is not processed, then another DoWork-coroutine is not launched.