Skip to content

feat(graphql): migrate dynamic fields to backward diff using objects_version#11477

Draft
tomxey wants to merge 3 commits intoinfra/feat/backward-history-consistent-viewsfrom
sc-platform/dynamic-fields-consistency-using-objects-version
Draft

feat(graphql): migrate dynamic fields to backward diff using objects_version#11477
tomxey wants to merge 3 commits intoinfra/feat/backward-history-consistent-viewsfrom
sc-platform/dynamic-fields-consistency-using-objects-version

Conversation

@tomxey
Copy link
Copy Markdown
Contributor

@tomxey tomxey commented May 8, 2026

Description of change

Switch DynamicField::paginate from forward diff (objects_snapshot + objects_history) to backward diff (checkpointed_objects + objects_backward_history). The version-pinned path uses the new backward_view::version_pinned query; the no-version-pin path falls through to the existing consistent-view query.

Read-side:

  • New backward_view::version_pinned query mirrors consistent::query in shape (Source A from checkpointed_objects + Source B from objects_backward_history merged via DISTINCT ON), but pivots on a version-axis cutoff rather than the cp-axis. For each candidate the target version is the largest objects_version.object_version that is <= parent_version. The state row at (object_id, target_version) is read from checkpointed_objects (current state) or objects_backward_history (prior state). Tombstone target versions exist in objects_version but have no corresponding state row in either table, so the candidate is correctly excluded.
  • Source A excludes rows whose (object_id, object_version) also appears in objects_backward_history, suppressing duplicates during the brief race window where backward_history has been written but checkpointed_objects has not yet been updated.
  • Behaviour matches the original forward-diff "earliest / produced-at" semantics: any DF with lamport <= parent_version is in scope, and the latest pre-cutoff state of each candidate is returned.

Tests:

  • dof_add_reclaim_transfer_reclaim_add.move (delete-recreate of a Field-object with the same derived id) passes without any new schema column: the version-axis approach disambiguates the intra-checkpoint deletion gap via objects_version's tombstone versions.
  • dynamic_fields.move: two queries renamed away from the snapshot-lag "outside consistent range" naming (those semantics don't apply under backward-diff retention) and re-recorded.

Links to any relevant issues

fixes #11019

How the change has been tested

Describe the tests that you ran to verify your changes.

Make sure to provide instructions for the maintainer as well as any relevant configurations.

  • Basic tests (linting, compilation, formatting, unit/integration tests)
  • Patch-specific tests (correctness, functionality coverage)

Infrastructure QA (only required for crates that are maintained by @iotaledger/infrastructure)

  • Synchronization of the indexer from genesis for a network including migration objects.
  • Restart of indexer synchronization locally without resetting the database.
  • Restart of indexer synchronization on a production-like database.
  • Deployment of services using Docker.
  • Verification of API backward compatibility.

@tomxey tomxey self-assigned this May 8, 2026
@iota-ci iota-ci added infrastructure Issues related to the Infrastructure Team sc-platform Issues related to the Smart Contract Platform group. labels May 8, 2026
Switch DynamicField::paginate from forward diff (objects_snapshot +
objects_history) to backward diff (checkpointed_objects +
objects_backward_history). The version-pinned path uses the new
backward_view::version_pinned query; the no-version-pin path falls
through to the existing consistent-view query.

Read-side:

- New backward_view::version_pinned query mirrors consistent::query in
  shape (Source A from checkpointed_objects + Source B from
  objects_backward_history merged via DISTINCT ON), but pivots on a
  version-axis cutoff rather than the cp-axis. For each candidate the
  target version is the largest objects_version.object_version that is
  <= parent_version. The state row at (object_id, target_version) is
  read from checkpointed_objects (current state) or
  objects_backward_history (prior state). Tombstone target versions
  exist in objects_version but have no corresponding state row in
  either table, so the candidate is correctly excluded.
- Source A excludes rows whose (object_id, object_version) also
  appears in objects_backward_history, suppressing duplicates during
  the brief race window where backward_history has been written but
  checkpointed_objects has not yet been updated.
- Behaviour matches the original forward-diff "earliest /
  produced-at" semantics: any DF with lamport <= parent_version is in
  scope, and the latest pre-cutoff state of each candidate is
  returned.

Tests:

- dof_add_reclaim_transfer_reclaim_add.move (delete-recreate of a
  Field-object with the same derived id) passes without any new
  schema column: the version-axis approach disambiguates the
  intra-checkpoint deletion gap via objects_version's tombstone
  versions.
- dynamic_fields.move: two queries renamed away from the snapshot-lag
  "outside consistent range" naming (those semantics don't apply
  under backward-diff retention) and re-recorded.
@tomxey tomxey force-pushed the sc-platform/dynamic-fields-consistency-using-objects-version branch from 45be64d to 079d80d Compare May 8, 2026 12:33
@tomxey tomxey changed the title feat(graphql): migrate dynamic fields to backward diff feat(graphql): migrate dynamic fields to backward diff using objects_version May 8, 2026
tomxey added 2 commits May 8, 2026 17:00
Replace the generic `filter_fn: impl Fn(RawQuery) -> RawQuery` parameter
on `backward_view::version_pinned::query` with an explicit `parent:
IotaAddress` argument. The DF-shape filter (`owner_id = parent AND
owner_type = Object AND df_kind IS NOT NULL`) is now constructed
internally rather than supplied by the caller.

The exclusion of non-Active rows (tombstones, NYC markers, synth
WrappedOrDeleted) used to depend implicitly on the caller's filter
constraining columns that are NULL on those rows. Making the filter
explicit removes that "magic" coupling — the function is now
self-contained and its DF semantics are clear from its signature.
Drop `consistency::View` and `consistency::build_objects_query`. They
were the forward-diff (`objects_snapshot` + `objects_history`) query
builder used by `DynamicField::paginate`; that path was migrated to
backward-diff in the previous commits, leaving these symbols dead.

Keeps `UNAVAILABLE_CHECKPOINT_SEQUENCE_NUMBER`, the `Checkpointed`
trait, and the cursor structs — those are still in use by other types.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

infrastructure Issues related to the Infrastructure Team sc-platform Issues related to the Smart Contract Platform group.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants