Design Philosophy
This section explains how Taskurai handles tasks.
Overall Design
Taskurai is designed with the following principles in mind:
- Decoupling tasks from APIs to increase scalability and tolerance for failures.
- Load leveling.
- Processing background tasks at scale.
- Handling non-critical tasks in an asynchronous pattern:
- A critical task could be saving an order to the database (could be handled by the API, that then creates one or more non-critical tasks).
- A non-critical task could be creating an order confirmation PDF, sending emails, etc.
The plans (Start, Go, Pro) are not set up to support business-critical scenarios where the following cases are important:
- Guaranteed FIFO pattern
- At-Most-Once (At-Least-Once is supported)
- Atomic operations (Stock handling, bank transactions, etc.)
Task Order
Tasks are started in the order of creation (FIFO). However, a few factors can influence this behavior:
- Workers can be scaled horizontally and support concurrent tasks in the worker. Execution order cannot be guaranteed this way.
- When a worker retrieves a new task, this task is made invisible for a set time. When the worker cannot handle the task on time (for example, due to a crash), the task will reappear to be visible to other instances of the worker. The order is not guaranteed this way.
- The number of instances is a target amount, not a guarantee. Limiting a worker to only one instance will not guarantee that only one instance is running. For example, during the deployment of a new worker, both the old and new instances may be running concurrently for a short period of time.
Delivery Guarantee
Taskurai delivers a task At-Least-Once.
Each worker leases a task for a period of time. You should configure the worker in such a way that the visibility timeout is suitable for the initial inspection of the task.
The worker can extend the lease multiple times when performing work for the task.
When a worker crashes and you have configured a suitable timeout, another instance of the worker can quickly process the task again.
Since a task can be processed more than once (after a crash or timeout), your commands should be designed to be idempotent.
For example, if your command is to generate an order confirmation PDF, only one file should be created by your code. Each run should produce the same results. In some cases, it could be sufficient to overwrite the same file.
You can keep track of the status of the running task by storing progress, output, and results on the task itself.