Table of Contents

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:

  1. Creating a tenant and initializing its database
  2. Migrating an existing tenant database on a given cluster version
  3. 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:

  1. Tenant Management decides which database and cluster version are targeted.
  2. Tenant Management triggers the MigrateDatabase workflow.
  3. The business cluster receives the migration request through pub/sub.
  4. The business cluster validates that the requested cluster name and version match itself.
  5. The business cluster executes the schema migration.
  6. If the migration is not a dry run, tenant database migration interceptors are executed.
  7. Migration results are published back to Tenant Management.
  8. 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:

  1. Tenant creation creates or initializes the tenant database for a new tenant.
  2. 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:

  1. databaseServerId: target database server
  2. allowDataLoss: allows destructive migration operations when needed
  3. dryRun: simulates the migration without applying it

Behavior:

  1. Resolves the target database server and the active tenants attached to it.
  2. Publishes a MigrateDatabase event containing the cluster name, cluster version, database connection information, tenant list, allowDataLoss, dryRun, and a generated migrationIdentifier.
  3. Returns the migrationIdentifier immediately.

Notes:

  1. This method does not change the cluster version associated with the database.
  2. It is the base migration operation reused by the other methods.
  3. The returned migrationIdentifier can be used to correlate migration results in Tenant Management.
  4. When the tenant manager receives the migration results, it checks the dryRun flag 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:

  1. databaseServerId: target database server
  2. clusterVersionId: existing target cluster version
  3. dryRun: simulates the migration workflow

Behavior:

  1. If dryRun is false, updates the ClusterVersionId of the target database server.
  2. Saves the change in Tenant Management.
  3. Calls MigrateDatabase for the same database.
  4. If dryRun is false, 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:

  1. databaseName: target database name
  2. version: target version string
  3. buildStoreTimeoutInSeconds: timeout used for the cache rebuild phase

Behavior:

  1. Loads the database server by name.
  2. Looks for an existing cluster version with the same cluster name and requested version.
  3. If it exists, switches the database server to that cluster version.
  4. If it does not exist, updates the current cluster version record in place.
  5. Saves the change in Tenant Management.
  6. Calls MigrateDatabase for the database.
  7. 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:

  1. The cluster name matches the current cluster
  2. 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:

  1. Deploying the new cluster version in parallel
  2. Updating Tenant Management to point the database to the new cluster version
  3. Triggering the migration workflow
  4. 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:

  1. ChangeDatabaseClusterVersion rebuilds caches after a non-dry-run version switch.
  2. SetDatabaseClusterVersion explicitly forces a per-call synchronous full cache rebuild in the backend flow before returning.
  3. MigrateDatabase alone 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:

  1. MigrateDatabase supports dryRun and allowDataLoss.
  2. ChangeDatabaseClusterVersion supports dryRun and uses MigrateDatabase with allowDataLoss = false.
  3. SetDatabaseClusterVersion does 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:

  1. The run-once marker is persisted only after OnTenantDatabaseMigratedAsync(...) returns success.
  2. The interceptor business logic and the marker persistence are not atomic.
  3. 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.
  4. Run-once interceptors must therefore remain idempotent and retry-safe, even when the functional intention is to apply the treatment only once.
  5. The execution marker is stored in $NeosObject with ObjectType = 'TenantMigrationInterceptor' and ObjectName = 'tenant-id:<TenantId>|interceptor:<InterceptorName>'.

For implementation guidance, see Database migration customization.

  1. Initialization and customization
  2. Tenant Management
  3. Tenant Resolution Caching
  4. Database migration customization
  5. Deployment guide