पाठ 19 / 32

Onboarding and Provisioning

यह पाठ अभी हिंदी में अनुवादित नहीं है, इसलिए अंग्रेज़ी में दिखाया जा रहा है।

Design an automated, idempotent onboarding workflow that creates a tenant record, provisions its resources and activates it safely on retry.

लेखक: · 15 साल से वेब डेवलपर

Onboarding is a workflow, not an insert

When a customer signs up, the control plane must do several things: create the tenant record in the catalogue, choose its placement (pool shard or dedicated resources, plus region), create schemas or databases if the model needs them, seed default settings and roles, create the first admin user, set up billing, and finally mark the tenant active. For a pooled tenant this takes milliseconds; for a silo tenant it may take minutes. Steps can fail halfway, such as when the database is created but the email service is down. So treat onboarding as a workflow: each step is idempotent (running it twice has the same effect as once), progress is recorded, and the whole thing can be retried or resumed. Keep the tenant in a provisioning state until all steps succeed, so half-built tenants never receive traffic. Durable workflow engines or a simple state machine in a table both work.

The tenant lifecycle

A tenant is provisioned, becomes active, may be suspended (for non-payment or abuse), and is finally offboarded with export and deletion.

Four stages in a row: provisioning, active, suspended, offboarded.
Figure 7.1: provisioning -> active -> suspended -> offboarded

Idempotent provisioning steps

Each step checks whether its work is already done. Retrying after a crash skips finished steps instead of duplicating them.

state = {"tenants": {}, "admins": set(), "schemas": set()}

def step(name, done, do):
    if done():
        print(f"skip  {name} (already done)")
    else:
        do()
        print(f"done  {name}")

def provision(tenant_id, admin_email):
    step("create tenant record",
         lambda: tenant_id in state["tenants"],
         lambda: state["tenants"].update({tenant_id: "active"}))
    step("create schema",
         lambda: tenant_id in state["schemas"],
         lambda: state["schemas"].add(tenant_id))
    step("invite admin",
         lambda: (tenant_id, admin_email) in state["admins"],
         lambda: state["admins"].add((tenant_id, admin_email)))

provision("zen", "owner@zen.in")
print("-- retry after a crash --")
provision("zen", "owner@zen.in")

Output:

done  create tenant record
done  create schema
done  invite admin
-- retry after a crash --
skip  create tenant record (already done)
skip  create schema (already done)
skip  invite admin (already done)

Make sign-up abuse-resistant

If every free sign-up creates a database, a script can create thousands. Rate-limit sign-ups, verify email before expensive provisioning, and put free tenants in the pool so they cost almost nothing until they convert.

त्वरित जाँच: Why should each provisioning step be idempotent?

  • So the workflow can be safely retried after a partial failure without duplicating resources
  • So that steps run faster
  • So tenants can skip email verification
  • So that provisioning never needs a database
Answer

So the workflow can be safely retried after a partial failure without duplicating resources — Failures halfway are normal. Idempotent steps mean a retry finishes the job instead of creating a second schema or a second admin invite.