Load Precomputed Embeddings into Pinecone with a Daily Upsert Pipeline
Download a JSON export of embeddings, reshape it into Pinecone's vector format with Python, upsert the file in one call, and report the count to Slack.
Find PostgreSQL tables with autovacuum_enabled off or lax scale factors, alert Slack with dead rows, reset the setting, VACUUM and verify.
The usual advice for a large bulk load is ALTER TABLE ... SET (autovacuum_enabled = false), then switch it back after. The second step is the one that gets forgotten. From then on:
The same happens to TOAST tables, set separately with toast.autovacuum_enabled, and to tables given a scale factor so lax that vacuum almost never triggers.
This blueprint finds those tables, with their dead and live row counts, and checks that autovacuum is on for the server. It alerts Slack, and can reset the parameter and run VACUUM (ANALYZE) on each table. It then audits again to verify. Tables you switch off on purpose, such as staging tables you truncate yourself, can be allowlisted.
This blueprint was created by zkasuran.
| Finding | Severity | Rule | Fix |
|---|---|---|---|
AUTOVACUUM_OFF_SERVER |
Critical | autovacuum is off for the whole server |
Report only: change it in postgresql.conf or with ALTER SYSTEM |
AUTOVACUUM_DISABLED |
High | autovacuum_enabled off on the table, and possibly its TOAST table |
ALTER TABLE ... RESET (...), then VACUUM (ANALYZE) |
TOAST_AUTOVACUUM_DISABLED |
High | Off on the TOAST table only | RESET (toast.autovacuum_enabled), then VACUUM (ANALYZE) |
LAX_SCALE_FACTOR |
Medium | autovacuum_vacuum_scale_factor above max_scale_factor |
Report only |
ALLOWED_AUTOVACUUM_OFF |
Info | Off, but in allowed_tables |
None |
audit (io.kestra.plugin.jdbc.postgresql.Query, read-only) reads reloptions of every table and its TOAST table, the dead and live rows from pg_stat_user_tables, and the server autovacuum setting. It returns each finding with its fix, built with regclass.actionable drops the informational rows. summary logs everything.route (io.kestra.plugin.core.flow.If):alert posts to Slack with the dead row counts.remediate_gate runs only when auto_remediate is true and dry_run is false.remediate (io.kestra.plugin.core.flow.Loop) resets the parameter on each table and, with vacuum_after_reset, runs VACUUM (ANALYZE) right away, one table at a time.reaudit and verify_result fail if a table still has autovacuum off. dead_after and remediated log the dead rows now.errors alerts Slack when the audit cannot run.\d+ does not make obvious.ALTER TABLE and VACUUM.The override is set once and forgotten, because nothing checks it later. Kestra runs the check every day and keeps the allowlist next to the flow. It sends the dead row counts to Slack, resets and vacuums behind auto_remediate and dry_run, verifies the result, and keeps a history of when each table was switched off and back on.
On Kestra 2.0.5 OSS against PostgreSQL 16, with a local HTTP sink standing in for Slack:
events (600,000 rows, 200,000 dead) with autovacuum_enabled=false;order_items (320,000 live, 80,000 dead) off on the table and its TOAST table;shipments with autovacuum_vacuum_scale_factor=0.9;staging_import off and allowlisted.| Run | Inputs | Result |
|---|---|---|
| 1 | defaults | 2 high findings with their dead rows, 1 lax scale factor, the allowlisted table reported as info. Alert sent |
| 2 | auto_remediate: true, dry_run: true |
Same report. events still has {autovacuum_enabled=false} |
| 3 | auto_remediate: true, dry_run: false |
Parameters reset on both tables and the TOAST table, VACUUM (ANALYZE) run, verified. pg_class shows no options left, and pg_stat_user_tables shows 0 dead rows and a recorded vacuum |
| 4 | defaults | Only the lax scale factor left, report only |
| 5 | after resetting the scale factor | Clean, with 1 allowlisted table |
| 6 | ALTER SYSTEM SET autovacuum = off |
Critical server finding |
| 7 | unreachable database | errors alert sent |
docker run -d --name pg -p 5432:5432 -e POSTGRES_PASSWORD=pg postgres:16;ALTER TABLE t SET (autovacuum_enabled = false) on a table, then update or delete some rows.POSTGRES_USERNAME / POSTGRES_PASSWORD: credentials used by every query.SLACK_WEBHOOK_URL: used by alert and the errors block.SECRET_.| Input | Default | Purpose |
|---|---|---|
jdbc_url |
jdbc:postgresql://localhost:5432/app |
Database |
allowed_tables |
[public.staging_import] |
Tables allowed to have autovacuum off |
max_scale_factor |
0.4 |
Highest acceptable vacuum scale factor |
vacuum_after_reset |
true |
Vacuum right after the reset |
auto_remediate |
false |
Allow ALTER TABLE and VACUUM |
dry_run |
true |
Must be false for a statement to run |
POSTGRES_USERNAME, POSTGRES_PASSWORD and SLACK_WEBHOOK_URL secrets.jdbc_url and allowed_tables.auto_remediate: true and dry_run: false outside peak hours, since VACUUM reads the whole table.daily trigger, which always runs in safe mode.age(relfrozenxid)) to rank the findings by urgency.autovacuum_analyze_scale_factor and autovacuum_vacuum_cost_delay overrides.