jonnifyelixir
F4 / F4.08 / redis

F4.08 · part 3 of 3

Redis keys

In Redis the id is a string key, namespaced with a colon: user:USR0NbWMtkosp8. The namespace keeps key spaces apart and makes a scan or an eviction policy easy to scope. The same self-validating id pays off harder here than anywhere: a cache exists to absorb load, so the cheapest win is to reject the load that should never reach it. A malformed or impossible id is a 404 at the edge — no GET, no fallback query.

A burst of eight requests, some real and some the kind a scanner sends. Compare an edge that validates first with one that forwards everything. Select a mode.

Validate, then reach the cache

The same eight requests, two edges. With validation, only well-formed, plausibly-timed USR ids become a cache lookup; the rest are shed for the cost of a decode. Without it, every request becomes a GET, and the ones that cannot exist miss the cache and fall through to the database. Select a mode.

Edge mode · select one

request id (expects USR) outcome 3 of 8 reach the cache · 5 shed at the edge (0 I/O)

      

edge: validates before the cache

load on store: 3 lookups

A cache absorbs load best when it never sees the load it should not. Validating the id at the edge turns a scanner’s flood into constant-time 404s and leaves the cache for requests that could be real.

Advanced: the keyspace and the honest limit

The key is the branded id under a type namespace — user:USR0NbWMtkosp8, session:SES… — so one Redis instance holds several record kinds without collision, and a colon prefix scopes a SCAN or a per-type eviction. The validating plug runs before the Redix call, so a rejected id never becomes a network round-trip to the cache, let alone a database read.

The honest limit, the same as the hub’s: validation proves an id is well-formed and plausibly-timed, not that it exists. A correctly-shaped USR id that was never issued still costs one cache lookup, and a miss still falls through to one query — that path cannot be skipped without knowing the keyspace. What the edge removes is the high-volume, zero-effort probe: wrong length, non-base62, out of range, wrong namespace, future timestamp. Those are most of a scan, and they now cost the attacker a 404 and the store nothing.

# validate at the edge; only then touch Redis, only then the DB
def profile(conn, %{"id" => id}) do
  with {:ok, _snow} <- Portal.Id.validate(id, :USR),         # no I/O
       {:ok, json} <- Redix.command(["GET", "user:" <> id]) do
    json(conn, json)                                          # cache hit
  else
    {:error, _} -> send_resp(conn, 404, "")              # malformed id: shed
    {:ok, nil}    -> load_from_db_and_cache(conn, id)        # well-formed miss
  end
end

a self-describing id

The edge can judge its shape and time with no lookup.

a protected cache

Bogus traffic is 404’d before Redis; only plausible ids reach the keyspace.

That completes F4.08: the branded id is a bigint key, a time-range bound, and a namespaced cache key — and a gate that sheds what cannot exist. Next module: F4.09 — Branded CHAMP maps & GenServer, which keys an in-memory CHAMP on the same id, partitioned by namespace.

References

Primary sources for this lesson, and where it connects in the course.

Sources

Related in this course