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().
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:
-- 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_idslist 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.