Submit and manage Tasks
Submit a durable Task, select its resources, follow its status, and cancel it.
Submit an operation as a Task when it must keep
going after your program exits, retry after a Worker failure, or run on more
compute than your computer has. This guide submits Table maintenance, which
compacts the frames Table on the main branch of the demo Project. The
same requests work for every
built-in Task.
Prerequisites
- You have the Project
editorrole, and the permissions that the specification needs on its Resources. - The Project has Worker capacity for the specification. See Worker Pools.
Find the branch and Table IDs
Table maintenance names the branch and the Table by ID, not by name. Look both up in the Catalog. First read the branch:
curl "https://api.spiraldb.dev/api/catalog/projects/demo/branches/by-name?name=main" \
-H "Authorization: Bearer $SPIRAL_TOKEN"Save branchId as $BRANCH_ID and headCommitId as $COMMIT_ID. Then
resolve the Table's path at that commit:
curl -X POST "https://api.spiraldb.dev/api/catalog/projects/demo/commits/$COMMIT_ID/namespace/resolve" \
-H "Authorization: Bearer $SPIRAL_TOKEN" \
-H "Content-Type: application/json" \
-d '{"path": ["frames"]}'Save resource.descriptor.resourceId as $TABLE_RESOURCE_ID. The IDs do not
change when the branch moves or the Table is written, so you can store them.
Submit a Task
A submission carries a definition of the Task, optional resource options, and
an idempotency key. The definition's type names the kind of Task, and its
fields name the Resources it works on. The key makes the request safe to
retry: if you send the same request again with the same key, Spiral returns
the same Task instead of creating a second one. Reusing a key with a
different definition fails with a conflict, because it most likely means two
different operations got the same key. Each SDK's submit_task (submitTask
in TypeScript) returns a handle that reads the Task's progress and waits for
its result, and picks a new key for each call.
definition = TasksTaskDefinition(
table_maintenance=TasksTableMaintenanceDefinition(
read_target=TasksTaskReadTarget(latest_on_branch=TasksTaskReadTargetLatestOnBranch(branch_id=branch_id)),
table_resource_id=table_resource_id,
)
)
options = TasksTaskOptions(resource_profile="cpu_4")
task = client.project("demo").submit_task(definition, options=options)
if task is None:
print("skipped: another maintenance Task already waits for this Table")
return
print(f"accepted {task.id}")
progress = task.progress()
if progress is not None:
print(f"{progress.percentage}% done")
print(f"published {len(task.wait().versions)} versions")The response has a type and a matching payload:
| Outcome | Meaning |
|---|---|
accepted | Spiral created the Task, or returned the Task for this idempotency key. The response contains accepted.task with its taskId and status. |
skipped | Another Task for the same Table is already waiting, so there is no need for this one. The response has no Task. See When two Tasks overlap. |
Select resources
The options object controls execution:
| Option | Value |
|---|---|
resourceProfile | cpu_1, cpu_2, cpu_4, cpu_8, or cpu_16. It must be in the specification's allowed list. Without it, the Task uses the default profile. |
maxAttempts | The largest number of Attempts. |
deadlineAtMicros | A deadline in microseconds since the Unix epoch. If the Task has not finished by then, Spiral stops it and marks it failed with deadline_exceeded. |
Read each specification's default and allowed profiles:
curl https://api.spiraldb.dev/api/tasks/projects/demo/task-specs \
-H "Authorization: Bearer $SPIRAL_TOKEN"You cannot ask for custom CPU or memory values; select a profile instead. See Choosing a Worker size.
Follow the status
Read the Task, its Attempts, and the Resources that it references:
details = client.tasks.read_task(task_id)
print(f"{details.task.task_spec_id.name} ran {details.task.attempt_count} attempts")
if details.task.status in ("succeeded", "failed", "cancelled"):
print("already finished")
else:
task = client.tasks.cancel_task(task_id)
print(f"cancellation requested for {task.task_id}")To find Tasks, list them with filters such as status, taskSpecName,
resourceId, origin, and pageSize. Pass nextPageToken from a response as
pageToken to read the next page:
curl "https://api.spiraldb.dev/api/tasks/projects/demo/tasks?status=running&pageSize=20" \
-H "Authorization: Bearer $SPIRAL_TOKEN"GET /api/tasks/projects/demo/task-stats returns the count of ready and running Tasks.
Cancel a Task
curl -X POST https://api.spiraldb.dev/api/tasks/$TASK_ID/cancel \
-H "Authorization: Bearer $SPIRAL_TOKEN"In an SDK, call cancel_task (cancelTask in TypeScript), as the example in
Follow the status shows.
Cancelling asks the Worker to stop; it does not undo work. If the Worker
already published its change, the Task ends succeeded instead of
cancelled. Either way, no new Attempt starts after the request. See
What happens when things fail.
Troubleshooting
HTTP errors use the shared error envelope.
| Error | Cause | Fix |
|---|---|---|
Tasks:InvalidArgument | The definition is invalid, or the profile is not allowed. | Read parameters.reason. |
Default:PermissionDenied | You do not have a permission that the Task needs on a Resource. | Read operation and resource, then ask a Project admin for a role. |
Tasks:Conflict | The idempotency key was used with another input. | Use a new key for a new submission. |
The Task stays ready | No compatible Worker can claim it. | Turn on managed Workers, or start a Worker that runs the specification. See Worker Pools. |
Where next: automate the Task on a schedule or after each Table change.