Last-Item Stock Race Condition in Mobile Ecommerce
Two users buy the last item at the same second. Without a server-side lock one gets a duplicate. The fix is a Postgres function, not client state.
Two users buy the last item at the same second. Without a server-side lock one gets a duplicate. The fix is a Postgres function, not client state.
Pain Point
Two users buy the last item at the same second and you oversell. The fix is a Postgres function that locks, decrements, and inserts atomically.
User A and User B tap "Buy" on the last item at the same second. Both payment flows succeed. Both orders go through. You oversold.
I hit this the second week my first ecommerce app was live. The only reason I caught it fast was that the item was $1,200. A refund is an annoying email. A refund for $1,200 is an angry one.
The obvious fix is to read stock on the client and block if it is zero. That fails because both clients read the same stock count before either writes. By the time the writes happen, both have already seen stock = 1.
Optimistic concurrency with a stock_version column is better but still races under load if you rely on the client for the check.
Move the buy into a Postgres function that does three things atomically:
SELECT ... FOR UPDATE.CREATE OR REPLACE FUNCTION buy_item(p_user_id uuid, p_item_id uuid)
RETURNS uuid
LANGUAGE plpgsql
AS $$
DECLARE
v_order_id uuid;
BEGIN
PERFORM 1 FROM items WHERE id = p_item_id FOR UPDATE;
UPDATE items SET stock = stock - 1 WHERE id = p_item_id AND stock > 0;
IF NOT FOUND THEN
RAISE EXCEPTION 'out_of_stock';
END IF;
INSERT INTO orders (user_id, item_id) VALUES (p_user_id, p_item_id) RETURNING id INTO v_order_id;
RETURN v_order_id;
END;
$$;
Call it from NestJS or from a Supabase RPC. Either way, stock decrement and order insert are inside one transaction.
AI App Factory ships this Postgres function pre-wired with the ecommerce template. You get the stock lock, the error codes, and the refund path for free.
AI App Factory handles the boring infrastructure so you can build the product.