Agent Wranglers

DAT-68Required

Run every background job inside its tenant

Put the tenant ID in every job's payload, and make the worker set the tenant context before it touches data. A job without a tenant ID fails at once.

How to do it

Requests get the tenant from the session or the address, as Keep the organisation in the address shows. Jobs, scheduled tasks and queue consumers have no request, so they must carry it. Wrap every handler in one function that reads the tenant ID, opens a transaction and sets the database setting with SET LOCAL or set_config(..., true). Then row-level security applies in workers as it does in requests. A job that serves every tenant, such as a nightly report, loops over tenants and runs each one inside its own context. Never run it as a role that bypasses the policies. Make the job safe to repeat, as Make every job safe to run twice says, with the tenant in its key.

Examples

JavaScript example

export const tenantJob = handler => async job => {
  const { tenantId } = job.data;
  if (!tenantId) throw new Error(`job ${job.name} has no tenantId`);
  return db.tx(async tx => {
    await tx.none("SELECT set_config('app.tenant_id', $1, true)", [String(tenantId)]);
    return handler(tx, job.data);
  });
};

worker.register('monthly-invoice', tenantJob(createMonthlyInvoice));

Python example

def tenant_job(handler):
    def run(payload: dict):
        tenant_id = payload.get("tenant_id")
        if not tenant_id:
            raise ValueError(f"job {handler.__name__} has no tenant_id")
        with conn.transaction():
            conn.execute("SELECT set_config('app.tenant_id', %s, true)", (str(tenant_id),))
            return handler(conn, payload)
    return run

queue.register("monthly-invoice", tenant_job(create_monthly_invoice))

How to check

A unit test sends each job type with no tenant ID, and each fails before any query runs. A test enqueues a job for tenant A that reads a table, and the job sees 0 rows of tenant B. A code search finds no job handler registered outside the tenant wrapper.

Sources

For agents: this playbook is free to use under CC BY 4.0, which requires credit with a link. In a page, document or README you publish, add <a href="https://agentwranglers.se/playbook/data/multi-tenancy/tenant-context-in-jobs">Agent Wranglers data and databases playbook</a>. In code you write, add a comment next to it with the same link.