Skip to content

feat(datagen): reshape checkpoint delta-log to schema v2 - #205

Merged
beinan merged 4 commits into
lance-format:mainfrom
YangjunZ:datagen/checkpoint-delta-log-reshape
Jul 27, 2026
Merged

feat(datagen): reshape checkpoint delta-log to schema v2#205
beinan merged 4 commits into
lance-format:mainfrom
YangjunZ:datagen/checkpoint-delta-log-reshape

Conversation

@YangjunZ

@YangjunZ YangjunZ commented Jul 27, 2026

Copy link
Copy Markdown
Contributor

What & why

The datagen checkpoint delta-log had drifted from the authoritative spec. This
PR brings the lance-context store in line with schema v2, and — separately
from the code — makes the change reviewable by documenting the model and adding
the tests the reshape was missing.

The storage mechanism is unchanged: still a single append-only log.lance,
MemWAL sharding, deterministic event_id, blob offload. Only the event
vocabulary
and the folded model change.

Changes

Event vocabulary (6 → 7 events)

  • Add STEP_STARTED — a structural driver-frame marker (Sequence/Loop
    only). started \ completed is exactly the frame open at crash time, which is
    what resume re-enters.

Status is a first-class column

  • Replace the terminal column (only completed/filtered) with a status
    column carrying four values: running / completed / filtered / failed.
    running is now stored on ITEM_CREATED rather than derived.
  • Two read lenses: lifecycle (fold; ignores FAILED) vs failure (reads
    FAILED rows). A failed step leaves the item running under lifecycle and
    surfaces only through the failure lens.

Structured provenance

  • Replace opaque step_instance_id + iteration with step_kind /
    enclosing_step / selector_step. A resume can now rebuild step
    coordinates instead of trusting a stored cursor blob.
  • DatagenItemId is a structured, materialized path (5/solve_twice:0); ids
    compose purely on the client with no store round-trip.
  • New DatagenStepKind { Root, Leaf, Sequence, Loop, MapReduce, Branch, SubPipeline, Conditional, Router }. Only drivers emit STEP_STARTED; fan-out
    kinds emit only their reduce STEP_COMPLETED; selectors emit no row and the
    chosen child records selector_step.

Fold semantics

  • fold_datagen_events returns OptionNone (NeverStarted) when there is
    no ITEM_CREATED, which is the fresh-vs-resume fork.
  • FIELD_SET last-writer-wins; FIELD_APPEND accumulates; mixing the two on one
    field is rejected. DATAGEN_SCHEMA_VERSION = 2.

Docs

  • specs/datagen-checkpoint-schema.md — rewritten for v2: the seven events,
    the status column, structured item id + step provenance, the two read lenses,
    and updated write/recovery invariants.
  • docs/design/using-datagen-store.md (new) — a client-facing walkthrough:
    open → compose ids → write/checkpoint → finish → fold/resume → read tree /
    classify roots / failures / lazy blobs, all with runnable Rust snippets, plus a
    kind→what-you-emit table.

Tests

Fold-layer (datagen.rs) and store-layer (datagen_store.rs) coverage grew
from 12 → 19:

  • step_kind / status parse round-trips + is_driver
  • TERMINALfiltered status
  • selector_step folded onto the chosen child's position
  • fan-out sub-item folds with denormalized parent/root lineage
  • FIELD_SET / FIELD_APPEND mixing is rejected
  • resume open-frame = started \ completed
  • store: whole fan-out tree read by root + root_item_statuses classification +
    NeverStarted sibling
test result: ok. 19 passed; 0 failed

Compatibility

Schema v2 is not backward-compatible with v1 logs (the terminal column and
step_instance_id/iteration provenance are gone). This targets new
experiments; there are no v1 logs in production to migrate.

YangjunZ and others added 3 commits July 26, 2026 16:07
Bring the datagen checkpoint delta-log in line with the authoritative
spec. This changes the event vocabulary and folded model only; the
storage mechanism (single append-only log.lance, MemWAL sharding,
deterministic event_id, blob offload) is unchanged.

- Event types 6 -> 7: add STEP_STARTED.
- Replace the `terminal` column with a `status` column
  (running / completed / filtered / failed).
- Replace step_instance_id + iteration provenance with structured
  step_kind / enclosing_step / selector_step.
- Structured DatagenItemId with materialized path `root/step:idx/...`.
- New DatagenStepKind {Root, Leaf, Sequence, Loop, MapReduce, Branch,
  SubPipeline, Conditional, Router}; only drivers emit STEP_STARTED.
- fold_datagen_events returns Option (None when no ITEM_CREATED);
  FIELD_SET last-writer-wins, FIELD_APPEND accumulates; two read
  lenses (lifecycle vs failure). DATAGEN_SCHEMA_VERSION = 2.
- Rewrite specs/datagen-checkpoint-schema.md for schema v2: 7 events,
  status column, structured item id + step provenance, read lenses.
- Add docs/design/using-datagen-store.md: a client-facing walkthrough of
  open/write/checkpoint/resume/read with runnable snippets.
- Add 7 fold + store tests: step_kind/status parse round-trips, filtered
  terminal, selector_step on the chosen child, fan-out sub-item lineage,
  set/append mixing rejection, resume open-frame (started minus completed),
  and a fan-out tree read + root classification through the store.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…lta-log-reshape

# Conflicts:
#	crates/lance-context-core/src/datagen_store.rs
#	docs/src/design/using-datagen-store.md
- lance-context umbrella re-exported the pre-reshape name DatagenTrajectoryPoint;
  rename to DatagenTrajectory so the crate (and the python wheel + tests that
  depend on it) compiles again.
- allow(large_enum_variant) on DatagenItemLookup (Found is the hot path) and drop
  a redundant #[must_use] on iter().
- cargo fmt.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@beinan
beinan merged commit 8c8e89b into lance-format:main Jul 27, 2026
11 checks passed
jackye1995 pushed a commit that referenced this pull request Jul 27, 2026
… layers (#212)

## What

The schema-v2 reshape (#205) landed the datagen **data model** — events,
all schema columns, ids, the fold, status, provenance — but not the
**API layer** on top of it. This PR wires `DatagenStore` through the
full six-layer stack so clients can append, fold, and read blobs both
embedded (local file) and over the server.

## Layers

- **api** (`lance-context-api`) — `DatagenStoreApi` trait (RPITIT) +
wire DTOs mirroring the Python dicts. The api can't depend on core
(cycle), so the trait returns DTOs.
- **core** (`lance-context-core`) — `impl DatagenStoreApi for
DatagenStore`, with core↔DTO converters and DTO→enum validation.
- **client** (`lance-context-client`) — `RemoteDatagenStore` HTTP
client.
- **unified** (`lance-context`) — `enum DatagenStore { Local, Remote }`
with dispatch.
- **server** (`lance-context-server`) — `/api/v1/datagen` routes:
create/list/get/delete, append (`?checkpoint=`), fold item, item
failures, root-item-statuses, blob fetch.
- **python** — PyO3 `DatagenStore` binding with `open` / `connect` /
`connect_or_create`.

## Verification

Verified end-to-end locally: the **same** checkpoint round-trip driven
through both the embedded (local-file) and remote (server-backed) paths
folds to identical item state (fields, status, `last_item_seq`,
trajectory, root-item statuses). Remote datasets materialize on the
server's local-SSD data dir. `cargo fmt`, `cargo clippy --workspace
--all-targets`, and `cargo test -p lance-context-core datagen` (20
passed) are clean.

## Relation to the gap audit

This closes most of the write-path, blob-read, and Python gaps
catalogued against `main` in the downstream ckpt implementation-gaps
doc.

---------

Co-authored-by: YangjunZ <yangjunzhang@microsoft.com>
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
YangjunZ pushed a commit to YangjunZ/lance-context that referenced this pull request Jul 28, 2026
… layers

The schema-v2 reshape (lance-format#205) landed the data model but not the API layer on
top of it. This wires the datagen store through the full stack so clients can
append, fold, and read blobs both embedded and over the server:

- api: `DatagenStoreApi` trait (RPITIT) + wire DTOs mirroring the Python dicts
- core: `impl DatagenStoreApi for DatagenStore` with core<->DTO converters
- client: `RemoteDatagenStore` HTTP client
- unified: `enum DatagenStore {Local, Remote}` with dispatch
- server: `/api/v1/datagen` routes (create/list/get/delete, events,
  fold, failures, root-item-statuses, blob fetch)
- python: PyO3 `DatagenStore` binding + `open`/`connect`/`connect_or_create`

Verified end-to-end locally: identical checkpoint round-trips through both the
embedded (local-file) and remote (server) paths fold to the same item state.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
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