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

Out of stock push pinged me 14 times in 3 minutes — auto-86 push debounce (PR #644 V F3)

Jerome (44) runs L Astre Marseillais bouillabaisse spot on Marseille s Vieux-Port. Saturday 20:42 dinner rush Apple Watch buzzed 14 identical "📦 Out of stock: bouillabaisse rouille" pushes in 3 minutes. Manager Cecile got the same 14. Support 4-layer forensic: (1) multiple device subscriptions? 4+3 = 7 endpoints, but Jerome got 14, so theory was half-explained. (2) stock_qty hit <=0 multiple times? D1_MENU showed 6 portions at 20:38; at 20:42 SIX concurrent orders attempted to include rouille. (3) Order A first: atomic UPDATE WHERE >= 1 succeeded, stock=0, fire auto-86 push. Orders B-F concurrent: SELECT saw 0, atomic UPDATE WHERE >= 1 0 rows affected (returned insufficient_stock) BUT secondary "stock_qty <= 0" SELECT check fired in EACH handler, all 5 fired auto-86 push too. 6 concurrent fires × 7 endpoints = 42 attempted; Apple Watch rate-limiting collapsed some, but Jerome + Cecile each surfaced 14. Bug: the atomic state update and the side-effect push were separate checkpoints; concurrent race had ALL handlers hit the "stock <= 0" guard. **PR #644 batch V F3** fix: reuse the existing cron_idempotency_claims table (originally for cron dedup) — INSERT OR IGNORE on (restaurant_id, product_id, 5-min window) key. Only the winning handler fires the push; the other 5 silently skip. After 5 minutes the claim expires; if the product depletes again, a fresh push fires. Pre-fix Jerome had ~580 pushes/month (spam-heavy); post-fix ~160 (signal). Pattern: any state-trigger-driven side effect ((stock<=0)->push, (balance<=0)->alert, (logins>=5)->lockout email) must be debounced separately from the atomic state update. The state update being correct doesn t make the side effect idempotent. cron_idempotency_claims is an existing table; no new migration.

th

thMenu Team

thmenu.com

Found this helpful? Share it.