Premier League Predictor: FastAPI & Redis — Chapter 11, Exercise 1 ================================================================== TASK Run 200 concurrent WATCH-guarded claims (with a 50 ms hold) through a default pool, a ConnectionPool with max_connections=20, and a BlockingConnectionPool with max_connections=20. Report how many succeed, how many fail and with what error, and the peak client count for each. SOLUTION Each claim: async with r.pipeline(transaction=True) as pipe: WATCH the key, sleep 0.05 s, SISMEMBER, MULTI, SADD, EXEC. A separate monitor connection sampled INFO clients every 10 ms. Tested with redis-py 8.1.0. pool peak clients ok failed default (max_connections = 100) 101 100 100 ConnectionPool(max_connections=20) 21 20 180 BlockingConnectionPool(max=20) 21 200 0 Every failure was: MaxConnectionsError: Too many connections. The peaks include the monitor's own connection, so the app used 100, 20 and 20. Why: a WATCH transaction pins one connection from the WATCH until the EXEC, so 200 concurrent claims want 200 connections at once. A plain pool raises as soon as it has none left to hand out. A BlockingConnectionPool makes the caller wait for one to be released, so the 200 requests were served 20 at a time. If the pools are closed with plain aclose() instead of aclose(close_connection_pool=True), a client given its own pool leaves that pool's connections open, and the next run starts with them still counted. An earlier version of this test reported a peak of 41 for the last case for that reason. WHY THIS WORKS AS AN ANSWER ---------------------------- It reports success, failure and peak for all three pools from real runs, identifies the failure as the pool's refusal rather than a Redis limit, and explains why WATCH makes the connection count depend on hold time.