Isolation Providers¶
Base¶
BaseIsolationProvider ¶
BaseIsolationProvider(config: TenancyConfig)
Bases: ABC
Abstract base class for data isolation strategies.
Isolation providers are responsible for:
-
Scoping sessions — returning a database session configured so that queries automatically target the correct tenant namespace (:meth:
get_session). -
Filtering queries — adding
WHEREclauses or other predicates that enforce per-tenant visibility (:meth:apply_filters). -
Provisioning — creating the necessary database structures (schemas, tables, RLS policies) when a new tenant is onboarded (:meth:
initialize_tenant). -
Deprovisioning — removing all tenant data and structures when a tenant is deleted (:meth:
destroy_tenant).
Type safety¶
:meth:apply_filters is generic over :data:~fastapi_tenancy.core.types.SelectT
(bound to sqlalchemy.sql.Select) so that the concrete query type is
preserved through the call::
q: Select[tuple[User]] = select(User)
filtered: Select[tuple[User]] = await provider.apply_filters(q, tenant)
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
config
|
TenancyConfig
|
Application-wide tenancy configuration. |
required |
Source code in src/fastapi_tenancy/isolation/base.py
get_session
abstractmethod
¶
get_session(
tenant: Tenant,
) -> AbstractAsyncContextManager[AsyncSession]
Yield a database session scoped to tenant's namespace.
The session must be fully configured before yielding — i.e. the
search_path, session variable, or connection must already be
set so that subsequent queries are automatically isolated.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
tenant
|
Tenant
|
The tenant whose data namespace should be active. |
required |
Returns:
| Type | Description |
|---|---|
AbstractAsyncContextManager[AsyncSession]
|
An async context manager that yields a configured |
AbstractAsyncContextManager[AsyncSession]
|
class: |
Raises:
| Type | Description |
|---|---|
IsolationError
|
When the session cannot be opened or configured. |
Example::
async with provider.get_session(tenant) as session:
result = await session.execute(select(User))
await session.commit()
Source code in src/fastapi_tenancy/isolation/base.py
apply_filters
abstractmethod
async
¶
apply_filters(query: SelectT, tenant: Tenant) -> SelectT
Return query filtered to only expose tenant's data.
The implementation should use SQLAlchemy's .where() method with
a properly bound parameter — never string interpolation.
For strategies that already enforce isolation at the session level
(e.g. DATABASE isolation with separate databases per tenant),
this method may return query unchanged.
The generic bound SelectT ensures the concrete Select subtype
is preserved through the call, so mypy can type-check the result.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
query
|
SelectT
|
A SQLAlchemy |
required |
tenant
|
Tenant
|
The currently active tenant. |
required |
Returns:
| Type | Description |
|---|---|
SelectT
|
The (potentially) filtered query. |
Source code in src/fastapi_tenancy/isolation/base.py
initialize_tenant
abstractmethod
async
¶
initialize_tenant(
tenant: Tenant, metadata: MetaData | None = None
) -> None
Provision database structures for a newly created tenant.
Called once when a tenant is registered. The implementation should be idempotent so that repeated calls do not cause errors.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
tenant
|
Tenant
|
The newly created tenant. |
required |
metadata
|
MetaData | None
|
Optional SQLAlchemy |
None
|
Raises:
| Type | Description |
|---|---|
IsolationError
|
When provisioning fails. |
Source code in src/fastapi_tenancy/isolation/base.py
destroy_tenant
abstractmethod
async
¶
destroy_tenant(tenant: Tenant, **kwargs: Any) -> None
Deprovision and permanently delete all data for tenant.
.. warning:: This is a destructive, irreversible operation. Ensure the caller has appropriate authorisation and a confirmed audit trail before invoking.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
tenant
|
Tenant
|
The tenant to destroy. |
required |
**kwargs
|
Any
|
Provider-specific options. |
{}
|
Raises:
| Type | Description |
|---|---|
IsolationError
|
When deprovisioning fails. |
Source code in src/fastapi_tenancy/isolation/base.py
verify_isolation
async
¶
verify_isolation(tenant: Tenant) -> bool
Return True if the tenant's isolation is functioning correctly.
The base implementation logs a warning and returns True.
Subclasses should override this with a meaningful check (e.g.
verify that the schema exists, the database is reachable, RLS
policies are active).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
tenant
|
Tenant
|
The tenant to verify. |
required |
Returns:
| Type | Description |
|---|---|
bool
|
|
bool
|
detected. |
Source code in src/fastapi_tenancy/isolation/base.py
get_schema_name ¶
get_schema_name(tenant: Tenant) -> str
Compute the schema name for tenant using the global config.
Delegates to :meth:~fastapi_tenancy.core.config.TenancyConfig.get_schema_name
so the naming convention is always consistent.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
tenant
|
Tenant
|
The tenant to compute the schema name for. |
required |
Returns:
| Type | Description |
|---|---|
str
|
A safe, lowercased schema name string. |
Source code in src/fastapi_tenancy/isolation/base.py
get_database_url ¶
get_database_url(tenant: Tenant) -> str
Return the database connection URL for tenant.
If the tenant has a database_url override set, that is returned.
Otherwise the URL is derived from the global config template.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
tenant
|
Tenant
|
The tenant to get the URL for. |
required |
Returns:
| Type | Description |
|---|---|
str
|
A fully-qualified async database URL string. |
Source code in src/fastapi_tenancy/isolation/base.py
Schema¶
SchemaIsolationProvider ¶
SchemaIsolationProvider(
config: TenancyConfig, engine: AsyncEngine | None = None
)
Bases: BaseIsolationProvider
Schema-per-tenant isolation with automatic dialect-based fallback.
PostgreSQL / MSSQL — native schema isolation
Creates a dedicated schema per tenant. Sets search_path (with
SET LOCAL) on every session connection so unqualified table
references resolve correctly.
SQLite / unknown dialects — table-name prefix
Copies the application MetaData with a tenant-specific prefix
applied to every table name (e.g. t_acme_corp_users).
MySQL / MariaDB — database-per-tenant delegation
MySQL's SCHEMA == DATABASE. Transparently delegates to
:class:~fastapi_tenancy.isolation.database.DatabaseIsolationProvider.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
config
|
TenancyConfig
|
Tenancy configuration. |
required |
engine
|
AsyncEngine | None
|
Optional pre-built engine to reuse (avoids a duplicate pool
when this provider is used inside
:class: |
None
|
Example::
provider = SchemaIsolationProvider(config)
await provider.initialize_tenant(tenant, metadata=Base.metadata)
async with provider.get_session(tenant) as session:
result = await session.execute(select(User))
Source code in src/fastapi_tenancy/isolation/schema.py
get_table_prefix ¶
get_table_prefix(tenant: Tenant) -> str
Return the table-name prefix for non-schema dialects.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
tenant
|
Tenant
|
Target tenant. |
required |
Returns:
| Type | Description |
|---|---|
str
|
Table-name prefix string ending with |
Source code in src/fastapi_tenancy/isolation/schema.py
get_session
async
¶
get_session(tenant: Tenant) -> AsyncIterator[AsyncSession]
Yield a tenant-scoped AsyncSession.
Dispatches to the correct session strategy based on the detected database dialect.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
tenant
|
Tenant
|
Currently active tenant. |
required |
Yields:
| Name | Type | Description |
|---|---|---|
Configured |
AsyncIterator[AsyncSession]
|
class: |
Raises:
| Type | Description |
|---|---|
IsolationError
|
When the session cannot be opened. |
Source code in src/fastapi_tenancy/isolation/schema.py
apply_filters
async
¶
apply_filters(query: SelectT, tenant: Tenant) -> SelectT
Apply WHERE tenant_id = :id as a defence-in-depth filter.
For native-schema dialects the search_path already enforces
isolation; this filter is an additional safety net. For prefix-mode
dialects it is the primary isolation mechanism.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
query
|
SelectT
|
SQLAlchemy |
required |
tenant
|
Tenant
|
Currently active tenant. |
required |
Returns:
| Type | Description |
|---|---|
SelectT
|
Filtered query. |
Source code in src/fastapi_tenancy/isolation/schema.py
initialize_tenant
async
¶
initialize_tenant(
tenant: Tenant, metadata: MetaData | None = None
) -> None
Create the tenant's isolation namespace.
- Native schemas →
CREATE SCHEMA IF NOT EXISTS - Prefix mode → copy+rename tables in metadata with tenant prefix
- MySQL → delegates to database provider
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
tenant
|
Tenant
|
Target tenant. |
required |
metadata
|
MetaData | None
|
Application :class: |
None
|
Raises:
| Type | Description |
|---|---|
IsolationError
|
When provisioning fails. |
Source code in src/fastapi_tenancy/isolation/schema.py
destroy_tenant
async
¶
destroy_tenant(tenant: Tenant, **kwargs: Any) -> None
Drop the tenant's isolation namespace.
.. warning:: Permanently destroys all tenant data.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
tenant
|
Tenant
|
The tenant to deprovision. |
required |
Raises:
| Type | Description |
|---|---|
IsolationError
|
When the drop operation fails. |
Source code in src/fastapi_tenancy/isolation/schema.py
verify_isolation
async
¶
verify_isolation(tenant: Tenant) -> bool
Verify that the tenant's schema / tables exist and are reachable.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
tenant
|
Tenant
|
Tenant to verify. |
required |
Returns:
| Type | Description |
|---|---|
bool
|
|
Source code in src/fastapi_tenancy/isolation/schema.py
close
async
¶
Dispose the engine and release pooled connections.
Source code in src/fastapi_tenancy/isolation/schema.py
Database¶
DatabaseIsolationProvider ¶
DatabaseIsolationProvider(
config: TenancyConfig,
master_engine: AsyncEngine | None = None,
)
Bases: BaseIsolationProvider
Separate database per tenant with automatic dialect-based provisioning.
A single master engine connects to the admin/default database for DDL. Per-tenant engines are created lazily on the first request and cached in a bounded LRU cache for the lifetime of the application.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
config
|
TenancyConfig
|
Tenancy configuration. |
required |
master_engine
|
AsyncEngine | None
|
Optional pre-built master engine (shared engine
injected by :class: |
None
|
Example::
provider = DatabaseIsolationProvider(config)
await provider.initialize_tenant(tenant, metadata=Base.metadata)
async with provider.get_session(tenant) as session:
result = await session.execute(select(Order))
await provider.close() # disposes all per-tenant engines
Source code in src/fastapi_tenancy/isolation/database.py
get_session
async
¶
get_session(tenant: Tenant) -> AsyncIterator[AsyncSession]
Yield a session connected to tenant's dedicated database.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
tenant
|
Tenant
|
Currently active tenant. |
required |
Yields:
| Name | Type | Description |
|---|---|---|
An |
AsyncIterator[AsyncSession]
|
class: |
Raises:
| Type | Description |
|---|---|
IsolationError
|
When the session cannot be opened. |
Source code in src/fastapi_tenancy/isolation/database.py
apply_filters
async
¶
apply_filters(query: SelectT, tenant: Tenant) -> SelectT
No filtering required — each tenant has a dedicated database.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
query
|
SelectT
|
SQLAlchemy |
required |
tenant
|
Tenant
|
Currently active tenant (ignored). |
required |
Returns:
| Type | Description |
|---|---|
SelectT
|
query unchanged. |
Source code in src/fastapi_tenancy/isolation/database.py
| Python | |
|---|---|
initialize_tenant
async
¶
initialize_tenant(
tenant: Tenant, metadata: MetaData | None = None
) -> None
Create tenant's dedicated database and optionally create tables.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
tenant
|
Tenant
|
Target tenant. |
required |
metadata
|
MetaData | None
|
Application :class: |
None
|
Raises:
| Type | Description |
|---|---|
IsolationError
|
When database creation or table creation fails. |
Source code in src/fastapi_tenancy/isolation/database.py
| Python | |
|---|---|
424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 | |
destroy_tenant
async
¶
destroy_tenant(tenant: Tenant, **kwargs: Any) -> None
Drop tenant's dedicated database.
.. warning:: Permanently destroys all tenant data.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
tenant
|
Tenant
|
The tenant to destroy. |
required |
**kwargs
|
Any
|
Accepted for interface compatibility; not used by this provider. |
{}
|
Raises:
| Type | Description |
|---|---|
IsolationError
|
When the database cannot be dropped. |
Source code in src/fastapi_tenancy/isolation/database.py
| Python | |
|---|---|
505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 | |
verify_isolation
async
¶
verify_isolation(tenant: Tenant) -> bool
Return True if tenant's database exists and is reachable.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
tenant
|
Tenant
|
Tenant to verify. |
required |
Returns:
| Type | Description |
|---|---|
bool
|
|
Source code in src/fastapi_tenancy/isolation/database.py
close
async
¶
Dispose all per-tenant engines from the LRU cache and the master engine.
Source code in src/fastapi_tenancy/isolation/database.py
| Python | |
|---|---|
RLS¶
RLSIsolationProvider ¶
RLSIsolationProvider(
config: TenancyConfig, engine: AsyncEngine | None = None
)
Bases: BaseIsolationProvider
PostgreSQL Row-Level Security data isolation.
Activates per-request tenant filtering by setting the
app.current_tenant session variable at the start of every
transaction. PostgreSQL RLS policies reference this variable to
restrict row visibility transparently.
Raises :exc:~fastapi_tenancy.core.exceptions.ConfigurationError
at construction if the configured database is not PostgreSQL, so
misconfigured deployments fail immediately at startup.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
config
|
TenancyConfig
|
Application-wide tenancy configuration. |
required |
engine
|
AsyncEngine | None
|
Optional pre-built engine to reuse (share with the store to avoid a duplicate connection pool). |
None
|
Example::
provider = RLSIsolationProvider(config)
await provider.initialize_tenant(tenant, metadata=Base.metadata)
async with provider.get_session(tenant) as session:
# RLS policy silently adds: WHERE tenant_id = '<current tenant>'
result = await session.execute(select(User))
Schema hint::
ALTER TABLE users ENABLE ROW LEVEL SECURITY;
ALTER TABLE users FORCE ROW LEVEL SECURITY;
CREATE POLICY tenant_isolation ON users
USING (tenant_id = current_setting('app.current_tenant', TRUE));
Source code in src/fastapi_tenancy/isolation/rls.py
get_session
async
¶
get_session(tenant: Tenant) -> AsyncIterator[AsyncSession]
Yield a session with app.current_tenant set to tenant's ID.
Uses SELECT set_config('app.current_tenant', :id, TRUE) — a
fully parameterised call — to avoid injection via crafted tenant IDs.
Connection-level event listener design¶
set_config(..., TRUE) (equivalent to SET LOCAL) is
transaction-scoped: the GUC reverts when the current transaction
ends. If we simply call it once before yielding, any subsequent
session.begin() block opened by the route handler would operate
without the GUC, silently bypassing all RLS policies.
The fix is to install a begin event listener on the underlying
synchronous DBAPI connection object so that set_config is
re-executed at the start of every transaction opened on this
connection during the request lifetime.
Critical: the listener must be removed before the connection returns to the pool. Without removal, when the physical connection is reused by a future request for a different tenant, the old tenant's GUC listener fires on every new transaction — a silent cross-tenant data-read breach.
We save the listener function as a local variable and call
event.remove(sync_conn, "begin", _set_rls_guc) in a finally
block that wraps the entire session lifetime. The finally is
positioned outside the AsyncSession context manager so it always
runs — even if AsyncSession construction raises, or if an uncaught
exception propagates from the route handler.
Parameterisation note¶
Two different parameterisation syntaxes are intentionally used:
exec_driver_sql(in thebeginlistener): uses the native asyncpg$1placeholder becauseexec_driver_sqlbypasses SQLAlchemy's parameter rendering layer entirely.session.execute + text()(initial GUC set): uses SQLAlchemy's:namesyntax, translated to$1by asyncpg at render time.
Both are correct for their respective call sites.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
tenant
|
Tenant
|
Currently active tenant. |
required |
Yields:
| Name | Type | Description |
|---|---|---|
Configured |
AsyncIterator[AsyncSession]
|
class: |
Raises:
| Type | Description |
|---|---|
IsolationError
|
When the session variable cannot be set. |
Source code in src/fastapi_tenancy/isolation/rls.py
| Python | |
|---|---|
163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 | |
apply_filters
async
¶
apply_filters(query: SelectT, tenant: Tenant) -> SelectT
Add defence-in-depth WHERE tenant_id = :id to query.
Although the RLS policy already restricts rows at the database level, this explicit application-layer filter prevents accidental data leakage if the RLS policy is ever accidentally disabled on a table.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
query
|
SelectT
|
SQLAlchemy |
required |
tenant
|
Tenant
|
Currently active tenant. |
required |
Returns:
| Type | Description |
|---|---|
SelectT
|
Query with an additional |
Source code in src/fastapi_tenancy/isolation/rls.py
initialize_tenant
async
¶
initialize_tenant(
tenant: Tenant, metadata: MetaData | None = None
) -> None
Verify RLS policies are active; optionally create tables.
Unlike schema/database isolation, RLS uses shared tables — there is nothing to physically create per-tenant. This method logs a reminder about the required RLS policy and creates tables if metadata is supplied and they do not already exist.
When metadata is supplied, create_all is called with
checkfirst=True so repeated calls are idempotent.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
tenant
|
Tenant
|
The newly onboarded tenant. |
required |
metadata
|
MetaData | None
|
Application |
None
|
Raises:
| Type | Description |
|---|---|
IsolationError
|
When table creation fails. |
Source code in src/fastapi_tenancy/isolation/rls.py
destroy_tenant
async
¶
destroy_tenant(tenant: Tenant, **kwargs: Any) -> None
Delete all rows belonging to tenant from every tenant-scoped table.
Executes a DELETE FROM <table> WHERE tenant_id = :id for every
table in metadata (passed via keyword argument metadata).
.. warning::
This is a destructive, irreversible operation. Tables that
share rows with other tenants will retain their other data;
only rows where tenant_id = tenant.id are deleted.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
tenant
|
Tenant
|
Tenant to destroy. |
required |
**kwargs
|
Any
|
|
{}
|
Raises:
| Type | Description |
|---|---|
IsolationError
|
When metadata is not supplied or deletion fails. |
Source code in src/fastapi_tenancy/isolation/rls.py
verify_isolation
async
¶
verify_isolation(tenant: Tenant) -> bool
Verify that the RLS GUC can be set and read back correctly.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
tenant
|
Tenant
|
Tenant to verify. |
required |
Returns:
| Type | Description |
|---|---|
bool
|
|
Source code in src/fastapi_tenancy/isolation/rls.py
close
async
¶
Hybrid¶
HybridIsolationProvider ¶
HybridIsolationProvider(
config: TenancyConfig,
premium_engine: AsyncEngine | None = None,
standard_engine: AsyncEngine | None = None,
)
Bases: BaseIsolationProvider
Two-tier hybrid isolation: premium → strong; standard → economical.
Premium and standard isolation strategies are set in TenancyConfig::
config = TenancyConfig(
...
isolation_strategy="hybrid",
premium_tenants=["tenant-enterprise-1", "tenant-enterprise-2"],
premium_isolation_strategy="schema",
standard_isolation_strategy="rls",
)
Then::
provider = HybridIsolationProvider(config)
# Premium tenant → SchemaIsolationProvider
async with provider.get_session(premium_tenant) as session: ...
# Standard tenant → RLSIsolationProvider
async with provider.get_session(standard_tenant) as session: ...
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
config
|
TenancyConfig
|
Application-wide tenancy configuration. |
required |
premium_engine
|
AsyncEngine | None
|
Pre-built engine to inject into the premium provider.
When |
None
|
standard_engine
|
AsyncEngine | None
|
Pre-built engine to inject into the standard
provider. When |
None
|
Raises:
| Type | Description |
|---|---|
ConfigurationError
|
When |
Source code in src/fastapi_tenancy/isolation/hybrid.py
premium_provider
property
¶
premium_provider: BaseIsolationProvider
The inner provider used for premium tenants.
standard_provider
property
¶
standard_provider: BaseIsolationProvider
The inner provider used for standard tenants.
get_session
async
¶
get_session(tenant: Tenant) -> AsyncIterator[AsyncSession]
Yield a session from the tier-appropriate inner provider.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
tenant
|
Tenant
|
Currently active tenant. |
required |
Yields:
| Name | Type | Description |
|---|---|---|
Configured |
AsyncIterator[AsyncSession]
|
class: |
Raises:
| Type | Description |
|---|---|
IsolationError
|
When the session cannot be obtained. |
Source code in src/fastapi_tenancy/isolation/hybrid.py
apply_filters
async
¶
apply_filters(query: SelectT, tenant: Tenant) -> SelectT
Delegate query filtering to the tier-appropriate inner provider.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
query
|
SelectT
|
SQLAlchemy |
required |
tenant
|
Tenant
|
Currently active tenant. |
required |
Returns:
| Type | Description |
|---|---|
SelectT
|
Filtered query. |
Source code in src/fastapi_tenancy/isolation/hybrid.py
initialize_tenant
async
¶
initialize_tenant(
tenant: Tenant, metadata: MetaData | None = None
) -> None
Provision tenant's isolation namespace using the correct inner provider.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
tenant
|
Tenant
|
Tenant to provision. |
required |
metadata
|
MetaData | None
|
Application :class: |
None
|
Raises:
| Type | Description |
|---|---|
IsolationError
|
When provisioning fails. |
Source code in src/fastapi_tenancy/isolation/hybrid.py
destroy_tenant
async
¶
destroy_tenant(tenant: Tenant, **kwargs: Any) -> None
Deprovision tenant using the correct inner provider.
.. warning:: Permanently destroys all tenant data.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
tenant
|
Tenant
|
Tenant to destroy. |
required |
**kwargs
|
Any
|
Forwarded to the inner provider's |
{}
|
Raises:
| Type | Description |
|---|---|
IsolationError
|
When deprovisioning fails. |
Source code in src/fastapi_tenancy/isolation/hybrid.py
verify_isolation
async
¶
verify_isolation(tenant: Tenant) -> bool
Delegate isolation verification to the correct inner provider.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
tenant
|
Tenant
|
Tenant to verify. |
required |
Returns:
| Type | Description |
|---|---|
bool
|
|
Source code in src/fastapi_tenancy/isolation/hybrid.py
| Python | |
|---|---|
get_provider_for_tenant ¶
get_provider_for_tenant(
tenant: Tenant,
) -> BaseIsolationProvider
Return the inner provider that would handle tenant (no I/O).
Useful for introspection and testing.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
tenant
|
Tenant
|
Target tenant. |
required |
Returns:
| Type | Description |
|---|---|
BaseIsolationProvider
|
The inner :class: |
Source code in src/fastapi_tenancy/isolation/hybrid.py
| Python | |
|---|---|
close
async
¶
Dispose all engines and close inner providers.
Closes both inner providers first (which disposes any engines they hold
that were passed via premium_engine / standard_engine at
construction time), then disposes the shared engine created by this
provider. AsyncEngine.dispose() is idempotent, so disposing an
engine that was already disposed by an inner provider is safe and
produces no error.