Module 2 · Databases

Lesson 12 — NoSQL

Redis, MongoDB and search engines: what you sacrifice with each one and when it pays off.

Published
In this lesson
  1. Exercise 1 — Availability cache
  2. Exercise 2 — Atomic rate limit
  3. Exercise 3 — Distributed gift-card lock
  4. Exercise 4 — JSONB with GIN
  5. Exercise 5 — Simulated stampede
  6. Submit

Redis installed since Lesson 00 (redis-server); pip install redis in your venv. Do not look at solutions.md before submitting.

Exercise 1 — Availability cache

  1. Wrap your availability query (09) with Redis setex/get (TTL 10s, key avail:{event_id}). Measure with \timing/queries: first call vs second.
  2. Reserve a seat and check that the cache serves stale data for ≤10s. Is that acceptable for your business? Which TTL would you pick and why?
  3. Add invalidation: after a confirmed reservation, DELETE the key. Which problem does it solve? Which one does it introduce (stampede)?

Exercise 2 — Atomic rate limit

  1. Implement the "20 requests per IP per minute" limit with INCR + EX 60 on a middleware or a test view.
  2. Demonstrate it: 25 requests from the same IP → the 21st receives 429.
  3. Discuss: why does GET counter, if < 20: SET counter+1 (without atomicity) let 35 requests through under concurrency?

Exercise 3 — Distributed gift-card lock

  1. With r.lock("gift:TEST", timeout=5, blocking_timeout=2), two threads try to redeem the same card (a 0.2s sleep inside). What does the loser observe?
  2. Remove the lock's timeout=5 and kill the process inside the with (Ctrl+C in the thread complicates it; simulate with lock.release = crash). What happens to the card? Why is the TTL what prevents the eternal block?
  3. Connect with Lesson 10: inside the lock, what else do you need for the redemption to be atomic against the DB?

Exercise 4 — JSONB with GIN

  1. Add metadata JSONB DEFAULT '{}' to your Event model (a Lesson 11 migration, a safe one!) and the GIN index.
  2. Insert 3 events with different metadata ({"venue_type": "stadium"}, etc.) and query with @>. Verify in EXPLAIN that it uses the GIN index.
  3. Reflect: of those fields, which deserves a real column and which stays in JSONB?

Exercise 5 — Simulated stampede

  1. Empty the cache and fire 50 threads requesting availability at once. Count len(connection.queries): how many hit the DB?
  2. Simple fix: TTL with jitter (10 + random(0,5)). Repeat and compare. (The regeneration lock gets formalized with Celery in Lesson 29.)

Submit

Paste numbers and trimmed code. With this you close the whole data module; next Lesson 13 — REST done right (APIs module).