Tenant migration
This article explains how Tenant Management handles tenant database migration and cluster version changes in a multitenant deployment.
The topic covers three related concerns:
- Creating a tenant and initializing its database
- Migrating an existing tenant database on a given cluster version
- Changing the cluster version associated with a database and rebuilding tenant resolution caches
Overview
In multitenant mode, Tenant Management is responsible for orchestrating database migration, but the actual migration is executed by the target business cluster.
The high-level flow is:
- Tenant Management decides which database and cluster version are targeted.
- Tenant Management triggers the
MigrateDatabaseworkflow. - The business cluster receives the migration request through pub/sub.
- The business cluster validates that the requested cluster name and version match itself.
- The business cluster executes the schema migration.
- If the migration is not a dry run, tenant database migration interceptors are executed.
- Migration results are published back to Tenant Management.
- If the cluster version association changed, Tenant Management rebuilds tenant caches so routing and resolution reflect the new version.
Creation vs migration
Tenant creation and tenant migration are related but not identical:
- Tenant creation creates or initializes the tenant database for a new tenant.
- Tenant migration updates an existing tenant database schema for the cluster version associated with the target database.
The tenant creation flow is described in Initialization and customization. This article focuses on the migration and cluster-version management operations exposed by Tenant Management.
Main server methods
Tenant Management exposes three important server methods for this topic.
MigrateDatabase
Purpose: migrate the database currently associated with a given database server.
Main arguments:
databaseServerId: target database serverallowDataLoss: allows destructive migration operations when neededdryRun: simulates the migration without applying it
Behavior:
- Resolves the target database server and the active tenants attached to it.
- Publishes a
MigrateDatabaseevent containing the cluster name, cluster version, database connection information, tenant list,allowDataLoss,dryRun, and a generatedmigrationIdentifier. - Returns the
migrationIdentifierimmediately.
Notes:
- This method does not change the cluster version associated with the database.
- It is the base migration operation reused by the other methods.
- The returned
migrationIdentifiercan be used to correlate migration results in Tenant Management. - When the tenant manager receives the migration results, it checks the
dryRunflag to decide whether to update runtime state and trigger cache rebuilds.
ChangeDatabaseClusterVersion
Purpose: switch a database server to an existing cluster version, then migrate the database.
Main arguments:
databaseServerId: target database serverclusterVersionId: existing target cluster versiondryRun: simulates the migration workflow
Behavior:
- If
dryRunisfalse, updates theClusterVersionIdof the target database server. - Saves the change in Tenant Management.
- Calls
MigrateDatabasefor the same database. - If
dryRunisfalse, rebuilds all tenant caches.
Use this method when the target cluster version already exists in Tenant Management and you want to switch the database to that version before migrating.
SetDatabaseClusterVersion
Purpose: set a cluster version by version string for a database, then migrate and force only that full cache rebuild to complete before returning.
Main arguments:
databaseName: target database nameversion: target version stringbuildStoreTimeoutInSeconds: timeout used for the cache rebuild phase
Behavior:
- Loads the database server by name.
- Looks for an existing cluster version with the same cluster name and requested version.
- If it exists, switches the database server to that cluster version.
- If it does not exist, updates the current cluster version record in place.
- Saves the change in Tenant Management.
- Calls
MigrateDatabasefor the database. - Forces only this full cache rebuild to run in-process through
RebuildAllStoresAsync(..., useTaskRunnerOverride: false)so caches are rebuilt synchronously before returning without changing the default partial rebuild mode.
Use this method when you want Tenant Management to set the effective cluster version from a version string and ensure caches are immediately consistent after the migration sequence.
Differences between the methods
| Method | Changes database cluster version | Accepts dry run | Rebuilds tenant caches | Typical use |
|---|---|---|---|---|
MigrateDatabase |
No | Yes | No direct cache rebuild | Re-run a migration on the currently assigned cluster version |
ChangeDatabaseClusterVersion |
Yes, by existing clusterVersionId |
Yes | Yes, asynchronous/default rebuild path | Switch to an already known cluster version |
SetDatabaseClusterVersion |
Yes, by databaseName and version string |
No | Yes, synchronous in-process rebuild | Set or align the effective version string and refresh caches immediately |
Cluster matching on the business cluster
When the business cluster receives the MigrateDatabase event, it validates that:
- The cluster name matches the current cluster
- The cluster version matches the current cluster version
If the receiving cluster does not match, the migration request is accepted but not executed on that cluster.
This is why a safe multiversion deployment usually consists of:
- Deploying the new cluster version in parallel
- Updating Tenant Management to point the database to the new cluster version
- Triggering the migration workflow
- Rebuilding tenant caches so routing and resolution use the new version
Cache rebuild after version changes
Changing the cluster version in Tenant Management is not enough on its own. The multitenant caches must also be rebuilt so the rest of the platform sees the updated routing information.
The relevant caches are described in Tenant Resolution Caching.
Important behavior:
ChangeDatabaseClusterVersionrebuilds caches after a non-dry-run version switch.SetDatabaseClusterVersionexplicitly forces a per-call synchronous full cache rebuild in the backend flow before returning.MigrateDatabasealone does not rebuild caches because it does not modify the version association itself.
Dry run and data loss
Dry run support differs between the methods:
MigrateDatabasesupportsdryRunandallowDataLoss.ChangeDatabaseClusterVersionsupportsdryRunand usesMigrateDatabasewithallowDataLoss = false.SetDatabaseClusterVersiondoes not expose a dry run option.
When dryRun is enabled, the goal is to validate the migration plan without applying it and without updating runtime state that depends on the effective cluster version.
Run-once tenant interceptors and retries
Some business clusters register IRunOnceTenantDatabaseMigrationInterceptor implementations so a tenant-specific post-migration treatment is skipped once it has already been applied for that tenant.
Important behavior:
- The run-once marker is persisted only after
OnTenantDatabaseMigratedAsync(...)returns success. - The interceptor business logic and the marker persistence are not atomic.
- If the interceptor commits business changes but the marker cannot be saved afterwards, the migration is reported as failed and the interceptor is retried on the next migration.
- Run-once interceptors must therefore remain idempotent and retry-safe, even when the functional intention is to apply the treatment only once.
- The execution marker is stored in
$NeosObjectwithObjectType = 'TenantMigrationInterceptor'andObjectName = 'tenant-id:<TenantId>|interceptor:<InterceptorName>'.
For implementation guidance, see Database migration customization.