Troubleshooting
Workers Not Starting
-
Verify
shared_preload_libraries:SHOW shared_preload_libraries; -
Check PostgreSQL logs for errors
-
Ensure proper permissions on API key file
Workers Not Processing After CREATE EXTENSION
Background workers start with PostgreSQL, before the extension is
created. A worker checks for the extension on an exponential backoff,
waiting 5s, then 10s, then 20s, and so on up to a ceiling of 5 minutes.
After running CREATE EXTENSION pgedge_vectorizer, a worker discovers it
on its next check, so a database configured long before the extension was
created can wait up to 5 minutes. Reload the configuration to have the
workers check immediately.
If workers don't start processing:
-
Check the logs for messages like:
pgedge_vectorizer worker 1: extension not installed in database 'mydb', will check again in 5s (hint: run CREATE EXTENSION pgedge_vectorizer) -
Verify the extension was created in a database listed in
pgedge_vectorizer.databases:SHOW pgedge_vectorizer.databases; -
If needed, reload the configuration to reset the detection interval:
SELECT pg_reload_conf();
Slow Processing
- Increase workers:
ALTER SYSTEM SET pgedge_vectorizer.num_workers = 4; SELECT pg_reload_conf();
Workers are drawn from max_worker_processes, so raising num_workers beyond
the slots left spare there achieves nothing; the launcher simply logs that
max_worker_processes may be exhausted and carries on with what it has.
Raising max_worker_processes itself does require a restart.
- Increase batch size:
ALTER SYSTEM SET pgedge_vectorizer.batch_size = 20; SELECT pg_reload_conf();
Failed Embeddings
- Check API key is valid
- Verify network connectivity
-
Review error messages:
SELECT * FROM pgedge_vectorizer.failed_items; -
Retry failed items:
SELECT pgedge_vectorizer.retry_failed();
Provider Rate Limits
Every hosted provider limits how fast it will answer, and a queue with real work in it will reach that limit. This is ordinary traffic, not a failure, and the worker treats it as such:
- The refused items go back to
pendingtogether and are retried as one request. - The wait comes from the provider's
Retry-After, falling back to 5 seconds doubling to a minute if it sent none. - No attempt is charged, so throttling cannot exhaust an item's
max_attempts. The deferrals are counted inqueue.rate_limit_deferrals, and an item is only given up on after a hundred of them. - The worker stops sending until the wait has passed.
A queue being throttled looks like this and needs no intervention:
SELECT status, count(*), max(attempts) AS attempts,
max(rate_limit_deferrals) AS deferrals
FROM pgedge_vectorizer.queue
GROUP BY status;
Each deferral is logged with the status code and the wait taken:
LOG: pgedge_vectorizer worker for database "app": provider rate limited
(HTTP 429), deferring 22 queue items, next attempt in 4s
If deferrals climb steadily instead of clearing, work is arriving faster
than the provider's quota allows. Lower
pgedge_vectorizer.num_workers so fewer requests compete for it, raise
pgedge_vectorizer.batch_size so each request carries more, or move to a
plan with a higher limit.
Dimension Mismatch After Changing the Model
Each chunk table stores its vectors in an embedding vector(N) column,
where N is fixed when enable_vectorization() creates the table. If you
change pgedge_vectorizer.model to a model returning a different number
of dimensions, the worker cannot write the new vectors into the existing
column, and embeddings stop being produced for that table.
Nothing is corrupted when this happens. The worker compares the two dimensions before it writes, so the existing embeddings are left intact and no vector of the wrong size is ever stored.
The affected queue items move to failed rather than being retried,
because retrying cannot succeed. Run the following query to identify
them:
SELECT chunk_table, error_message, count(*)
FROM pgedge_vectorizer.queue
WHERE status = 'failed'
GROUP BY chunk_table, error_message;
An affected item reports Dimension mismatch: model=N, table=M, where N
is the dimension the configured model returned and M is the dimension
the chunk table expects. The server log carries a matching warning that
names the table.
Restoring the previous model is the quicker of the two remedies, and is the right one if the change was accidental. Substitute the model that built the table rather than the name shown here, because setting any other model leaves the dimensions mismatched:
ALTER SYSTEM SET pgedge_vectorizer.model = 'the-previous-model';
SELECT pg_reload_conf();
SELECT pgedge_vectorizer.retry_failed();
The table=M figure in the error gives the dimension the chunk table
expects, so the model you restore must be one that returns M dimensions.
Rebuilding the vectorizer keeps the new model and re-embeds the table
under it. Note that recreate_chunks() does not resolve a dimension
change, because that function deletes the rows of a chunk table without
altering the type of the column. Follow these steps instead:
-
Set the new model and reload the configuration so that the dimension detection uses the model you want.
ALTER SYSTEM SET pgedge_vectorizer.model = 'text-embedding-3-large'; SELECT pg_reload_conf(); -
Drop the vectorizer together with its chunk table, which discards the embeddings of the old dimension.
SELECT pgedge_vectorizer.disable_vectorization( 'docs', 'body', drop_chunk_table => TRUE); -
Enable vectorization again, which detects the new dimension, creates the chunk table to match, and queues every source row.
SELECT pgedge_vectorizer.enable_vectorization('docs', 'body');
Repeat those steps for every vectorized column, not just the one you noticed. The model is a single global setting while chunk tables are independent per column, so changing it affects every vectorizer whose dimension no longer matches. The following query lists them:
SELECT source_table, source_column, chunk_table
FROM pgedge_vectorizer.vectorizers
ORDER BY source_table, source_column;
Re-embedding calls the provider for every chunk in every table rebuilt, so confirm the cost against your provider's pricing before starting.