简体中文 English 繁體中文 日本語 Русский
本文为静态镜像,内容以交互版为准 在交互式文档中心打开 →

Shadow Modules and Gradual Promotion

A shadow module is a new version of the same module, running in parallel with the live old version under an independent owner (e.g., roll_shadow). It receives copies of real events, and its outbound calls are intercepted and logged instead of being actually dispatched. By comparing the behaviors of both versions in shadow_diff, you can confirm there are no issues before promoting the shadow to become the live version with a single promote action. You can also dismiss the shadow at any time. No code changes are required for the module; all operations are driven at runtime via APIs: similar to load / unload / reload, these operational actions can be directly invoked through the Dashboard or custom management modules, with no configuration files needed.

  1. Start: await sdk.module.shadow_start("roll", source="path/to/v2") —— The new version runs in parallel with the old version under an independent owner (defaulting to the path name).
  2. The shadow does not participate in real distribution or dependency graphs: commands with the same name go into the shadow directory, routes are registered but not mounted, lifecycle broadcasts are silent, and module.call and dependency resolution still point to v1.
  3. Promotion must always be confirmed by a human: await sdk.module.promote_shadow("roll"), and if it fails, the old instance is automatically rolled back to continue serving; dismiss_shadow can be used at any time to abandon the shadow.

Quick Start

# v2 code: Standard module format, shadow-agnostic (any directory, e.g. downloads/roll_v2/)
# In production bot (Dashboard / Management module call), start shadow with one line:
await sdk.module.shadow_start("roll", source="downloads/roll_v2")
# → Shadow runs independently as owner "roll_v2", coexisting with v1, with outbound traffic intercepted and tracked

# During trial period, compare behaviors:
report = sdk.module.shadow_diff("roll")
# {"shadow_owner": "roll_v2", "count": 3, "aligned": [...]}

After confirming correctness, promote:

await sdk.module.promote_shadow("roll")   # Promote, rollback to v1 on failure
await sdk.module.dismiss_shadow("roll")   # Or: discard shadow

Five Isolation Gates

Gate Mechanism
Event Copy The shadow handler receives an independent copy of the event (marked with shadow) — modifications, claims, or stopping propagation by the shadow only affect the copy, not the original event chain
Outbound Gate All Send DSL and Api calls from the shadow are intercepted and recorded (with a fake success response), without actually being sent — the shadow will not send duplicate replies
Storage Overlay KV writes from the shadow go into an in-memory overlay and are discarded from the persistent store; reads first check the overlay, and if not found, transparently read from the real store (gray-scale runs against real data); deletes are recorded as tombstones
Routing Shield HTTP/WS/SSE routes from the shadow are only registered but not mounted; commands with the same name are registered in the shadow command directory, and platform event method injection is prohibited
Lifecycle Silence The shadow does not broadcast its own lifecycle events, nor does it participate in the ecosystem dependency graph (module.call and dependency resolution still point to v1, preventing half-finished components from being depended upon)

Configuration Inheritance: The shadow inherits the configuration section from the original module by default (otherwise gray-scale would be distorted), and the configuration takes effect in place after promotion.

Honest Boundaries (Can't Be Blocked)

Promote and Rollback

The promote process: take a snapshot of the current version (including cascading dependents) → completely uninstall → register the shadow instance with the real name and load it. If any step fails, automatic rollback occurs and the old instance continues to serve (best-effort semantics: side effects executed by on_unload cannot be undone, and the old instance is in a cleaned-up state after rollback). Promoted shadow resources are reclaimed and bindings are released; the original module configuration section takes effect in place.

Persistence Reminder: promote is a runtime switch—after a restart, v2 will still be used. You need to persistently install the new version (pip install -U the new version / replace the plugin file). The runtime switch will not automatically handle package management for you.