Finding jobs¶
One filter describes "which jobs" everywhere Awa answers the question: the
admin UI's jobs page and DLQ page, awa job list and awa dlq list, Python's
find_jobs, and batch operations' preview and execution
(ADR-049). The same filter selects
the same jobs on every one of them.
Finding jobs needs the queue-storage engine. On the deprecated canonical engine (ADR-037) these calls return an error naming the missing queue_storage backend. The exception is a fresh install that holds no jobs yet: listing and counting there return nothing, and batch operations are still refused.
The filter¶
| Field | Meaning |
|---|---|
kind |
The job's kind. |
queue |
The job's queue. |
state |
One state, or a list: any of them. |
tag |
One tag, or a list: all of them (at most 20). |
dead_lettered |
true for only dead-lettered jobs, false for none of them. |
ids |
Only these job ids (at most 1,000). |
created_at_gte, created_at_lt |
The original insert time, unchanged by retries and moves. |
A field that is not set does not narrow the population. Unknown fields are an
error, so a typo cannot widen what a purge or batch operation acts on. The
same holds for the query parameters of GET /api/jobs, /api/jobs/count and
/api/dlq, where an empty value is an error too. In Rust the lists are the
states and tags fields.
Listing and counting share one 30-second budget per request, across every pass over candidates and every load of their rows. A filter that needs longer fails with an error asking to narrow it. Batch operations scan without that budget, since a failed scan fails the operation.
A DLQ purge or bulk retry, and a batch operation, needs a field that picks
out particular jobs (kind, queue, tag, ids or a created bound), or
allow_all. A state alone isn't enough: it still names every job in it. Over HTTP the full filter goes
to POST /api/dlq/retry-matching or /api/dlq/purge-matching:
{"filter": {"queue": "billing", "tag": ["tenant:acme"]}}, or
{"filter": {}, "all": true} for the whole DLQ.
Each job appears once, in its current state. Listing is newest first; page by
passing the last id you saw as before_id. A job whose claim is in flight
can be missed or shown in its previous state, and a job can change state
between pages.
Tags are how you find "jobs about X"¶
Attach a tag when you enqueue, then find every retained job carrying it, in any state:
On queue storage, tags are indexed by a partial GIN index on each partition where a job's row outlives its claim. Untagged jobs add nothing to it.
A few things a tag cannot do:
- It finds only jobs whose producers attached it. "Never tagged", "pruned" and "never inserted" all look like an empty result.
- It lives as long as the job's rows. Retention prunes both together. History beyond retention belongs in change data capture or traces.
- It is not private. Tags show in the admin UI and logs and are indexed.
Use an opaque identifier such as
order:123, never an email address or a secret.
A job may carry at most 20 tags.
Counting¶
count_jobs (HTTP GET /api/jobs/count) counts exactly up to 10,000 and
reports more as "10,000+". It reads the whole matching population, so the
admin UI fetches it when the filter changes rather than on every poll.
From a search to a batch operation¶
On the jobs page, a filter that can match available or scheduled jobs offers Change priority or queue of matching jobs, which opens the batch operations form with the same filter. Batch operations act only on available and scheduled jobs, so the preview counts those; running, waiting and finished jobs in the same filter are left alone.
A batch operation whose filter uses several tags, several states or
dead_lettered is refused while a runtime older than 0.7.0-alpha.2 is live:
an older runner can't read them. A 0.6 server or runner refuses a list under
tag or state rather than acting on more jobs.
Building the tag indexes¶
New queue-storage schemas, and the empty partitions of existing ones, get their tag indexes when they are prepared or migrated. For populated partitions, run as the migrator (the role that owns Awa's tables):
awa storage build-tag-index # one pass; ring partitions are indexed once pruning empties them
awa storage build-tag-index --wait # keep passing until every index exists
awa storage build-tag-index --now # build populated partitions now, CONCURRENTLY
--now holds back vacuum while each index builds and makes pruning of that
partition report Blocked until it finishes. Each concurrent build also
waits for every transaction that started before it, however long that takes. The build is safe to interrupt
and to repeat; an index an interrupted build left invalid is rebuilt.
awa storage status reports how many indexes exist under tag_indexes. Until they all do, tag
lookups still work and scan the partitions that have no index.