Tasks
A task is a unit of asynchronous work. Tasks run independently of the request/response flow that applies to standard Reboot method execution. Tasks are typically used for long-running operations, operations whose execution should be deferred to a later time, and/or operations that perform side effects external to Reboot.
Any Reboot method can be invoked as a task by using
schedule()
or spawn()
, followed by a call to the method.
Scheduling and Spawning
schedule()
schedule()
can be used from within a writer
or
transaction
method. It returns the ID of a
task that hasn't been started yet. This is because the creation of a task is an
effect, and effects only occur after the method that invokes schedule()
completes. (If the method's execution fails at any point, its scheduled tasks
will not be run.)
This means that the method that invokes schedule()
cannot wait for the
execution of the scheduled task to complete or obtain the response value
produced by the task. If you would like to do that, you should use spawn()
inside a workflow
method instead.
Here's an example:
- Python
- TypeScript
task_id = await self.ref().schedule().WelcomeEmail(context)
const taskId = await this.ref().schedule().welcomeEmail(context);
spawn()
spawn()
can be used from within a workflow
method; it can also be invoked from outside
Reboot using an
ExternalContext
. Calling spawn()
returns a [MethodName]Task
object that
represents the running task. You can use await
on the task object to get the
response (i.e., the return value of the method executed by the task).
For example, if spawn()
is used to run a workflow
method named
WelcomeEmail
, spawn()
returns an instance of the (generated)
WelcomeEmailTask
class.
- Python
- TypeScript
task = await self.ref().spawn().WelcomeEmail(context)
response = await task
const { task } = await this.ref().spawn().welcomeEmail(context);
const response = await task;
Task execution
Reboot automatically manages the execution of tasks. If a task encounters an unexpected error, Reboot will re-execute the task until it either completes successfully or aborts with a declared error. As a result, task methods should be designed to behave correctly even if they are executed multiple times. For example, if a task performs side-effects outside of Reboot, the developer should decide whether the side-effect should be re-executed every time the task is run ("at least once"), or only the first time ("at most once"). Reboot provides several helpers to facilitate this; see the workflow documentation for more detail.
Running a task at a specific time in the future
If no arguments are passed to schedule()
or spawn()
, the task will
be started as soon as possible in the background.
You can alternatively pass the when
argument to delay the task's execution
until a specific time in the future.
Similar to a browser's setTimeout
, work isn't guaranteed to start exactly
when
time has elapsed but sometime shortly thereafter, when resources are
available to handle the request.
- Python
- TypeScript
await self.ref().schedule(
when=timedelta(seconds=request.quote_expiration_seconds),
).ExpireQuote(
context,
quote=quote,
)
await this.ref().schedule({
when: new Date(Date.now() + request.quoteExpirationSeconds * 1000)
}).expireQuoteTask(context, { quote })
In Python, you can pass either a datetime
to identify a specific point in
time or a timedelta
to represent an amount of time in the future.
Retrieving a task from a task ID
If all you have is a TaskId
, you can use retrieve()
to get a
[MethodName]Task
. If desired, you can then await
it to get the response.
- Python
- TypeScript
response = await Account.WelcomeEmailTask.retrieve(
context,
task_id=welcome_email_task_id,
)
const response = await Account.WelcomeEmailTask.retrieve(context, { taskId });
Unexpected task errors and hotfixing
If a task encounters an unexpected error, it will be retried until it eventually runs to completion. Learn more about expected and unexpected errors.
Tasks that are failing to complete due to errors can be "hotfixed" by deploying a new version of the app. The next time the task runs, it will use the fixed code.
Hotfix code in development with ⌘s
. Saving a new version rebuilds your app
automatically. Learn more about rbt dev watch
.
Learn how to hotfix an app in production by deploying a new version.
rbt task
Task administration, such as listing and cancelling tasks, can be done through the Reboot CLI.
list
To get a list of all tasks currently executing, run e.g.
rbt task list --application-url=http://localhost:9991
. Note, your
application-url
may be different.
To see tasks in production, you will need to pass a few additional flags; see
rbt task list -h
for more details.
cancel
To cancel a specific task, use rbt task cancel
. To cancel a task, in
addition to the --application-url
, you must pass flags to identify
the task by its --state-type
, --state-id
, and --task-uuid
. These values
can be found by first running rbt task list
.