Skip to content

feat: migration tooling docs #46033

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 5 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
File renamed without changes.
141 changes: 141 additions & 0 deletions docs/orleans/migration/tooling.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
# Migration Tooling

> [!NOTE]
> If your app is already running on Orleans 7.0 or later (referred to as 7.x+ throughout), you don't need this tooling.

[Migrate from Orleans 3.x to 7.0](./guide.md) outlines the key changes introduced in Orleans 7.x+ and how to migrate from 3.x. One of the most significant breaking changes involves [serialization](./guide.md#serialization) and [grain identity representation](./guide.md#grain-identities). The migration tooling provides advanced APIs to convert persisted data to the Orleans 7.x+ format.

This guide explains how to use the tooling. If you're currently running Orleans 3.x, this will help you upgrade to 7.x+ while preserving data integrity.

**Prerequisites:**
- Orleans 3.x app

Check failure on line 11 in docs/orleans/migration/tooling.md

View workflow job for this annotation

GitHub Actions / lint

Lists should be surrounded by blank lines

docs/orleans/migration/tooling.md:11 MD032/blanks-around-lists Lists should be surrounded by blank lines [Context: "- Orleans 3.x app"] https://github.com/DavidAnson/markdownlint/blob/v0.37.4/doc/md032.md
- .NET 6.0 or higher

## Data Store Differences

Orleans 7.x+ introduces a new grain identity format that changes how grain-related data is stored—for example, in Azure Table Storage. The table below compares a sample grain entry from Orleans 3.x and 7.x+, as stored in Azure Table Storage:

| Version | PartitionKey | GrainReference |
|----------------|-----------------------------------------------|------------------------------------------------------------------------------|
| **Orleans 3.x** | 3787bc57a0a1459388a8a5ecb9dcd843_1C2B3D8D | GrainReference=0000000000000000000000000000012f03fffffffcd9142b |
| **Orleans 7.x+**| 3787bc57a0a1459388a8a5ecb9dcd843_1C2B3D8D | simplepersistent_12F |

In Orleans 7.x+, `GrainReference` is a structured, human-readable string. This schema change **breaks** direct storage compatibility between Orleans 3.x and 7.x+.

## Migration Plan

Your Orleans 3.x application uses persistent storage to store grain state using the older format. This storage may be Azure Table Storage, but the tooling is not limited to that. The migration plan involves pre-creating a new storage system where the same grain data will be saved using the Orleans 7.x+ format. This new storage can be of a different type—for example, you can migrate from Azure Table Storage to Cosmos DB.

Throughout this guide:
- The **source storage** refers to the existing storage used by Orleans 3.x.

Check failure on line 30 in docs/orleans/migration/tooling.md

View workflow job for this annotation

GitHub Actions / lint

Lists should be surrounded by blank lines

docs/orleans/migration/tooling.md:30 MD032/blanks-around-lists Lists should be surrounded by blank lines [Context: "- The **source storage** refer..."] https://github.com/DavidAnson/markdownlint/blob/v0.37.4/doc/md032.md
- The **destination storage** refers to the new storage compatible with Orleans 7.x+.

The migration tooling supports two modes of migration: **runtime** and **offline**.

At runtime Orleans reads and writes grain state during execution via [`Grain<TGrainState>.ReadStateAsync()`](https://learn.microsoft.com/dotnet/api/orleans.grain-1.readstateasync) and [`Grain<TGrainState>.WriteStateAsync()`](https://learn.microsoft.com/dotnet/api/orleans.grain-1.writestateasync). The migration tooling can intercept these calls to replicate data into the destination storage.

Reminder data is also stored differently in Orleans 7.x+, and its migration follows a similar pattern. Specific APIs for reminder migration will be covered later.

> [!NOTE]
> Migration does not require application downtime. It is designed to be run over an extended period—potentially hours, days, or weeks — depending on your system’s scale. A gradual rollout is recommended, with careful monitoring to avoid disruptions and validate the correctness of migrated state.

### Migration Steps

1. **Prepare destination storage** to hold migrated data.
2. **Register migration components** so Orleans knows which storage is the source and which is the destination.
3. **Wire up source/destination pairs** by registering `MigrationGrainStorage` or `MigrationReminderTable`, and select the appropriate migration mode.
4. **Run the application** for a prolonged period of time to ensure grains are accessed and migrated.
5. **Adjust migration mode** to reduce or eliminate dependency on source storage. Repeat as needed until you are ready to transition fully to Orleans 7.x+.

### How to connect Migration Tooling to your app

Migration tooling is distributed as a set of NuGet packages:

- [Microsoft.Orleans.Persistence.Migration](https://www.nuget.org/packages/Microsoft.Orleans.Persistence.Migration) - main package containing core types for the migration functionality.

Check failure on line 54 in docs/orleans/migration/tooling.md

View workflow job for this annotation

GitHub Actions / lint

Trailing spaces

docs/orleans/migration/tooling.md:54:186 MD009/no-trailing-spaces Trailing spaces [Expected: 0 or 2; Actual: 1] https://github.com/DavidAnson/markdownlint/blob/v0.37.4/doc/md009.md
- [Microsoft.Orleans.Persistence.AzureStorage.Migration](https://www.nuget.org/packages/Microsoft.Orleans.Persistence.AzureStorage.Migration) - implements migration to azure storage (both blob or table).
- [Microsoft.Orleans.Persistence.Cosmos](https://www.nuget.org/packages/Microsoft.Orleans.Persistence.Cosmos) or [Microsoft.Orleans.Persistence.Cosmos.Migration](https://www.nuget.org/packages/Microsoft.Orleans.Persistence.Cosmos.Migration) implement migration to CosmosDB. Since there was no cosmosDb storage support in Orleans 3.x, we introduce both here.

After choosing and installing a needed package (lets say we are performing migration to AzureStorage), you should do the following. Imagine you had such a storage connection registration:
```csharp

Check failure on line 59 in docs/orleans/migration/tooling.md

View workflow job for this annotation

GitHub Actions / lint

Fenced code blocks should be surrounded by blank lines

docs/orleans/migration/tooling.md:59 MD031/blanks-around-fences Fenced code blocks should be surrounded by blank lines [Context: "```csharp"] https://github.com/DavidAnson/markdownlint/blob/v0.37.4/doc/md031.md
.AddAzureBlobGrainStorage(options =>
{
...
})
```

Below is step-by-step explanation of how to define migration tooling behavior for migrating azure blob storage to another blob storage (in Orleans7.x+ format). After registrations are in place, a new deployment of application is needed to start migrating the data.

1) Add `AddMigrationTools()`. This registers core components of the migration tooling.
2) Make your initial (source) registration named. For example - `source-storage`:

Check failure on line 69 in docs/orleans/migration/tooling.md

View workflow job for this annotation

GitHub Actions / lint

Lists should be surrounded by blank lines

docs/orleans/migration/tooling.md:69 MD032/blanks-around-lists Lists should be surrounded by blank lines [Context: "2) Make your initial (source) ..."] https://github.com/DavidAnson/markdownlint/blob/v0.37.4/doc/md032.md
```csharp

Check failure on line 70 in docs/orleans/migration/tooling.md

View workflow job for this annotation

GitHub Actions / lint

Fenced code blocks should be surrounded by blank lines

docs/orleans/migration/tooling.md:70 MD031/blanks-around-fences Fenced code blocks should be surrounded by blank lines [Context: "```csharp"] https://github.com/DavidAnson/markdownlint/blob/v0.37.4/doc/md031.md
.AddAzureBlobGrainStorage("source-storage", options =>
{
...
})
```

Check failure on line 75 in docs/orleans/migration/tooling.md

View workflow job for this annotation

GitHub Actions / lint

Fenced code blocks should be surrounded by blank lines

docs/orleans/migration/tooling.md:75 MD031/blanks-around-fences Fenced code blocks should be surrounded by blank lines [Context: "```"] https://github.com/DavidAnson/markdownlint/blob/v0.37.4/doc/md031.md
3) Register the destination storage - for example `destination-storage`. Note - it has `Migration` in the name explicitly defining the **destination** storage:

Check failure on line 76 in docs/orleans/migration/tooling.md

View workflow job for this annotation

GitHub Actions / lint

Ordered list item prefix

docs/orleans/migration/tooling.md:76:1 MD029/ol-prefix Ordered list item prefix [Expected: 1; Actual: 3; Style: 1/1/1] https://github.com/DavidAnson/markdownlint/blob/v0.37.4/doc/md029.md

Check failure on line 76 in docs/orleans/migration/tooling.md

View workflow job for this annotation

GitHub Actions / lint

Lists should be surrounded by blank lines

docs/orleans/migration/tooling.md:76 MD032/blanks-around-lists Lists should be surrounded by blank lines [Context: "3) Register the destination st..."] https://github.com/DavidAnson/markdownlint/blob/v0.37.4/doc/md032.md

Check failure on line 76 in docs/orleans/migration/tooling.md

View workflow job for this annotation

GitHub Actions / lint

Lists should be surrounded by blank lines

docs/orleans/migration/tooling.md:76 MD032/blanks-around-lists Lists should be surrounded by blank lines [Context: "3) Register the destination st..."] https://github.com/DavidAnson/markdownlint/blob/v0.37.4/doc/md032.md
```csharp
.AddMigrationAzureBlobGrainStorage("destination-storage", options =>
{
...
})
```
4) Now you need to connect a pair of source-destination storage. That can be done via `AddMigrationGrainStorageAsDefault` or `AddMigrationGrainStorage`:
```csharp
.AddMigrationGrainStorageAsDefault(options =>
{
options.SourceStorageName = "source-storage";
options.DestinationStorageName = "destination-storage";

// mode selection
options.Mode = GrainMigrationMode.ReadDestinationWithFallback_WriteBoth;
})
```

Reminders follow a similar pattern. Snippet below shows registration of azure table storage as source reminder storage, and azure table storage as destination storage for migration connecting them via `UseMigrationReminderTable` registration:
```csharp
.UseAzureTableReminderService("source", options =>
{
...
})
.UseMigrationAzureTableReminderStorage("destination", options =>
{
...
})
.UseMigrationReminderTable(options =>
{
options.SourceReminderTable = "source";
options.DestinationReminderTable = "destination";
options.Mode = ReminderMigrationMode.ReadDestinationWithFallback_WriteBoth;
})
```

### Runtime Migration

Most of the grains are imagined to be accessed during the application runtime, and they will be written into the destination storage in Orleans7.x+ format.

Registering components as described [above](#how-to-connect-migration-tooling-to-your-app) will make your Orleans application perform a runtime-migration of data. Every time grain state is being written or read from the persistent storage, it will decide to do it against both source and destination storage or a single one depending on the mode.

Choose an appropriate mode carefully - it controls the behavior and how dependent application behavior is on the source storage (or on the destination storage). Here are possible `Orleans.Persistence.Migration.GrainMigrationMode` values you can choose:

- `Disabled` goes through the migration components, but only communicates with the source storage. You will not see migrated data in destination storage if using such a mode.
- `ReadSource_WriteBoth` will replicate data to destination storage, but reads will be done only from the source storage. It is the safest option to actually perform migration, but still be sure application behavior remains robust.
- `ReadDestinationWithFallback_WriteBoth` is similar to previous one, but read will be done from destination storage firstly, and then in case data is not found will fallback to reading from source storage. Should be used for later stages of the migration
- `ReadDestinationWithFallback_WriteDestination` is a mode which will not write grain states into the source storage. If you are running in this mode without problems, then you are ready to migrate to Orleans7.x+ app
- `ReadWriteDestination` is a mode to only use destination storage.

Reminders follows the similar pattern but using `Orleans.Persistence.AzureStorage.Migration.Reminders.ReminderMigrationMode`.

### Offline Migration

However, not all of the grains are expected to be updated during the runtime of Orleans application. Offline migration helps in such a case by providing a way to run a background work in migrating the data.

Offline migration capabilities is added via another registration: `AddDataMigrator(source, destination)`. That registers `DataMigrator`, which you can resolve and invoke explicitly, or choose an appropriate option to configure it as a background worker.

`DataMigrator` does loop through all the grains or reminders in the source storage, and performs a write of the same data in the destination storage in Orleans7x+ format.

`DataMigrator` only works on a single (primary) silo (eliminating conflicts accross different nodes), has an option to control operations per second. View `Orleans.Persistence.Migration.DataMigratorOptions` for details.

### Notes

At the moment of writing this guideline migration tooling is in active development phase and is already available in preview. If you encounter any issues please submit a [new issue](https://github.com/dotnet/orleans/issues/new) and hopefully we will help you migrate to the latest Orleans
8 changes: 6 additions & 2 deletions docs/orleans/toc.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,6 @@ items:
href: overview.md
- name: Benefits
href: benefits.md
- name: "Migrate from Orleans 3.x to 7.0"
href: migration-guide.md
- name: Quickstarts
items:
- name: Build your first Orleans app
Expand Down Expand Up @@ -241,6 +239,12 @@ items:
href: implementation/load-balancing.md
- name: Unit testing
href: implementation/testing.md
- name: Migrate Orleans3.x to Orleans7.x+
items:
- name: Overview
href: migration/guide.md
- name: Migration Tooling
href: migration/tooling.md
- name: Resources
items:
- name: Frequently asked questions
Expand Down
Loading