1"""Chapter 42 (case study: e-commerce cart and checkout, Amazon / Flipkart) deep-dive code.
21. Cart storage: guest cart merged into the user's cart at login.
32. Inventory reservation: conditional decrement vs oversell; reservations that expire.
43. Idempotent order creation.
54. Payment hand-off: an order state machine, including a payment that arrives late.
65. The checkout saga with compensations.
7Run: python3 ch42_checkout.py"""
8import threading
9
10
11# ---- 1. carts ----------------------------------------------------------------------
12
13def merge_carts(user_cart, guest_cart, max_qty=10):
14 merged = dict(user_cart)
15 for sku, qty in guest_cart.items():
16 merged[sku] = min(max_qty, merged.get(sku, 0) + qty)
17 return merged
18
19
20def reprice(cart, prices_at_add, current_prices):
21 """At checkout, show what changed since the item was added."""
22 return {sku: (prices_at_add[sku], current_prices[sku]) for sku in cart
23 if prices_at_add[sku] != current_prices[sku]}
24
25
26# ---- 2. inventory ------------------------------------------------------------------
27
28class Inventory:
29 def __init__(self, stock):
30 self.stock = dict(stock)
31 self.lock = threading.Lock() # stands in for a database row update
32 self.holds = {} # order_id -> (sku, qty, expires_at)
33
34 def naive_reserve(self, sku, qty, read_snapshot):
35 if read_snapshot >= qty: # decided on a stale read
36 self.stock[sku] -= qty
37 return True
38 return False
39
40 def reserve(self, sku, qty, order_id, now, ttl=600):
41 """UPDATE stock SET available = available - qty WHERE sku = ? AND available >= qty"""
42 with self.lock:
43 if self.stock[sku] >= qty:
44 self.stock[sku] -= qty
45 self.holds[order_id] = (sku, qty, now + ttl)
46 return True
47 return False
48
49 def confirm(self, order_id):
50 return self.holds.pop(order_id, None) is not None
51
52 def release(self, order_id):
53 hold = self.holds.pop(order_id, None)
54 if hold:
55 self.stock[hold[0]] += hold[1]
56
57 def expire(self, now):
58 for oid, (_, _, exp) in list(self.holds.items()):
59 if exp <= now:
60 self.release(oid)
61
62
63# ---- 3/4. orders -------------------------------------------------------------------
64
65TRANSITIONS = {
66 "created": {"payment_pending", "cancelled"},
67 "payment_pending": {"paid", "payment_failed", "expired"},
68 "paid": {"confirmed"},
69 "payment_failed": {"cancelled"},
70 "expired": {"refund_due"}, # money arrived after we gave up: refund it
71 "confirmed": set(), "cancelled": set(), "refund_due": set(),
72}
73
74
75class Orders:
76 def __init__(self):
77 self.by_key, self.orders = {}, {}
78
79 def create(self, idem_key, cart):
80 if idem_key in self.by_key:
81 return self.by_key[idem_key], "existing"
82 oid = f"o{len(self.orders) + 1}"
83 self.orders[oid] = {"state": "created", "cart": cart}
84 self.by_key[idem_key] = oid
85 return oid, "created"
86
87 def move(self, oid, new):
88 cur = self.orders[oid]["state"]
89 if new not in TRANSITIONS[cur]:
90 return False
91 self.orders[oid]["state"] = new
92 return True
93
94
95def on_payment_event(orders, inv, oid, event):
96 """Payment provider webhook (Chapter 24): succeeded / failed."""
97 state = orders.orders[oid]["state"]
98 if event == "succeeded":
99 if state == "payment_pending" and orders.move(oid, "paid"):
100 inv.confirm(oid)
101 orders.move(oid, "confirmed")
102 return "confirmed"
103 if state == "expired":
104 orders.move(oid, "refund_due")
105 return "refund" # stock was already released; don't ship blind
106 return "ignored" # duplicate webhook
107 if event == "failed" and orders.move(oid, "payment_failed"):
108 inv.release(oid)
109 orders.move(oid, "cancelled")
110 return "cancelled"
111 return "ignored"
112
113
114# ---- 5. saga -----------------------------------------------------------------------
115
116def checkout_saga(steps):
117 done, log = [], []
118 for name, do, undo in steps:
119 if do():
120 log.append(f"{name}: ok")
121 done.append((name, undo))
122 else:
123 log.append(f"{name}: failed")
124 for n, u in reversed(done):
125 u()
126 log.append(f"undo {n}")
127 return False, log
128 return True, log
129
130
131# ---- checks ------------------------------------------------------------------------
132
133def run_checks():
134 # 1. carts
135 assert merge_carts({"dune": 1}, {"dune": 2, "atomic-habits": 1}) == {"dune": 3, "atomic-habits": 1}
136 assert merge_carts({"pen": 9}, {"pen": 5}) == {"pen": 10}
137 assert reprice(["dune", "pen"], {"dune": 499, "pen": 20}, {"dune": 449, "pen": 20}) == {"dune": (499, 449)}
138
139 # 2. 100 phones in stock, 150 buyers at the same instant
140 inv = Inventory({"phone": 100})
141 snapshot = inv.stock["phone"] # everyone read 100 at once
142 sold = sum(inv.naive_reserve("phone", 1, snapshot) for _ in range(150))
143 assert sold == 150 and inv.stock["phone"] == -50 # oversold by 50
144
145 inv = Inventory({"phone": 100})
146 results = []
147 threads = [threading.Thread(target=lambda i=i: results.append(inv.reserve("phone", 1, f"o{i}", now=0)))
148 for i in range(150)]
149 [t.start() for t in threads]; [t.join() for t in threads]
150 assert sum(results) == 100 and inv.stock["phone"] == 0
151
152 # abandoned checkouts: 30 holds expire after 10 minutes and go back on sale
153 paid = [oid for oid in list(inv.holds)][30:] # 70 buyers pay; 30 walk away
154 for oid in paid:
155 inv.confirm(oid)
156 inv.expire(now=601)
157 assert inv.stock["phone"] == 30
158
159 # 3. idempotent orders: the Place Order button is tapped twice
160 orders = Orders()
161 o1, how1 = orders.create("cart-77-attempt-1", {"dune": 1})
162 o2, how2 = orders.create("cart-77-attempt-1", {"dune": 1})
163 assert o1 == o2 and how2 == "existing" and len(orders.orders) == 1
164
165 # 4. payment hand-off
166 inv = Inventory({"dune": 5})
167 orders = Orders()
168 a, _ = orders.create("k-a", {"dune": 1}); inv.reserve("dune", 1, a, now=0); orders.move(a, "payment_pending")
169 assert on_payment_event(orders, inv, a, "succeeded") == "confirmed"
170 assert on_payment_event(orders, inv, a, "succeeded") == "ignored" # duplicate webhook
171 b, _ = orders.create("k-b", {"dune": 1}); inv.reserve("dune", 1, b, now=0); orders.move(b, "payment_pending")
172 assert on_payment_event(orders, inv, b, "failed") == "cancelled" and inv.stock["dune"] == 4
173 c, _ = orders.create("k-c", {"dune": 1}); inv.reserve("dune", 1, c, now=0); orders.move(c, "payment_pending")
174 inv.expire(now=601); orders.move(c, "expired") # customer took too long
175 assert on_payment_event(orders, inv, c, "succeeded") == "refund" # late money: refund
176 assert not orders.move(a, "cancelled") # illegal transition refused
177
178 # 5. saga: coupon, reserve, pay; payment fails -> undo
179 state = {"coupon_used": False, "reserved": False}
180 steps = [("apply coupon", lambda: state.update(coupon_used=True) or True, lambda: state.update(coupon_used=False)),
181 ("reserve stock", lambda: state.update(reserved=True) or True, lambda: state.update(reserved=False)),
182 ("charge card", lambda: False, lambda: None)]
183 ok, log = checkout_saga(steps)
184 assert not ok and log[-2:] == ["undo reserve stock", "undo apply coupon"] and state == {"coupon_used": False, "reserved": False}
185
186 # Step 2 arithmetic: a sale opening, 5,000 orders a second at peak
187 assert 5_000 * 3 == 15_000 # writes a second: order + items + reservation, at least
188 assert 50_000_000 * 2_000 / 1e9 == 100 # GB of carts at ~2 KB each
189 return {"naive_sold": 150, "safe_sold": 100, "late_payment": "refund", "saga": log}
190
191
192if __name__ == "__main__":
193 print("All checks pass.", run_checks())