Multi-tenancy¶
One Drupal instance can host several isolated workflow realms, called tenants. Each tenant runs its own processes: instances, tokens and variables partition per tenant, so a query, a listing or a purge in one tenant never reaches another's rows.
Every install starts with one tenant out of the box, the default tenant, and that is all a single-tenant site ever needs. Multi-tenancy stays invisible until you create a second tenant, so a single-tenant site behaves as if tenancy were absent.
How isolation works¶
Each partitioned row records the tenant it belongs to in a tenant field. The
value is stamped at create time by the engine, from the active tenant, and
defaults to the default tenant so a row is never tenantless. A process
workflow is either shared across every tenant (the default) or scoped
to a single tenant.
A delegation is likewise a single tenant's: someone covering for a person in one realm is a stranger in another, and every question the resolver answers names its tenant.
Managing tenants¶
The Tenants list at /admin/config/workflow/orchestra/tenants holds every realm
the site has. Add tenant creates one from a label, the machine name that
every row in the realm is stamped with, and a weight that orders the listings.
The machine name is offered once and fixed afterwards: renaming it would leave
the instances, tokens, variables and workflows it stamped behind under the old
name.
Each row reaches Edit for the label and the weight, Retention and Reading for what the realm keeps and who may read its runs, and Delete. Deleting a tenant is not a tidy-up. It cancels whatever is still running in the realm, then removes every process instance scoped to it, with their tokens, variables and incidents, and the workflows scoped to it; the confirmation counts those rows before you agree to it. The default tenant cannot be deleted at all, because every request that resolves to no tenant falls back to it.
Removing a tenant with drush config:delete does none of that. It writes to the
configuration directly, and nothing carries a raw config write back to the
entity system, so the realm's rows are left behind with no tenant to belong to.
Use the form, or a config import, which deletes through the entity system as the
form does.
Resolving the active tenant¶
Which tenant a request acts in is decided by tenant resolvers: services
tagged orchestra.tenant_resolver implementing TenantResolverInterface. Each
resolver returns a tenant machine name or NULL to defer; the first non-null
answer wins, and if none answer the default tenant is used. With no resolver
installed every request resolves to the default tenant.
That fallback is what keeps a single-tenant site simple, and it is also what a
site can back itself into: a second tenant, added on the Tenants list above or
arriving with a config import, cannot be reached at all while no resolver is
installed. No request ever acts in it, so nothing can be created there, and the
listings and access checks, scoped to the tenant in effect, hide anything that
is already there. The site looks multi-tenant and is not. Orchestra says so on
the status report, with a warning naming how many tenants are out of reach, so
install a connector (orchestra_domain below, or orchestra_server_api for
remote consumers), tag a resolver of your own, or delete the tenants you are not
using.
To resolve tenants your own way, implement the interface and tag the service:
services:
my_module.tenant_resolver:
class: Drupal\my_module\MyTenantResolver
tags:
- { name: orchestra.tenant_resolver }
Mapping a domain to a tenant¶
The optional orchestra_domain submodule maps the active domain (from the
Domain module) to a tenant, so each
domain runs its own isolated set of processes on one Drupal instance. Bind a
domain to a tenant on the domain add/edit form (the binding is a third-party
setting on the domain record); a request served on that domain then acts in the
bound tenant, falling back to the default when a domain has no binding. It is
just another tenant resolver, so the kernel itself stays domain-agnostic.
Tenant-scoped operators¶
The process instances UI (provided by orchestra_ui) lives under Structure at
/admin/structure/orchestra/instances, with a menu link and a canonical link
template per instance. Two permissions gate it:
- Access Orchestra process instances (
access orchestra instances): a tenant-scoped operator who sees and drives only their own tenant's instances. The list and the per-instance page resolve the active tenant the usual way and show only that tenant's rows. - Administer Orchestra (
administer orchestra): the cross-tenant super-permission. A holder reaches every tenant's instances and is not narrowed to the active one.
The list's filter follows the same split: a tenant-scoped operator narrows by workflow (their tenant's plus the shared ones) and status, while the tenant filter itself is offered only to an administrator, whose list spans every tenant.
So you can give an operator day-to-day control of one realm's processes without handing them the keys to every other tenant on the site.
Who reads a realm's runs¶
Separately from those operator permissions, a tenant decides how much of a run's own audience may read it: its Reading operation on the Tenants list sets the read access for every workflow in the realm, overriding the site setting, and any workflow of the realm may override the tenant in turn. That is about reading a run without administrative rights, not about driving it, and it never crosses tenants: a run is readable only from inside its own tenant, whatever the scope says.