paritytech / paritytech/web3-storage

[Provider] Storage data-format versioning & migration (fail loudly on incompatible on-disk data)

Open
#375 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
Rust
Stars
12
Forks
3
Avg merge
2d 2h
Merged PRs (30d)
33

Description

Motivation

The provider's persisted state (RocksDB today) has no format identity: nothing on disk says which encoding or schema version wrote it. Any change to the serialization silently invalidates existing provider data, and the node does not notice at startup — it limps along treating undecodable buckets as missing.

PR #365 is the first real instance: switching bincodeparity-scale-codec means data written before the upgrade no longer decodes. The failure mode today is soft and quiet: get_bucket logs a tracing::warn! and returns None, which callers surface as Error::BucketNotFound — an operator sees "bucket not found" errors instead of "your database format is incompatible, resync/migrate required".

SCALE makes this class of problem sharper: the encoding is purely positional (no field names or tags on disk), so even reordering fields of BucketState forks the disk format with no compiler or test signal — and same-typed field swaps decode "successfully" with values scrambled.

Whatever DB engine we end up with (#101), we need a story for evolving the on-disk format without stranding or corrupting existing provider data.

Current state

  • crates/providers/storage/src/backend/disk.rsBucketState and StoredNode are SCALE-encoded raw into column families; decode failures warn and return None.
  • A metadata column family already exists (CF_METADATA, currently holding only the persisted nonce counter under KEY_NONCE) — a natural home for a format-version marker.
  • DiskStorage::new opens the DB unconditionally; there is no compatibility check of any kind.
  • No golden-vector tests pin the encoded byte layout of persisted types (round-trip tests added in #365 pass even after a format-breaking field reorder).

Proposed / potential solutions

  • Format-version marker in CF_METADATA, written on DB creation and checked in DiskStorage::new: unknown/missing version ⇒ refuse to start with an actionable error (resync or run migration) instead of per-value decode warnings.
  • Migration hooks keyed off that marker: on version mismatch, run registered migrations (or, for a storage provider, possibly the cheaper answer: wipe and resync from chain/replicas — worth deciding explicitly).
  • Golden-vector tests for every persisted type (assert_eq!(hex::encode(value.encode()), "…")) so an accidental format change fails CI.
  • Keep the mechanism behind StorageBackend / engine-agnostic, so the engine evaluation in #101 can swap the DB without changing the versioning contract.

TODO

  • Decide the migration philosophy for provider data: migrate in place vs. wipe-and-resync per version bump
  • Add format-version marker to CF_METADATA + startup check in DiskStorage::new
  • Add golden-vector encoding tests for BucketState and StoredNode
  • Document the version-bump procedure for future format changes (when to bump, how to add a migration)

Related issues

  • #101 — DB engine evaluation (engine choice constrains the migration mechanics)
  • #36 — default storage/bucket internal implementation
  • #365 — bincode → SCALE (the breaking change that motivated this)
  • #254 / #259 — provider↔chain protocol versioning; distinct concern, but the startup guard could live next to that handshake

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with crates/providers/storage/src/backend/disk.rs, especially BucketState, StoredNode, CF_METADATA, and DiskStorage::new. Review the related issues #101 and #365 before deciding whether incompatible data should be migrated or wiped and resynced. Done means the chosen policy is implemented with startup compatibility handling, golden-vector tests, and documented version-bump procedures.

Written by the indexing model from the issue text.

Assessment

Tech stack
rust
Domain
backend, database
Issue type
Feature
Difficulty
5/5
Estimated time
Over a week
Activity status
Active
Clarity
Mostly clear
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.