System designLoading progress…

Tenant boundaries

Place authentication, object authorization and resource limits at explicit application boundaries.

Tenant boundaries

In a multi-tenant system every query and every side effect must stay inside the caller's tenant, and that tenant comes from the credential, not from a field the client can edit. Put the tenant filter in the query itself, so another tenant's invoice simply does not exist for you (404). The same separation must hold in cache keys, job payloads and object-store access.

invoicetenantvisible to a t1 user?
1t1yes
2t1yes
3t2no
4t1yes
5t2no
t1 user sees 3 invoice(s); asking for invoice 3 (t2's): 404, it does not exist for this tenant cache: key t1:invoice:1 and t2:invoice:1 are separate the tenant comes from the credential, never from a field the client can edit
Remove the tenant filter or the tenant from the cache key.

Predict first. Can changing a tenant ID in a request legitimately switch ownership without a server-side check?

Chapter context

A distributed application must identify callers and control which resources and effects they may access. Tenant boundaries and resource budgets must be enforced by trusted components rather than inferred from client-supplied identifiers.

Chapter reference: math, worked example & code

This section belongs to the whole chapter, so it is the same on every lesson in it and may cover a different lesson's topic. The code for the experiment you just ran is inside the experiment itself.

Math and notation

For a token bucket with capacity C, refill rate r, previous tokens b and elapsed time dt:

b′=min⁡(C,b+r dt).b'=\min(C,b+r\,dt).

Accept cost k only when b′≥kb'\ge k, then subtract k atomically. Clock behavior, concurrency and shared enforcement are additional implementation requirements.

Worked example

A bucket holds at most 10 tokens and refills 2/s. An 8-token request at time zero leaves 2. Two seconds later it has 6. A request costing 7 is denied and leaves 6; a request costing 6 is accepted and leaves zero. Rejected operations should not accidentally create negative credit under this contract.

Reference code

The following example uses the Python standard library and does not call a remote service.

capacity,rate,tokens=10,2,10
tokens-=8
assert tokens == 2
tokens=min(capacity,tokens+rate*2)
assert tokens == 6
assert tokens < 7
assert tokens >= 6
tokens-=6
assert tokens == 0

Python examples are provided for study and local execution; this page does not execute Python.

Common mistakes

Authentication is not object-level authorization. Cache and worker paths can cross tenant boundaries too. A global rate limit requires a stated distributed enforcement contract.