Skip to content

Commit 0eb338f

Browse files
LEANDERANTONYclaude
andcommitted
Refresh worker: chunk_size 30 globally (was per-source)
Commit `18fde26` set ashby chunk_size to 30 (down from 100) to dodge Supabase's default 60 s `statement_timeout` on the service_role REST path. Greenhouse + Lever + Workday were left at 100 because they weren't hitting the wall at the time. Tonight's 00:00 cron tick logs show greenhouse + lever now ALSO hitting `canceling statement due to statement timeout` at chunk_size=100: Failed to upsert chunk for greenhouse: ... statement_timeout Failed to upsert chunk for greenhouse: ... statement_timeout Failed to upsert chunk for lever: ... statement_timeout Failed to upsert chunk for lever: ... statement_timeout Failed to upsert chunk for lever: ... statement_timeout The cause is just table growth — cached_jobs is now ~13 K rows, the GENERATED `search_tsv` tsvector column has to be re-derived on every insert, and the per-chunk index churn at 100 rows finally crossed the 60 s budget. ~100-200 rows per refresh were silently dropping across the two sources. Fix: drop chunk_size to 30 across all sources. ~3-4× more HTTP roundtrips per refresh (extra ~250 calls), each one comfortably under the statement_timeout. Total refresh duration largely unchanged because the upstream ATS-API fetches dominate the wall-clock time. Also rewrote the comment to record this trajectory so the next person who looks at chunk sizing sees the history without having to dig through git blame. (Cron schedule was changed from `*/30 * * * *` to `0 */4 * * *` in Supabase — six refreshes per day is plenty for cached job listings, no need to slam the four ATS providers every 30 min. The change is in pg_cron, not in this repo.) Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent 23212f1 commit 0eb338f

1 file changed

Lines changed: 22 additions & 16 deletions

File tree

backend/services/job_cache_service.py

Lines changed: 22 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -138,24 +138,30 @@ def refresh_cached_jobs(
138138
providers_report[source_name] = provider_report
139139
continue
140140

141-
# Upsert in chunks. 100 rows per request keeps each transaction
142-
# short enough to avoid Supabase throttling on the service-role
143-
# endpoint — earlier 200-row chunks intermittently failed
144-
# mid-refresh after sustained writes (likely the supabase REST
145-
# tier's per-connection write budget). 100 is a good middle
146-
# ground for the lighter-payload sources.
141+
# Upsert in chunks. The cached_jobs table has a GENERATED
142+
# STORED `search_tsv` tsvector column that gets re-derived on
143+
# every row insert plus index churn on every chunk; that work
144+
# has to fit inside Supabase's default 60 s
145+
# `statement_timeout` for the service_role REST path.
147146
#
148-
# Ashby is the exception: its postings carry much larger
149-
# description bodies, and the GENERATED STORED `search_tsv`
150-
# column has to be re-derived on every row insert. At
151-
# chunk_size=100 we observed five consecutive statement
152-
# timeouts per refresh ("canceling statement due to statement
153-
# timeout") on Supabase's default 60 s `statement_timeout`,
154-
# silently losing ~500 rows. chunk_size=30 finishes each
155-
# chunk in well under 60 s; total Ashby refresh goes from
156-
# ~18 requests to ~60, but every row lands.
147+
# Sized history:
148+
# - 200 → intermittently failed after sustained writes
149+
# - 100 → fine for greenhouse + lever + workday early on,
150+
# but Ashby (heavier descriptions) was hitting the
151+
# timeout consistently, dropping ~500 rows per refresh.
152+
# We patched Ashby alone to 30 and kept the others at
153+
# 100 in commit 18fde26.
154+
# - As the table grew past 12 K rows, indexes/tsvector
155+
# work scaled, and at 13 K rows greenhouse + lever
156+
# started hitting the 60 s wall too at chunk_size=100,
157+
# silently dropping ~100-200 rows per refresh.
158+
#
159+
# 30 across all sources is the conservative resolution.
160+
# Trade-off: ~3-4× more HTTP roundtrips per refresh (an
161+
# extra ~250 calls), but each one comfortably finishes
162+
# inside the statement_timeout, so every row lands.
157163
if all_postings:
158-
chunk_size = 30 if source_name == "ashby" else 100
164+
chunk_size = 30
159165
for i in range(0, len(all_postings), chunk_size):
160166
chunk = all_postings[i : i + chunk_size]
161167
try:

0 commit comments

Comments
 (0)