Premier League Predictor: FastAPI & Redis — Chapter 6, Exercise 3 ================================================================= TASK Enter a result for a fixture that was never created using a plain HSET, then explain why a later chapter's existence checks would treat the result as a real fixture, and what the Lua-guarded version does instead. SOLUTION Plain write: await r.hset("fixture:999", mapping={"home_score": 1, "away_score": 0}) await r.hgetall("fixture:999") -> {'home_score': '1', 'away_score': '0'} await r.exists("fixture:999") -> 1 HSET creates the key if it is absent. The result is a hash with two fields and none of the fields a real fixture has (home_team_id, away_team_id, gameweek). Redis has no schema to say those are required, and EXISTS only asks whether the key is present, so any later check of the form "does fixture:999 exist?" answers yes. A scoring or table job would then read home_team_id and get nothing back. Guarded version (the ENTER_RESULT Lua script): if redis.call('EXISTS', KEYS[1]) == 0 then return -1 end entering a result for fixture:999 -> -1 EXISTS fixture:999 -> 0 EXISTS fixture:999:points -> 0 The check and the write run as one atomic unit, so a fixture that doesn't exist is refused and nothing is left behind. The route turns -1 into a 404. Note: this only protects the fixture key. It cannot stop a fixture hash that was created incomplete some other way — Redis will not enforce which fields a hash must contain. WHY THIS WORKS AS AN ANSWER ---------------------------- It shows that EXISTS answers a question about a key, not about whether the record is complete, and it puts the check inside the same atomic script as the write so the guard has no gap.