SteNicholas opened a new pull request, #330:
URL: https://github.com/apache/paimon-cpp/pull/330

   ### Purpose
   
   Linked issue: close #326
   
   Java Paimon commits a snapshot through `Catalog#commitSnapshot(identifier, 
tableUuid, baseSnapshotUuid, snapshot, statistics)`, and `CommitTableRequest` 
carries `tableId`, `baseSnapshotUuid`, `snapshot` and `statistics`. Paimon C++ 
only had a request-only mode: `CatalogSnapshotCommit` built a request with 
`snapshot` and `statistics`, the caller had to send it, 
`SnapshotCommit::Commit` had no base snapshot uuid to pass down, and `Snapshot` 
had no `uuid` at all, so a catalog could neither detect a commit aimed at a 
recreated table nor perform the optimistic-concurrency check Java relies on.
   
   This PR ports catalog-managed commits end to end, so a `RestCatalog` table 
is written, committed and expired with the same `FileStoreWrite` / 
`FileStoreCommit` API as a file-system table.
   
   **Catalog integration**
   
   - `Catalog::SupportsVersionManagement()` (default `false`) and the internal 
`VersionManagedCatalog` interface with `LoadSnapshot()` and `CommitSnapshot()`. 
A catalog that reports version management without implementing the interface is 
refused at commit creation instead of silently writing a snapshot file.
   - `RestCatalog` implements it through `RESTApi::LoadSnapshot()` (`GET 
.../tables/{table}/snapshot`) and `RESTApi::CommitSnapshot()` (`POST 
.../tables/{table}/commit`), with the matching `GetTableSnapshotResponse` and 
`CommitTableResponse` messages. `FileSystemCatalog` does not manage versions, 
so it keeps the renaming file-system commit.
   - `CommitContextBuilder::WithCatalog(catalog, identifier)` and 
`WriteContextBuilder::WithCatalog(catalog, identifier)`. Commits and writers 
load the current schema from the catalog on every commit; when the catalog 
manages versions they also load its latest snapshot, through a 
`SnapshotManager` snapshot loader, and publish new snapshots through it. Only 
the main branch is supported, and the catalog supplies the file system for 
manifests and data unless `WithFileSystem()` overrides it. `WithCatalog()` and 
`UseRESTCatalogCommit()` are mutually exclusive.
   
   **Request and snapshot format, aligned with Java**
   
   - `CommitTableRequest` gains nullable `tableId` and `baseSnapshotUuid` in 
Java's field order, serialized as `null` when unset. 
`SnapshotCommit::Commit(base_snapshot_uuid, snapshot, statistics)` carries the 
base uuid; `RenamingSnapshotCommit` ignores it as Java does.
   - `Snapshot` gains a nullable `uuid`, serialized right after `version` and 
omitted when unset. `Snapshot::GenerateUuid()` gives every new snapshot one, 
`Tag::TrimToSnapshot()` keeps it, and `Table::CatalogUuid()` exposes the 
metastore uuid for `tableId`.
   
   **Conflicts and recovery**
   
   - A catalog that refuses the commit (returns `false`) is a conflict: the 
commit rebases on the catalog's latest snapshot and retries within the existing 
`commit.max-retries` / `commit.timeout` limits, the same way a lost file-system 
rename does. `ConflictDetection` reuses the base snapshot of the attempt, since 
the latest snapshot may exist only in the catalog.
   - A catalog error leaves the outcome uncertain: the manifests and snapshot 
metadata are kept and `FilterAndCommit()` resolves it, deduplicating against 
the catalog's snapshot and the published history. `RestHttpClient::Execute()` 
gains a `retry_safe` flag; the commit POST is sent with it off, so it is 
neither retried automatically nor replayed through a redirect.
   
   **Expiration**
   
   - When a catalog is configured, `Expire()` requires the catalog's current 
snapshot to be loaded and its retained history to be published on the file 
system before deleting anything. An unpublished or mismatched current snapshot 
skips expiration; catalog or retained-metadata read errors propagate without 
deleting files.
   - Files referenced by retained snapshots are protected, including files a 
retained rollback snapshot adds back together with their extra files and 
external paths.
   - The existing `NotImplemented` for index manifests is returned before any 
file is deleted, replacing the `assert(false)` that preceded it.
   
   **Compatibility**
   
   - The request-only `UseRESTCatalogCommit()` mode and the legacy 
`CommitContext` constructor are kept. `GetLastCommitTableRequest()` now always 
includes `tableId` and `baseSnapshotUuid`, `null` when unset; callers that 
spliced a `tableId` into that JSON themselves should switch to 
`CommitContextBuilder::WithTableId()`.
   
   ### Tests
   
   New UT (76 cases), plus `MockCatalog` in 
`src/paimon/testing/mock/mock_catalog.h` and `snapshot_test_helper.h`:
   
   - `catalog_snapshot_commit_test.cpp` (new target): commit through the 
catalog, a lost race is not an error, catalog failures propagate, a catalog 
without version management is refused, request-only build without a catalog.
   - `commit_table_request_test.cpp`: `TestNullTableIdAndBaseSnapshotUuid`. 
`rest_messages_test.cpp`: `GetTableSnapshotResponseRoundTrip`, 
`CommitTableResponseRoundTrip`.
   - `snapshot_test.cpp`: `TestGenerateUuid`, `TestUuid`. `tag_test.cpp`: 
`TestTrimToSnapshotKeepsTheUuid`.
   - `file_system_catalog_test.cpp`: `TestDoesNotManageVersions`, 
`TestClaimingVersionManagementWithoutImplementingItIsRefused`.
   - `commit_context_test.cpp` / `write_context_test.cpp`: `TestWithCatalog`, 
`TestLegacyConstructor`, `TestCatalogRequiresMainBranch`.
   - `file_store_commit_impl_test.cpp` (37 cases): base snapshot uuid on the 
request; catalog commit takes the snapshot and is refused for a table recreated 
under the same name; catalog schema wins over the table path and is re-read on 
every commit; retries when refused, rebases on the winner of each race, 
succeeds after losing one race; unknown outcome keeps the manifests and 
recovers through `FilterAndCommit`, including an answer lost on the way back 
and a history that is only partly published; realtime commits are idempotent 
through the catalog; row-id checks use the catalog latest and the published 
history; rollback through the catalog; 
`FileStoreRollbackExpireTest.TestRetainedRollbackKeepsRestoredFiles`; catalog 
expiration waits for rollback publication, uses published snapshots and 
validates retained metadata before deleting files.
   - `snapshot_manager_test.cpp` (11 cases): the snapshot loader answers which 
snapshot is latest, `LatestSnapshotOfUser*` walks the published history, stops 
at the earliest retained or an expired snapshot, retries a stale boundary and 
reports a gap it cannot explain.
   - `rest_catalog_test.cpp`: `CommitSnapshot`, `LoadSnapshot`, 
`CommitSnapshotErrors`, `FileStoreCommitIsBuiltFromTheCatalog`, 
`TableUuidFallsBackToFullNameWithoutServerId`, 
`CommitTakenThenReportedUnavailableIsNotReplayed`, and 
`RestCatalogCommitRecoveryTest.CommitAcceptedThenLostRecoversAfterRestart`.
   - `rest_http_client_test.cpp`: 
`NonRetrySafeRequestDoesNotRetryTransientResponses`, 
`NonRetrySafeRequestDoesNotFollowRedirects`.
   - `file_store_write_test.cpp`: 
`TestRealtimeCatalogCommitRefreshAndRecovery`. `expire_snapshots_test.cpp`: 
`TestCleanUnusedDataFileDeletesExtraFiles` becomes 
`TestCleanUnusedDataFilePreservesRetainedExtraFiles`.
   
   `pre-commit run --files <changed files>` passes (clang-format, cmake-format, 
codespell, sphinx-lint, C++ lint).
   
   ### API and Format
   
   Yes.
   
   - `include/paimon/catalog/catalog.h`: `Catalog::SupportsVersionManagement()`.
   - `include/paimon/catalog/table.h`: optional `uuid` constructor argument and 
`Table::CatalogUuid()`; `Table::Uuid()` returns the catalog uuid when present 
and still falls back to the full name.
   - `include/paimon/commit_context.h`, `include/paimon/write_context.h`: 
`WithCatalog(catalog, identifier)` on both builders, `GetCatalog()` / 
`GetIdentifier()` / `GetTableId()` accessors, and a `CommitContext` constructor 
taking the catalog fields; the previous constructor is retained.
   - `include/paimon/file_store_commit.h`, `include/paimon/file_store_write.h`: 
documentation of the catalog commit mode, conflict retries, recovery and 
expiration requirements.
   - Snapshot JSON gains an optional `uuid` field after `version`, omitted when 
unset. `CommitTableRequest` JSON gains `tableId` and `baseSnapshotUuid`, `null` 
when unset. The REST client now calls the `/snapshot` and `/commit` table 
endpoints of the Java REST catalog protocol.
   
   ### Documentation
   
   Yes.
   
   - `docs/source/user_guide/catalog.rst`: a new **Committing through the 
catalog** section covering `WithCatalog()`, the schema and snapshot sources, 
conflict retries, recovery, the expiration requirements, the rollback / 
expiration coordination note, and the list of Java REST catalog operations that 
still have no C++ counterpart.
   - `docs/source/user_guide/snapshot.rst`: the `uuid` field and the renumbered 
field list.
   - `docs/source/user_guide/write.rst`: the catalog write and commit flow, and 
the request-only alternative for callers without a catalog client.
   - `docs/source/user_guide/format_table.rst`: `WithCatalog()` and 
`WithTableId()` added to the builder options a format table refuses.
   
   ### Generative AI tooling
   
   Generated-by: Claude Code (claude-fable-5-1)
   
   🤖 Generated with [Claude Code](https://claude.com/claude-code)
   


-- 
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