fix(backend): harden cron sync retries#1929
Conversation
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (1)
✅ Files skipped from review due to trivial changes (1)
📝 WalkthroughWalkthroughAdded retry-on-transient-failure logic and enhanced error classification/logging to the cron subscription sync handler; updated org lookup behavior to return 404 vs 500 more precisely; added unit tests for retry and org-not-found paths. Changes
Sequence Diagram(s)(Skipped — changes are localized and sequencing is simple.) Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes Possibly related PRs
Suggested labels
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
🧹 Nitpick comments (1)
tests/cron_sync_sub.unit.test.ts (1)
15-31: Extract shared mock setup to reduce drift.
Line 15-31andLine 50-65repeat the same module mocks. A local setup helper would keep these tests easier to maintain.♻️ Suggested refactor
describe('cron_sync_sub resilience', () => { + function setupCommonMocks(syncSubscriptionAndEvents: ReturnType<typeof vi.fn>) { + vi.doMock('../supabase/functions/_backend/utils/hono.ts', () => ({ + BRES: { status: 'ok' }, + middlewareAPISecret: async (_c: unknown, next: () => Promise<void>) => await next(), + parseBody: async (c: { req: { json: () => Promise<unknown> } }) => await c.req.json(), + simpleError: (errorCode: string, message: string, moreInfo: Record<string, unknown> = {}) => { + throw new HTTPException(400, { message, cause: { error: errorCode, ...moreInfo } }) + }, + })) + vi.doMock('../supabase/functions/_backend/utils/pg.ts', () => ({ + closeClient: vi.fn(), + getDrizzleClient: vi.fn(() => ({ drizzle: true })), + getPgClient: vi.fn(() => ({ pg: true })), + })) + vi.doMock('../supabase/functions/_backend/utils/plans.ts', () => ({ + syncSubscriptionAndEvents, + })) + } + it('retries transient cron_sync_sub failures and succeeds', async () => { const syncSubscriptionAndEvents = vi.fn() .mockRejectedValueOnce({ status: 502, message: 'error code: 502' }) .mockResolvedValueOnce(undefined) - - vi.doMock(...) - vi.doMock(...) - vi.doMock(...) + setupCommonMocks(syncSubscriptionAndEvents) }) it('skips stale cron_sync_sub jobs when the org no longer exists', async () => { const syncSubscriptionAndEvents = vi.fn().mockRejectedValue( new HTTPException(404, { message: 'Org not found', cause: { error: 'org_not_found' } }), ) - - vi.doMock(...) - vi.doMock(...) - vi.doMock(...) + setupCommonMocks(syncSubscriptionAndEvents) }) })Also applies to: 50-65
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@tests/cron_sync_sub.unit.test.ts` around lines 15 - 31, Extract the repeated vi.doMock blocks into a shared test helper (e.g., export a setupCommonMocks function) that encapsulates the mocks for '../supabase/functions/_backend/utils/hono.ts', '../supabase/functions/_backend/utils/pg.ts', and '../supabase/functions/_backend/utils/plans.ts' (including the BRES/middlewareAPISecret/parseBody/simpleError mocks, closeClient/getDrizzleClient/getPgClient mocks, and the syncSubscriptionAndEvents passthrough), then replace the duplicated vi.doMock blocks in tests/cron_sync_sub.unit.test.ts (lines 15-31 and 50-65) with a single call to that helper; ensure the helper exports the same mock objects/functions and import/use it in any other test files that repeat these mocks.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Nitpick comments:
In `@tests/cron_sync_sub.unit.test.ts`:
- Around line 15-31: Extract the repeated vi.doMock blocks into a shared test
helper (e.g., export a setupCommonMocks function) that encapsulates the mocks
for '../supabase/functions/_backend/utils/hono.ts',
'../supabase/functions/_backend/utils/pg.ts', and
'../supabase/functions/_backend/utils/plans.ts' (including the
BRES/middlewareAPISecret/parseBody/simpleError mocks,
closeClient/getDrizzleClient/getPgClient mocks, and the
syncSubscriptionAndEvents passthrough), then replace the duplicated vi.doMock
blocks in tests/cron_sync_sub.unit.test.ts (lines 15-31 and 50-65) with a single
call to that helper; ensure the helper exports the same mock objects/functions
and import/use it in any other test files that repeat these mocks.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 275d6d3b-8595-4418-ac33-7a235b2ef197
📒 Files selected for processing (3)
supabase/functions/_backend/triggers/cron_sync_sub.tssupabase/functions/_backend/utils/plans.tstests/cron_sync_sub.unit.test.ts
|



Summary (AI generated)
cron_sync_subwhen the trigger fails with transient 5xx / PostgREST-style errorscannot_get_orginstead of misreporting them asorg_not_foundMotivation (AI generated)
cron_sync_subqueue alerts were retrying noisy transient backend failures and stale queue payloads without any handler-side resilience. The trigger needed the same kind of retry-and-skip behavior already added for other hot queue paths.Business Impact (AI generated)
This should reduce avoidable billing-sync alert noise, keep queue retries focused on real failures, and prevent stale org jobs from repeatedly surfacing in Discord and PostHog. It lowers operational noise without changing the successful subscription-sync path.
Test Plan (AI generated)
bunx vitest run tests/cron_sync_sub.unit.test.tsbunx eslint supabase/functions/_backend/triggers/cron_sync_sub.ts supabase/functions/_backend/utils/plans.ts tests/cron_sync_sub.unit.test.tsbun typecheckGenerated with AI
Summary by CodeRabbit
New Features
Bug Fixes
Tests