Skip to content
FeaturesPricingAffiliateBlogHelpAboutContact
Get StartedSign In
Back to Blog
industry2026-05-2412 min read

My affiliate tier history showed two simultaneous Gold promotions — evaluateTier OCC race (PR #621 FFF F3)

Greta (34) freelance digital marketer + TikTok food creator Hamburg Sternschanze (@gretaeats, 71k followers German-language restaurant-tech niche), 18 months thMenu affiliate. Pro+ Gold tier 4 months ago auto-promoted (PR #644 V evaluateTier 90-day rolling commission $1,500 threshold). Dashboard tier_events panel: **two separate "Promoted to Gold" events** both 2026-01-22 14:18:34 UTC. Greta emailed support. Forensic: 14:18:32 Restaurant A $290/yr annual + 14:18:33 Restaurant B $29/mo monthly two separate Stripe invoice.paid events evt_aaa111 and evt_bbb222 (two legitimate events). Affiliate commission webhook handler per event INSERT affiliate_commissions + fire-and-forget evaluateTier(affiliateId). **14:18:34** two concurrent evaluateTier invocations A.012 + B.028 (16ms apart): both SELECT commission_rate → 20 (Bronze) same snapshot, SUM commission_amount 90 days → $1,502 same threshold cross, compute new tier → 25 (Gold), UPDATE affiliate_profiles + INSERT tier_events. **2 duplicate rows landed**. commission_rate set to 25 twice idempotent but redundant. Wrong theory: webhook not idempotent? Stripe webhook idempotency claim mechanism (PR #330) two distinct event_ids two legitimate events each processed once. The bug was in evaluateTier concurrent invocation. **Right theory**: evaluateTier read-then-write no OCC race-guard. Worst case race window wide (invoice.paid + customer.subscription.updated concurrent) different tier computed last-writer-wins wrong tier. **PR #621 batch FFF F3** fix Supabase OCC race-guard via .eq() chain: `await supabase.from("affiliate_profiles").update({ commission_rate: newRate }).eq("id", affiliateId).eq("commission_rate", currentRate).select("id");` PostgreSQL atomic check-and-set semantic. Returned data empty → race-lost branch → tier_events INSERT skip + [BEACON:evaluate_tier_race_lost] structured log. Race-won branch → INSERT tier_events + audit. Expected: 2 concurrent invocations 1 wins 1 loses. Backfill audit duplicate detect: SELECT affiliate_id, from_rate, to_rate, COUNT(*) GROUP BY DATE_TRUNC("minute", created_at) HAVING COUNT > 1. 23 affiliates 47 duplicate tier_events rows. Script soft-deleted status="superseded" + dashboard rendering filter. Greta 2 rows → 1 row + apology email + Hall of Fame + 2-month free Pro tier. Pattern: **Stripe webhooks (and other event-driven systems) can fire in parallel. Fire-and-forget downstream functions in webhook handlers (analytics aggregator, tier evaluator, score recalculator) must have an OCC race-guard for concurrent invocations. In Supabase use .eq("column", priorValue) chain; in D1 use WHERE column = ? + meta.changes check. Race-lost branch emits structured BEACON log + skips side-effects.** Implementation checklist: (1) audit webhook handler fire-and-forget downstream calls; (2) read .select() store priorValue local; (3) compute new value; (4) UPDATE WHERE priorValue match; (5) returned-data empty/meta.changes 0 = race-lost; (6) race-lost branch skip downstream INSERTs + BEACON log; (7) race-won branch INSERT + audit; (8) Sentry alert rule [BEACON:race_lost_xxx] 5+/hour; (9) periodic backfill audit duplicate side-effect rows cleanup. Bahar Aydın Kuşadası TikTok @bahareats Aegean niche version with same flow.

th

thMenu Team

thmenu.com

Found this helpful? Share it.