Skip to content

feat: Add async migrations - #470

Open
jsonbailey wants to merge 5 commits into
mainfrom
jb/sdk-2767/async-migrations
Open

feat: Add async migrations#470
jsonbailey wants to merge 5 commits into
mainfrom
jb/sdk-2767/async-migrations

Conversation

@jsonbailey

@jsonbailey jsonbailey commented Jul 27, 2026

Copy link
Copy Markdown
Contributor

Adds the async migration surface for the async Python SDK — AsyncMigrator, AsyncMigratorBuilder, AsyncMigrationConfig, and the AsyncMigratorFn type — in ldclient/migrations/async_migrator.py, eagerly exported from ldclient/migrations/__init__.py (import-cheap: asyncio stdlib only, no aiohttp).

Also carries the migration config/builder dedup shared by the sync and async surfaces: types.py gains _MigrationConfigBase[_MigratorFnT] (Generic) and _MigratorBuilderBase (fluent-setter mixin), and migrator.py's MigratorBuilder is re-based onto that mixin. These are behavior-preserving refactors of already-released sync code.

The public async classes carry experimental .. caution:: blocks.

Self-contained: depends only on already-merged foundation work. The AsyncLDClient/Context references are TYPE_CHECKING-only (annotations kept lazy via from __future__ import annotations); no runtime import of any not-yet-merged async module.

No CHANGELOG or version changes (handled at release).

Tracked internally: SDK-2767


Note

Medium Risk
New experimental API on the migration/data-path; wrong stage or dual-write behavior could affect customer backends, though it mirrors proven sync logic and is gated with caution docs.

Overview
Adds an experimental async migration API for the async Python SDK, parallel to the existing sync Migrator.

New public surface in async_migrator.py: AsyncMigrator / AsyncMigratorImpl, AsyncMigratorBuilder, AsyncMigrationConfig, and AsyncMigratorFn (coroutine callbacks returning Result). read/write await AsyncLDClient.migration_variation, run old/new origins per migration stage (including parallel/serial/random dual-read via asyncio.gather), optional read consistency comparison, and call track_migration_op synchronously (not awaited). AsyncMigratorBuilder validates read/write config on build() and returns an error string when missing.

ldclient/migrations/__init__.py eagerly exports the async types (stdlib asyncio only, documented as import-cheap). Broad pytest coverage in test_async_migrator.py mirrors sync migrator behavior (payloads, invoked/latency/error/consistency telemetry, exceptions, execution order).

Reviewed by Cursor Bugbot for commit d62ed3c. Bugbot is set up for automated code reviews on this repo. Configure here.

@jsonbailey
jsonbailey force-pushed the jb/sdk-2767/async-migrations branch from abe7dac to c735e0e Compare July 27, 2026 22:01
@jsonbailey

Copy link
Copy Markdown
Contributor Author

@cursor review

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ Bugbot reviewed your changes and found no new issues!

Comment @cursor review or bugbot run to trigger another review on this PR

Reviewed by Cursor Bugbot for commit c735e0e. Configure here.

@jsonbailey
jsonbailey force-pushed the jb/sdk-2767/async-migrations branch from c735e0e to ec7746a Compare July 29, 2026 19:31
…ic base

Drop _MigrationConfigBase and give MigrationConfig and AsyncMigrationConfig
their own bodies. The shared base saved only a few trivial passthrough
properties while adding generic indirection, and full duplication matches how
Config/AsyncConfig are handled. MigrationConfig reverts to its pre-async
standalone form.
Give MigratorBuilder and AsyncMigratorBuilder their own read_execution_order/
track_latency/track_errors methods instead of sharing a base for three trivial
property setters. MigratorBuilder and types.py revert to their pre-async form.
@jsonbailey
jsonbailey marked this pull request as ready for review July 30, 2026 21:59
@jsonbailey
jsonbailey requested a review from a team as a code owner July 30, 2026 21:59
@@ -1,8 +1,15 @@
# async_migrator is import-cheap (asyncio stdlib only, no aiohttp), so it is

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ASD-STE100?

Comment thread ldclient/migrations/types.py Outdated

return authoritative_result

async def __write_both(self, authoritative: AsyncExecutor, nonauthoritative: AsyncExecutor, tracker: OpTracker) -> Tuple[OperationResult, Optional[OperationResult]]:

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we have a standard approach we are using for task cancellation? I am concerned about cancelling between the writes potentially. I am not familiar enough with the ecosystem to know if we can just say not to do that.

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes using default effort and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, have a team admin enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit d62ed3c. Configure here.

nonauthoritative_result = await nonauthoritative.run()
tracker.invoked(nonauthoritative.origin)

return authoritative_result, nonauthoritative_result

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Dual-write cancellation leaves origins inconsistent

Medium Severity

__write_both awaits the authoritative write, then the nonauthoritative write, with no protection against task cancellation between those awaits. In dual-write stages, a cancel after the first write commits can skip the second origin and also skip track_migration_op, leaving origins out of sync. This is a new asyncio risk relative to the sync migrator.

Additional Locations (1)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit d62ed3c. Configure here.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants