NotesArticleSeat-measured

A compute bill from a one-minute cron

A one-minute cron produced 147 database transactions per minute with zero user traffic. After resume-backoff and index fixes the load fell to 24, then about 35 with Supply Audit. One-minute crons do not belong on a scale-to-zero database.

Published 2026-09-09 · Search job: database woken by cron

A database that is supposed to sleep will not sleep if a one-minute cron keeps asking it questions, even when no user is on the site.

On September 2, 2026 we measured 147 database transactions per minute on Neon, all originating from the one-minute production cron, with zero user traffic in the slice. The bill was a compute bill. This note does not publish the dollar amount. The lesson is the wake-up path.

What the cron was doing

The cron has a real job. The production spend supervisor takes a per-tick snapshot so we can catch a campaign that is serving on the exchange while Adprime Zero believes it is stopped. That check has to run even when our own columns say there is no work. Gating it on "do we think there is work" would hide the failure it exists to find.

The damage was not that one select. The damage was a 111-day auto_paused_daily_cap resume storm. Campaigns that hit their daily cap were retried every minute. There was no backoff. The same tick also asked a hard-failing conversion reporting view, event_agg, which does not exist on this seat. A 400 of that kind, retried every minute, is how a background job becomes a heater.

event_agg is not the conversion view. The real views on this seat are conversion_agg (id 6) and conversion_event_fires (id 15). The conversion report sync is now behind a default-off flag because we still do not have a saved field schema for conversion_agg. Pulling the live aggregation schema is required before that flag turns on. Otherwise we trade a view 400 for a fields 400 and keep the heater.

What changed the rate

Resume attempts now use an exponential backoff: 2 minutes, doubling, with a 6-hour ceiling. Backoff gates the resume attempt only. It must not filter the snapshot candidate list, or the supervisor stops catching exchange-active campaigns we think are stopped. Successful resume and every fresh pause reset the counter. A campaign that legitimately caps daily would otherwise inherit stale backoff and resume hours late after UTC midnight.

After those fixes, and the index work that went with them, load fell to 24 transactions per minute. Supply Audit later raised steady state to about 35. That second rise is a different job: a nightly scan plus a five-minute self-throttle so the audit does not sit on the per-minute wake-up path. The audit is the reason we did not stay at 24. It is also why we will not add another per-minute supervisor without a throttle.

The incompatibility

A one-minute cron is incompatible with Neon scale-to-zero. Scale-to-zero assumes idle. A tick that always has something to ask, even a cheap indexed select across several jobs, is not idle. We still run the minute tick because the spend supervisor's snapshot is a money check. We do not pretend the database will pause.

When you measure production load, reject any slice in which accounts or API keys were created. A parallel smoke can manufacture hundreds of transactions per minute that have nothing to do with steady state. The lesson we keep is simpler than the bill: if the cron cannot be idle, the database cannot sleep, and a 111-day retry of a permanent 400 will look like a healthy job until someone reads the transaction rate.