JingsongLi commented on PR #9529:
URL: https://github.com/apache/paimon/pull/9529#issuecomment-5506179589

   Following up on the version-model discussion, I compared this layout with 
the metadata model used by LeRobot v3. I think a simpler persisted contract 
would be to preserve the distinction that LeRobot already makes between Parquet 
metadata and JSON metadata.
   
   ## Proposed model
   
   1. Keep the LeRobot Parquet metadata as typed Paimon tables with the same 
**logical** schema:
   
      - `__tasks`: LeRobot task fields plus `version_id`
      - `__episodes`: LeRobot episode fields plus `version_id`
   
      Each published version is a complete metadata materialization in a new 
`version_id` partition. It is not a delta.
   
   2. Replace `__datasets` with a `__versions` manifest table. One row 
represents one published LeRobot version and contains the JSON metadata and 
publication information, for example:
   
      ```text
      version_id
      parent_version_id
      version_sequence
      status                    # PENDING / READY
      published_at
      info_json
      stats_json
      extra_metadata_json
      frames_snapshot_id
      episodes_schema_json
      tasks_schema_json
      source_revision
      metadata_checksum
      ```
   
   3. Keep frames snapshot-based. Frame rows should not carry `version_id`; the 
exact frame state for a release is selected by `frames_snapshot_id` in the 
version manifest. The snapshot should be pinned by a tag while that version is 
retained.
   
   This gives every component exactly one authoritative version selector:
   
   ```text
   frames      -> frames_snapshot_id
   episodes    -> version_id partition
   tasks       -> version_id partition
   info/stats  -> __versions.version_id
   ```
   
   For a table group that contains only one logical dataset, partitioning by 
`version_id` is enough. If companion tables can contain multiple datasets, use 
`(dataset_id, version_id)` as the partition and include both fields in the 
primary key:
   
   ```text
   tasks PK:   (dataset_id, version_id, task_index)
   episodes PK:(dataset_id, version_id, episode_index)
   ```
   
   ## Update semantics
   
   Suppose V2 adds one episode to V1:
   
   ```text
   V1:
     complete tasks metadata in partition V1
     complete episodes metadata in partition V1
     frames snapshot S1
   
   V2:
     complete tasks metadata in partition V2
     complete episodes metadata in partition V2
     frames snapshot S2 containing the old frames plus the new episode
   ```
   
   Tasks and episodes are rewritten because they are relatively small metadata 
and the full partition makes reading and deletion unambiguous. Frames are 
appended and versioned by snapshot, so existing blob data is not copied for 
every release.
   
   ## Publication protocol
   
   ```text
   reserve version V as PENDING
   write or append frames and obtain snapshot S
   pin S with a tag
   write the complete tasks partition V
   write the complete episodes partition V
   validate counts, references, schemas, and checksums
   publish V as READY
   optionally move dataset_heads.current_version_id to V
   ```
   
   Readers must start from a `READY` version row. They must never infer a 
release by independently opening the latest snapshots of the component tables.
   
   `version_id` should remain an opaque release identifier. It should not be 
used to determine the latest version; use a monotonic `version_sequence` or a 
separate `dataset_heads` pointer.
   
   ## Schema compatibility
   
   By "same schema" I mean the logical Arrow schema, not Parquet/Pandas 
artifacts such as `__index_level_0__`. This should preserve fields such as 
`tasks`, `data/chunk_index`, per-camera video offsets, and typed `stats/...` 
fields instead of folding them into JSON.
   
   There is one important constraint: Paimon schema is table-level, not 
partition-level. Additive LeRobot changes can evolve the table schema, with old 
partitions reading null for new fields. An incompatible type change for an 
existing field must either:
   
   - fail with a clear request to create a new table generation;
   - use an explicitly defined canonical type; or
   - fall back to lossless raw metadata storage.
   
   The original Arrow schemas and fingerprints should therefore be recorded in 
`__versions` and checked before writing a new partition.
   
   I would also define a forward-compatibility rule for all metadata files:
   
   - known `meta/*.parquet` files become typed companion tables;
   - known `meta/*.json` files are stored in the version row;
   - unknown files are preserved losslessly as `(version_id, path, bytes, 
checksum)` rather than rejected or silently dropped.
   
   This model is still a hybrid physical implementation, but it is not an 
ambiguous version model: snapshot identity defines frame membership, partition 
identity defines Parquet-metadata membership, and the `READY` manifest is the 
single cross-table publication point. It also follows the native LeRobot v3 
organization more closely than introducing a different normalized schema for 
tasks and episodes.
   


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to