Background jobs¶
Some work is too large for a request. Vector runs it as a job on your application’s queue:
queued actions — an action that declares
queued(), run in chunks ofchunkSize();large exports — above the resource’s
exportLimit(), up toexportQueueLimit(), and every Excel (XLSX) export;imports — every CSV import.
Each job runs as the admin who started it, reports its progress to
vector_jobs for the Jobs page, which the UI polls, and leaves that
admin a notification when it finishes, with its file when there is one.
The admin is rebuilt in the worker through the application’s
ActorResolverInterface. When they were being impersonated, the job row
remembers by whom, so the job’s log and audit rows name the
impersonator as the request’s would.
Requirements¶
kinetis/queue1.7 or later, with a backend (SQL, Redis, SQS or RabbitMQ) and a worker runningkinetis queue:work.An
ActorResolverInterfacebinding; see Authentication.
Without either, queued actions and imports answer 403 naming what is
missing, large exports are refused rather than truncated, and the
descriptor reports capabilities.queue: false, so the UI hides Jobs
and Import CSV.
Where the jobs go¶
Vector has no queue of its own:
Key |
Meaning |
|---|---|
|
The queue name Vector pushes to; |
|
A named queue connection for Vector’s jobs. Unset, the application’s own queue. |
A named connection is built by kinetis/queue’s own QueueFactory:
its backend is QUEUE_<NAME>_CONNECTION (redis, sql, sqs or
rabbitmq, with that backend’s package installed), and the backend
reads its own keys under the same name (REDIS_<NAME>_*, DB_<NAME>_*,
…). Vector builds it on the first push, once per worker, and closes it
with the application scope; the application’s own queues are untouched.
A backend the queue package cannot build refuses the push with a 403
carrying the queue package’s reason.
A worker serves the connection by name:
vendor/bin/kinetis queue:work --connection=admin --queue=vector
Whichever you choose, some worker must watch VECTOR_QUEUE on that
backend, or jobs wait as queued:
vendor/bin/kinetis queue:work --queue=default,vector
Delivery¶
A job is pushed for one attempt and claims its row with one conditional write before it starts, so a message the backend delivers twice does not run twice. A failure is final and shown on the job. A queued action must still tolerate a chunk running twice: a chunk’s flush and the progress written after it are separate statements.
A failure the admin should read — the selection is gone, the action declined, a validation refusal — is reported on the row verbatim; any other exception is logged, and the row says only that the job failed.
Stalled jobs¶
A job moves its own row, so a worker that dies mid-way leaves the row
running. A running row that has reported no progress for the minutes
the Background jobs settings group allows (30 until an admin changes
it) is answered as stalled: the Jobs page shows it so and stops
polling. kinetis vector:jobs:reap, run periodically by a scheduler,
marks such rows failed with the reason. The row itself stays running
until the reaper closes it, so a job that is in fact still running
finishes as usual.
Excel exports¶
format=xlsx on GET /vector/r/{resource}/export writes an Excel
workbook with typed cells: numbers and money as numbers (money in major
units, its currency in the header when the field has one, else from the
row’s currency), booleans as booleans, dates and date-times as real date
cells (in UTC), and everything else as text under the same formula guard
as CSV. It needs openspout/openspout (MIT) and PHP’s zip extension:
composer require openspout/openspout
Without them the descriptor’s capabilities.exportFormats is ["csv"]
and the UI offers CSV alone.
An XLSX export is always a job. openspout builds the sheet in a
temporary folder and zips it with ZipArchive on a real file path:
blocking file I/O, which a persistent request worker must not do and the
queue worker may. So XLSX is offered only when a queue runs Vector’s
jobs, the request answers 202 with the job, and the same field
selection and limits apply (exportQueueLimit()).
Imports¶
An import reads a CSV (up to 50 000 rows and 8 MB), maps columns to
fields as the user chose, and writes each row through the normal write
path: validation, policy, hooks and audit. Rows that fail are collected
into a file on the finished job, with the reason for each in an error
column.
What a row does depends on the mode:
Mode |
A row |
Needs |
|---|---|---|
Create new records |
is created |
permission to create |
Update existing records |
updates the record its key column matches; a row that matches none fails |
permission to update, and a key |
Update or create |
updates the matching record, or is created |
either permission, and a key |
The key column is the record’s ID or a field declared unique(), and
must be mapped. One indexed lookup per row runs through the resource’s
scope(), so a row can only match a record you may see in the list,
and every write goes through the writer, so the update policy, rules,
hooks and audit apply per row. The finished job reports
{created, updated, failed}; the exact contract is on REST API.
Re-importing an export with key: sku in update-or-create (upsert)
mode changes the values in place instead of duplicating rows. Map the
columns that import back — a relation exports its label, a rich-text
field its plain text — and skip the rest.
Rows are created one by one: a failed row never undoes the rows before it. To retry, fix and import the failed-rows file, not the original.
See also¶
Actions — declaring a queued action.
Limits and guarantees — every bound.