Skip to content

Multi-Tenancy

Weasel provides foundational building blocks for multi-tenant database strategies through the Weasel.Core.MultiTenancy namespace. These abstractions handle the mechanics of distributing tenants across a pool of databases and are primarily consumed by higher-level libraries like Marten and Wolverine.

Overview

In a sharded multi-tenant architecture, each tenant's data lives in one of several databases. Weasel provides the low-level plumbing for this pattern:

  • A pool of databases that can accept tenants
  • Assignment strategies that decide which database a new tenant goes to
  • Tracking of which tenants are in which database
  • CLI integration so migrations run across all tenant databases

Core Abstractions

ITenantDatabasePool

Manages the registry of databases and tenant assignments:

cs
public interface ITenantDatabasePool_Sample
{
    ValueTask<IReadOnlyList<PooledDatabase>> ListDatabasesAsync(CancellationToken ct);
    ValueTask AddDatabaseAsync(string databaseId, string connectionString, CancellationToken ct);
    ValueTask MarkDatabaseFullAsync(string databaseId, CancellationToken ct);
    ValueTask<string?> FindDatabaseForTenantAsync(string tenantId, CancellationToken ct);
    ValueTask AssignTenantAsync(string tenantId, string databaseId, CancellationToken ct);
    ValueTask RemoveTenantAsync(string tenantId, CancellationToken ct);
}

snippet source | anchor

Each PooledDatabase tracks a database's identifier, connection string, and whether it is full (accepting no more tenants).

ITenantAssignmentStrategy

Determines which database a new tenant should be assigned to:

cs
public interface ITenantAssignmentStrategy_Sample
{
    ValueTask<string> AssignTenantToDatabaseAsync(
        string tenantId,
        IReadOnlyList<PooledDatabase> availableDatabases);
}

snippet source | anchor

Weasel ships with several built-in strategies:

StrategyBehavior
SmallestTenantAssignmentAssigns to the database with the fewest tenants (balanced distribution).
HashTenantAssignmentUses a hash of the tenant ID for deterministic, even distribution.
ExplicitTenantAssignmentRequires manual mapping of each tenant to a specific database.

IDatabaseSizingStrategy

Controls when a database is considered "full" and should stop accepting new tenants:

cs
public interface IDatabaseSizingStrategy_Sample
{
    ValueTask<string> FindSmallestDatabaseAsync(IReadOnlyList<PooledDatabase> databases);
}

snippet source | anchor

IDatabase.TenantIds

The IDatabase interface includes a TenantIds property that lists which tenants are assigned to that database instance:

cs
public interface IDatabase_TenantIds_Sample
{
    List<string> TenantIds { get; }
    // ... other members
}

snippet source | anchor

This is used by the migration infrastructure to apply schema changes to the correct databases when running in a multi-tenant configuration.

CLI Integration

Weasel's command-line tools (accessed through the JasperFx CLI) are multi-tenancy aware:

  • db-apply -- applies pending migrations across all tenant databases in the pool.
  • db-assert -- verifies schema consistency across all tenant databases.
  • --database flag -- filters CLI operations to a specific database by identifier, useful for targeting a single tenant database during troubleshooting.

Typical Usage

Multi-tenancy in Weasel is designed to be consumed through Marten or Wolverine rather than used directly. A typical setup through Marten looks like:

  1. Configure a ITenantDatabasePool implementation (often backed by a master database table).
  2. Choose an ITenantAssignmentStrategy for new tenant distribution.
  3. Marten handles the rest: resolving connections per tenant, running migrations across all databases, and routing queries to the right database.

If you are building a custom multi-tenant system without Marten, you would implement ITenantDatabasePool for your storage mechanism and use ITenantAssignmentStrategy to place new tenants. The IDatabase infrastructure then ensures schema migrations are applied to every database in the pool.

Released under the MIT License.