Skip to content

[FRAME Core] Simple Multi-block Migrations #198

Closed
paritytech/substrate
#14275
@gavofyork

Description

@gavofyork

Needed for non-trivial migrations on parachains, and helpful for such migration on Relay/solo chains.

This is just a simple strategy and will work in probably around 80% of circumstances. It's good for pallets which are not depended upon by code which fires in on_initialize.

Have a migrations pallet (not too dissimilar from that of Moonbeam). This is configured with a tuple of all migrations, which provides introspection and access to each migration which needs to be completed. Migrations look like calls/tasks: they have a possibly-pessimistic weight estimation and can return the actual weight consumed. They are always benchmarked. Migrations should be written to proceed "piecewise" (e.g. one unit at a time, rather than migrating all entries of a storage map all at once).

Each migration defines a type Cursor which is Default + FullCodec + MaxEncodeLen. The migration function accepts a Cursor value and returns an Option<Cursor> value which is None if the migration is complete. If Some then it is up to the migration pallet to ensure it is used in the next call to the migrate function, storing it in state if weight constraints deem it necessary to wait until the next block before resuming.

The migrations pallet should suspend all non-critical subsystems in on_runtime_upgrade. This includes XCM and transaction processing. It should only re-enable them when all migrations' cursors are None.

One new hook function should be added: fn poll() -> Weight. This is guaranteed to execute regularly but not necessarily every block, providing a softer version of on_initialize. All Substrate pallets should have their on_initialize/on_finalize functions checked; if they can be moved or adapted to poll, they should be. While on_initialize/on_finalize should be called within an incomplete migration, poll need not (and indeed, should not) be.

Long-term

Deprecate on_* hooks

on_initialize/on_finalize should be deprecated. Pallets which absolutely require per-block-execution hooks on hard-deadlines (HDH) or mandatory extrinsics (ME) should go through a System config trait item which can give them low-level access at the cost of requiring the System config trait to be explicitly configured to reference them. There should be a regular facility for soft-deadline execution (which will work fine in almost all use-cases of parachain teams) which is implemented using tasks.

Guarantees

We should mark all storage items so they fall into one of three groups:

  1. Can be accessed during a hard-deadline-hook or mandatory extrinsic
  2. Can be accessed during a multi-block-migration
  3. Unavailable to both MBMs and HDHs/MIs (the default)

Ideally we could have some means of statically verifying that HDH code only accessed items marked as (1) and MBM code only accessed items marked as (2).

Unfortunately, this probably cannot be done statically (can it?) but at least could form a basis for code reviews and audit.

Metadata

Metadata

Assignees

Labels

T1-FRAMEThis PR/Issue is related to core FRAME, the framework.

Type

No type

Projects

Status

Done

Status

Done

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions