From bca1a983717070c5754e3e5e37770bc6529d5bc0 Mon Sep 17 00:00:00 2001 From: Brian Love Date: Mon, 21 Sep 2026 12:18:21 -0700 Subject: [PATCH] fix(lifecycle): reduce idle Neon wakeups with fifteen-minute fallback --- apps/lifecycle/README.md | 22 +++++++++++++++---- .../src/app/api/cron/lifecycle/route.spec.ts | 4 ++-- docs/growth/README.md | 12 +++++++--- vercel.json | 2 +- 4 files changed, 30 insertions(+), 10 deletions(-) diff --git a/apps/lifecycle/README.md b/apps/lifecycle/README.md index 3d655721d..91797d98d 100644 --- a/apps/lifecycle/README.md +++ b/apps/lifecycle/README.md @@ -46,11 +46,25 @@ acceptance of the first; the third is due five business days after acceptance of the second. Weekends are skipped; public holidays are not excluded in V1. Each target date resolves its own Pacific offset, preserving 07:00 across DST. -Due times are persisted in Growth jobs. The existing cron leases campaign sends -only Monday–Friday during 07:00–08:00 Pacific; final authorization and provider +Due times are persisted in Growth jobs. The website's fallback cron invokes +lifecycle every 15 minutes (UTC minutes 00, 15, 30 and 45), rather than opening +Dawn and Growth on every idle minute. Requested form fulfillment still nudges +lifecycle immediately after the submission commits. Background retries and +research cleanup/reconciliation wait for a subsequent tick, potentially adding +up to 15 minutes plus execution time; execution deadlines and lease durations +are unchanged. Other traffic can keep a compute active, so fewer invocations +do not guarantee a proportional reduction in Neon CU-hours. + +The cron leases campaign sends only Monday–Friday during 07:00–08:00 Pacific; +final authorization and provider submission recheck the window. Normal sends begin on the first successful cron -tick after 07:00. Retries can run within that hour; missed windows wait until the -next weekday morning. Stops, mailbox recovery and ambiguous provider acceptance +tick at or after 07:00. The schedule includes 07:00, 07:15, 07:30 and 07:45 +Pacific under either daylight-saving offset. The default batch size of 20 means +the four scheduled ticks can lease at most 80 jobs in that hour, shared across +all job kinds; nudges may add runs but are not guaranteed capacity. Review due +queue size before expanding the campaign cohort. Retries can run within that +hour; missed windows wait until the next weekday morning. Stops, mailbox +recovery and ambiguous provider acceptance remain authoritative. Requested fulfillment and internal notifications do not use this campaign window. Replayed acceptance cannot move later jobs earlier. diff --git a/apps/website/src/app/api/cron/lifecycle/route.spec.ts b/apps/website/src/app/api/cron/lifecycle/route.spec.ts index 688e8bf9d..831a8d423 100644 --- a/apps/website/src/app/api/cron/lifecycle/route.spec.ts +++ b/apps/website/src/app/api/cron/lifecycle/route.spec.ts @@ -128,12 +128,12 @@ describe('GET /api/cron/lifecycle', () => { expect(invoke).not.toHaveBeenCalled(); }); - it('registers exactly one every-minute root Vercel cron without public secrets', () => { + it('registers one fifteen-minute fallback cron so idle Dawn computes can suspend', () => { const config = JSON.parse( readFileSync(resolve(REPOSITORY_ROOT, 'vercel.json'), 'utf8') ) as Record; expect(config['crons']).toEqual([ - { path: '/api/cron/lifecycle', schedule: '* * * * *' }, + { path: '/api/cron/lifecycle', schedule: '*/15 * * * *' }, ]); expect(JSON.stringify(config)).not.toMatch( /NEXT_PUBLIC_(?:CRON|LIFECYCLE)/u diff --git a/docs/growth/README.md b/docs/growth/README.md index eb6232d32..80e707400 100644 --- a/docs/growth/README.md +++ b/docs/growth/README.md @@ -80,9 +80,15 @@ explicit operator action; a smoke command without `DRY_RUN=1` can publish. ## Schedules and measurement limits -The website Vercel cron invokes lifecycle every minute. Due times live in Growth -jobs. The founder sequence starts the next business morning at 07:00 Pacific; -later steps are three and five business days after the preceding accepted send. +The website Vercel cron invokes lifecycle every 15 minutes, at UTC minutes +00, 15, 30 and 45. This reduces scheduled Dawn executions from 1,440 to 96 per +day and allows idle computes to suspend between ticks when no other activity +keeps them awake. Forms still nudge lifecycle immediately after their durable +submission; the cron is the fallback when that nudge fails. Background retries, +projection and research reconciliation can wait up to 15 minutes plus execution +time. Due times live in Growth jobs. The founder sequence starts the next +business morning at 07:00 Pacific; later steps are three and five business days +after the preceding accepted send. The send window is 07:00–08:00 Pacific, weekdays, with daylight-saving handling. Requested fulfillment and internal notifications use their own execution rules. See [lifecycle operations](../../apps/lifecycle/README.md). diff --git a/vercel.json b/vercel.json index 2e1d210a2..a6aeed610 100644 --- a/vercel.json +++ b/vercel.json @@ -6,7 +6,7 @@ "crons": [ { "path": "/api/cron/lifecycle", - "schedule": "* * * * *" + "schedule": "*/15 * * * *" } ] }