Commit 635a836
fix(nuq-postgres): spread reindexes, bound maintenance with statement_timeout (firecrawl#3467)
* fix(nuq-postgres): spread reindexes per-index, bound maintenance with statement_timeout
The single daily REINDEX TABLE CONCURRENTLY on nuq.queue_scrape (and
queue_crawl_finished) at 09:00 UTC could run for 8+ hours on the live
queue, holding ShareUpdateExclusiveLock and blocking autovacuum the
whole time. With autovacuum stalled, queue_scrape accumulated 23% dead
tuples, the visibility map went stale (index-only scans were doing 5x
heap fetches), and worker pool starvation surfaced as multi-second
nuqHealthCheck (SELECT 1), nuqRenewLock and nuqGetJobToProcess
latencies.
Changes:
- Replace the two whole-table REINDEX crons with per-index
REINDEX INDEX CONCURRENTLY jobs spread across 02:00-08:20 UTC, 20 min
apart. Per-index reindex takes its lock only on the target index, and
the staggered cadence prevents any one job from stacking against the
next.
- Each reindex cron sets statement_timeout = 20min, so a stuck job
self-aborts instead of silently sitting for hours.
- Add statement_timeout to the two cleanup crons we observed hanging
in production: nuq_queue_scrape_backlog_reaper (50s) and
nuq_group_crawl_clean (4min).
- Add nuq_queue_scrape_backlog_times_out_at_idx so the per-minute
backlog reaper does an index range scan instead of a seq scan over
the whole backlog (~3M rows).
Operator note: nuq.sql only runs at initdb. Apply this change to the
running primary manually:
CREATE INDEX CONCURRENTLY IF NOT EXISTS
nuq_queue_scrape_backlog_times_out_at_idx
ON nuq.queue_scrape_backlog (times_out_at);
SELECT cron.unschedule('nuq_queue_scrape_reindex');
SELECT cron.unschedule('nuq_queue_crawl_finished_reindex');
-- then run the new SELECT cron.schedule(...) lines from this file.
Co-Authored-By: mogery <mogery@sideguide.dev>
* fix(nuq-postgres): prune cron.job_run_details hourly
pg_cron does not auto-trim cron.job_run_details and the table has no
default index on start_time, so with sub-minute jobs it grows unbounded
and any time-scoped query against it ends up seq-scanning the whole
history. Keep the last 24h.
Co-Authored-By: mogery <mogery@sideguide.dev>
* fix(nuq-postgres): make reindex crons single-statement, add watchdog
REINDEX CONCURRENTLY cannot run inside a transaction block, and pg_cron
wraps multi-statement command bodies in an implicit transaction. So the
inline `SET statement_timeout = '20min'; REINDEX INDEX CONCURRENTLY ...`
form fails with `REINDEX CONCURRENTLY cannot run inside a transaction
block`.
Drop the inline SET and add a 5-minute watchdog cron that cancels any
nuq reindex running longer than 25 minutes. The watchdog provides the
same self-aborting safety the inline timeout was meant to give, without
the tx-block restriction.
Co-Authored-By: mogery <mogery@sideguide.dev>
* fix(nuq-postgres): tighten reindex watchdog to actually cap < slot cadence
The previous watchdog (every 5 min, threshold 25 min) could cancel a
stuck reindex as late as ~30 min in, overlapping the next 20-min slot.
Run the watchdog every minute with an 18-min threshold so the worst-case
runtime is ~19 min, strictly under the 20-min cadence.
Co-Authored-By: mogery <mogery@sideguide.dev>
---------
Co-authored-by: firecrawl-spring[bot] <254786068+firecrawl-spring[bot]@users.noreply.github.com>
Co-authored-by: mogery <mogery@sideguide.dev>1 parent df4b468 commit 635a836
1 file changed
Lines changed: 54 additions & 5 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
105 | 105 | | |
106 | 106 | | |
107 | 107 | | |
| 108 | + | |
| 109 | + | |
| 110 | + | |
108 | 111 | | |
109 | 112 | | |
110 | 113 | | |
| |||
120 | 123 | | |
121 | 124 | | |
122 | 125 | | |
| 126 | + | |
123 | 127 | | |
124 | 128 | | |
125 | 129 | | |
126 | 130 | | |
127 | | - | |
128 | | - | |
| 131 | + | |
| 132 | + | |
| 133 | + | |
| 134 | + | |
| 135 | + | |
| 136 | + | |
| 137 | + | |
| 138 | + | |
| 139 | + | |
| 140 | + | |
| 141 | + | |
| 142 | + | |
| 143 | + | |
| 144 | + | |
| 145 | + | |
| 146 | + | |
| 147 | + | |
| 148 | + | |
| 149 | + | |
| 150 | + | |
| 151 | + | |
| 152 | + | |
| 153 | + | |
| 154 | + | |
| 155 | + | |
| 156 | + | |
| 157 | + | |
| 158 | + | |
| 159 | + | |
| 160 | + | |
| 161 | + | |
| 162 | + | |
| 163 | + | |
| 164 | + | |
| 165 | + | |
| 166 | + | |
129 | 167 | | |
130 | 168 | | |
131 | 169 | | |
| |||
171 | 209 | | |
172 | 210 | | |
173 | 211 | | |
174 | | - | |
175 | | - | |
176 | | - | |
| 212 | + | |
| 213 | + | |
| 214 | + | |
| 215 | + | |
| 216 | + | |
| 217 | + | |
| 218 | + | |
177 | 219 | | |
178 | 220 | | |
179 | 221 | | |
| |||
214 | 256 | | |
215 | 257 | | |
216 | 258 | | |
| 259 | + | |
217 | 260 | | |
218 | 261 | | |
219 | 262 | | |
| |||
231 | 274 | | |
232 | 275 | | |
233 | 276 | | |
| 277 | + | |
| 278 | + | |
| 279 | + | |
| 280 | + | |
| 281 | + | |
| 282 | + | |
0 commit comments