Lesson 32 / 32

Mixed Self-Check

Test yourself on mixed scenarios that combine identity, isolation, scaling and lifecycle ideas from the whole course.

By · 15 years in web development

How to use this self-check

Read each scenario, decide your answer and one reason before reading the walkthrough. If you cannot give a reason, revisit the named section. Being right for the wrong reason does not survive a follow-up question.

Scenario walkthroughs

Scenario 1: background jobs occasionally write data into the wrong tenant. Likely cause: the job message lacks tenant_id, or a worker thread reuses context from a previous job. Fix: put the tenant in every message and set or clear context per job (section 3). Scenario 2: after enabling RLS, developers still see all tenants in tests. Likely cause: tests connect as the table owner or a superuser. Fix: test as the restricted application role and add FORCE ROW LEVEL SECURITY (section 5). Scenario 3: a schema migration fails on 3 of 400 tenant databases. Fix: the wave rollout halts, the catalogue shows which tenants are on which version, the data issue is repaired, and the rollout resumes (section 7). Scenario 4: one tenant's nightly import slows every customer's dashboard. Fix: per-tenant limits and fair queues now, a replica or dedicated shard later (section 6).

Spot the bug

This handler runs in a pooled PostgreSQL setup. Identify the isolation bug before reading the self-check question.

def list_invoices(conn, tenant_id):
    with conn.cursor() as cur:
        cur.execute("SELECT set_config('app.tenant_id', %s, false)", (tenant_id,))
        cur.execute("SELECT id, total FROM invoices")
        return cur.fetchall()

Quick check: In the snippet above, the connection comes from a pool and the table uses RLS keyed on `app.tenant_id`. What is the main problem?

  • RLS cannot read custom settings
  • SELECT statements are never filtered by RLS
  • With is_local set to false the setting lasts for the whole session, so a later request on the pooled connection that forgets to set the tenant runs with this tenant's id
  • Python cannot pass parameters to SQL
Answer

With is_local set to false the setting lasts for the whole session, so a later request on the pooled connection that forgets to set the tenant runs with this tenant's id — set_config(..., false) behaves like a plain SET and lasts for the session. Run the work in a transaction with set_config(..., true) or SET LOCAL so the tenant setting cannot leak to the next borrower of the connection.