Skip to content

Retry dead jobs ​

Permanent failures and exhausted retries land in postvec.jobs_dead. Read last_error, fix the cause, then re-drive with retry_dead().

Job lifecycle, from trigger to dead letterA trigger creates a pending job; the worker claims it and either writes the vector, returns it to pending after a transient failure and backoff, or dead-letters it once the failure is permanent or the retries are exhausted. Dead jobs leave that state only when retry_dead is called.TRIGGERCLAIMVECTOR SETTRANSIENT · BACKOFFPERMANENT / SPENTretry_dead()pendingpostvec.jobsclaimedworker holds itjobs_deadnever retried alonewritten
sql
SELECT dead_id, pk_value, last_error
  FROM postvec.jobs_dead;

Resolve the condition named by last_error, such as a missing model, oversized input or unloaded converter. Then run:

sql
-- every dead row for this entry
SELECT postvec.retry_dead('public.docs', 'body');

-- a specific set
SELECT postvec.retry_dead(
  'public.docs', 'body', ARRAY[17, 23]::bigint[]
);

Expected

The return value is the number of dead rows consumed. Several dead rows can share a PK; an already-pending job coalesces. Those rows leave jobs_dead.

retry_dead() takes regclass, so the invoking role's search_path resolves the table before the definer body runs. 'public.docs' is unambiguous. refresh_lexical_stats() declares relation the same way.

Refusals ​

  • A mixed dead_ids list is validated atomically before locking; no locks are acquired if any identifier is invalid or belongs to another entry.
  • The entry must be present, enabled and idle (no live migration).
  • The new job starts clean: previous error and attempt state stay on the dead row that was consumed.

Authorization uses the invoking role (SET ROLE counts). jobs_dead is PUBLIC-SELECT; the re-drive function is the gated write.

Concurrent calls serialize: one consumes the rows, and the other reports that they are no longer present.

Managed PostgreSQL ​

On managed PostgreSQL the schema is plpgsql and retry_dead() behaves as above. postvec-server exposes the same call as POST /admin/managed/{name}/retry-dead with {"registry_id":1,"dead_ids":[123]}; omitting dead_ids retries the entry's eligible dead letters. The server runs the same function against the database, so the validation and the dead-row accounting above are unchanged.

Re-driven jobs are embedded by the postvec-server sync worker, in the same queue, with the same retry and dead-letter rules. The model fleet is the one configured on the server host.

Direct queue writes ​

Re-drive through retry_dead()

Copying rows from postvec.jobs_dead into postvec.jobs skips validation, deduplication and the ownership check. retry_dead() is the supported path.