Dremio is now part of SAP
Dremio Blog

43 minute read · September 25, 2026

Multi-Table Transactions with Iceberg REST Catalogs and Apache Polaris

Alex Merced Alex Merced Head of DevRel, Dremio
Multi-Table Transactions with Iceberg REST Catalogs and Apache Polaris
Copied to clipboard

Quick answer

The Iceberg REST protocol defines an optional transaction commit endpoint that can submit updates for several tables as one catalog operation. That endpoint can provide catalog-level atomicity when the server implements it, but a usable multi-table transaction also requires client (engine) support, conflict detection and retry strategies, and clear failure semantics. A REST catalog label alone does not guarantee that an engine will issue coordinated commits; you must verify the server implements the REST commit path and the engine issues the correct request shapes and retry behavior.

What this article covers

This guide distinguishes the protocol capability from end-to-end behavior and gives a practical path for implementing and validating multi-table commit semantics with Iceberg REST catalogs and Apache Polaris. You will get:

  • A concrete description of the REST multi-table commit endpoint, and how it differs from single-table commits.
  • Decision logic for when the catalog can provide atomicity and when it cannot.
  • Worked request and response examples for multi-table commits and for the requirement/update pairs the protocol uses.
  • Client pseudocode for retries and conflict handling, plus test and rollout checklists.
  • Failure modes, metrics to collect after deployment, and what to verify in the server and engine codebases.

Background: what the Iceberg REST protocol actually offers

The Iceberg REST protocol, as defined in the project documentation and OpenAPI spec, includes an optional commit endpoint that accepts multiple table updates in a single request. The primary specification is the Iceberg REST protocol documentation, with the OpenAPI schema in the Iceberg repository describing the request and response shapes. Both sources are current primary references for protocol behavior and must be read when you design an engine that calls a REST catalog. See the official Iceberg REST protocol documentation for details and the OpenAPI definition for exact JSON schemas. Facts in this article checked on September 22, 2026 against those primary sources.

Important constraints to keep in mind from those sources: the commit endpoint is optional. A catalog server may not expose it. The OpenAPI file in the Iceberg repository defines the request model for multi-table commit, including requirement and update entries. If your catalog server does not implement the REST commit path, you will not get catalog-level atomic commits and must coordinate commits in another way.

Single-table versus multi-table commit

Put bluntly, updating one table and updating many tables are different operational problems. With single-table commits the usual pattern is: write data files, compute a manifest or metadata update, then atomically replace the table metadata file by writing a new manifest list and using an object-store atomic rename. Many Iceberg catalogs implement that single-table atomicity through the table metadata object strategy, and engines assume substituting the table metadata file is atomic for that table.

When you need to commit changes to multiple tables atomically you cannot rely on per-table metadata file replaces to coordinate across tables. The REST multi-table commit endpoint attempts to provide catalog-level atomicity by accepting a list of updates and applying them under a catalog transaction. Whether this gives you the isolation and atomicity you want depends on the catalog server implementation and on how the engine prepares the commit payload.

SINGLE-TABLE VERSUS MULTI-TABLE COMMIT1Multi-table commit request2Requirement and update pai…3Retry pseudocode4Verify the resultA useful implementation has an observable result at every boundary. A successful command alone is not the acceptance test.
Single-table versus multi-table commit. Each stage has a result that can be checked before the next stage begins.

Diagram note: the first diagram above contrasts the timeline for two single-table commits versus a single multi-table commit. In the multi-table case the server applies both updates inside a single catalog transaction. If the server does not support multi-table commits, the engine must issue separate single-table commits, which can be observed partially if one succeeds and another fails.

What the protocol provides, and what it does not

Protocol-level capability: the REST API defines a commit endpoint that takes an array of requirement/update objects. The OpenAPI spec describes fields such as requirement.type and update.type, plus table identifiers and metadata references. This gives a standard wire format for attempting multiple table updates in one catalog call.

Limits and cautions:

  • The REST endpoint is optional, catalog implementations may not implement it. You must verify the catalog server you use exposes and documents /commit or the equivalent multi-table commit path. See the Iceberg REST protocol documentation and the OpenAPI definition for the exact path and semantics.
  • Catalog atomicity is not the same as distributed transaction isolation across engines and clients. The catalog can apply updates together, but that does not automatically provide higher isolation levels or distributed two-phase commit guarantees unless the catalog implements them. The server may apply updates atomically but still allow concurrent readers to observe intermediate states depending on how the server exposes snapshot visibility.
  • Engine support is required. Even if the catalog supports multi-table commit, the engine must produce the correct requirement/update pairs, submit a single commit request, and handle conflict responses and retries correctly. Do not assume clients will automatically use the endpoint just because the catalog exposes it.

How a multi-table commit is expressed

The protocol models commits as requirement/update pairs. Requirements describe preconditions, such as "table current metadata equals X" or "table has no conflicting snapshot", and updates describe the user-intended metadata change, for example "set current snapshot to Y" or "write new metadata file Z". The server evaluates requirements, and if they hold, the server applies updates and returns success. If any requirement fails, the server rejects the whole request, returning an error that indicates the conflicting table and expected versus actual state.

Requirement and update pairing is critical. The OpenAPI model lists exact requirement types and update types. Confirm the supported types in the server you run by reviewing the server docs and the OpenAPI schema shipped by the server. If you cannot find the server behavior, test it directly with an integration test before relying on it in production.

Worked example: multi-table commit request

Below is a minimal multi-table commit request body constructed from the shapes defined in the OpenAPI spec. Replace placeholders with the real identifiers your catalog uses. The example shows two tables, t1 and t2, each with a requirement that the current metadata location equals a known value, and each with an update that sets a new current metadata location. The server will atomically validate both requirements and, if they all pass, install both updates in one catalog operation.

<!-- wp:code -->
{
  "operations": [
    {
      "requirement": {
        "type": "METADATA_EQUALS",
        "table": "db.t1",
        "metadata_location": "s3://bucket/t1/metadata/v42.json"
      },
      "update": {
        "type": "SET_METADATA",
        "table": "db.t1",
        "new_metadata_location": "s3://bucket/t1/metadata/v43.json"
      }
    },
    {
      "requirement": {
        "type": "METADATA_EQUALS",
        "table": "db.t2",
        "metadata_location": "s3://bucket/t2/metadata/v7.json"
      },
      "update": {
        "type": "SET_METADATA",
        "table": "db.t2",
        "new_metadata_location": "s3://bucket/t2/metadata/v8.json"
      }
    }
  ]
}
<!-- /wp:code -->

This payload mirrors the requirement/update pairing in the OpenAPI file. The exact property names and operation types must be confirmed against the OpenAPI file your server implements, because the Iceberg project keeps the REST schema in the repository and servers may choose to implement subsets. See the OpenAPI definition in the Iceberg repository for the canonical schema.

Atomic commit decision tree

When designing an engine that may ask the catalog for a multi-table commit, follow this decision tree. It reduces the risk that you assume atomicity you do not have.

ATOMIC COMMIT DECISION TREERisk 1The REST endpoint is optionalTest itRisk 2Catalog atomicity is not the same as distributed transact…Bound itRisk 3Engine support must be verifiedMonitor it
Atomic commit decision tree. Each technical risk needs a matching test, boundary, or operating signal.
  • Step 1, can the catalog server expose a commit endpoint? Verify the server advertises the REST commit path. If no, you cannot rely on catalog-level atomicity; do not attempt multi-table commit calls.
  • Step 2, does the catalog documentation and OpenAPI schema on the server implement requirement/update semantics? Check the server's OpenAPI or documentation. If the server exposes a commit endpoint but with a different model, you must adapt to the server behavior. The Iceberg OpenAPI definition is authoritative for common implementations.
  • Step 3, does the engine prepare requirement entries that accurately reflect the pre-commit table state? Engines must record the metadata locations or snapshot ids they depend on. If the engine cannot supply reliable preconditions, the server cannot guarantee atomic application across tables.
  • Step 4, can you tolerate catalog-level rejections requiring coordinated retries? If a requirement fails, the server rejects the whole multi-table commit. That is stronger than partial success and means your client must be able to roll work back or retry. If you cannot cleanly roll back partial client-side work, atomic catalog commits may not be helpful.
  • Step 5, what concurrency model does the catalog provide for reads during the commit? Measure or inspect whether the server publishes a new snapshot only after all updates apply, or if readers can see intermediate table states. The server documentation should state how snapshots and visibility behave; if not, test it.

Conflict and retry sequence

Because requirements are checked atomically, the whole multi-table commit will fail if any requirement is stale. That is desirable in many cases, but it changes how you do retries. If table t1 changed concurrently, and you retry only the t1 change, you break the multi-table atomicity unless you re-run the full sequence that produced the change and recompute requirements for all tables.

CONFLICT AND RETRY SEQUENCEObservecollect the signalCompareuse a baselineDiagnoselocate the boundaryActchange one variablemeasured evidenceunexpected changesmallest safe responsenew baseline
Conflict and retry sequence. The loop turns table or catalog signals into controlled operational changes.

A typical conflict and retry sequence looks like this:

  • Client reads snapshots of t1 and t2, deriving metadata locations A and B.
  • Client writes data files and prepares new metadata locations A2 and B2. Client sends a single commit with requirement metadata_equals A and metadata_equals B, updates to A2 and B2.
  • Server evaluates requirements. If t1 has been modified by another client and no longer has metadata A, server responds with a requirement failed error indicating t1 is inconsistent. No updates are applied.
  • Client receives the error, re-reads the affected table(s), reconciles data files and manifests, recomputes the updated metadata location(s), and retries the entire multi-table commit in one request. Partial retries of only one table are incorrect unless you design compensating logic that preserves atomicity.

Worked example: retry pseudocode

The pseudocode below is a minimal approach to retrying multi-table commits. It assumes the server rejects on requirement failures, returns which table failed, and that clients can re-run write/manifest operations for affected tables. It also uses an exponential backoff policy and aborts after a configurable number of attempts. This pattern must be adapted to your engine's failure semantics and to the file-system semantics where you write data files.

<!-- wp:code -->
maxAttempts = 5
attempt = 0
while attempt < maxAttempts:
    attempt += 1
    # 1. Read current metadata locations and snapshots for each table
    state = read_table_state([t1, t2])

    # 2. Local write of data files and compute new metadata files
    new_meta_t1 = prepare_metadata(t1, state[t1])
    new_meta_t2 = prepare_metadata(t2, state[t2])

    # 3. Build multi-table commit payload with requirements bound to the state we read
    payload = build_commit_payload([
        {"requirement": {"type": "METADATA_EQUALS", "table": "t1", "metadata_location": state[t1].metadata_location},
         "update": {"type": "SET_METADATA", "table": "t1", "new_metadata_location": new_meta_t1}},
        {"requirement": {"type": "METADATA_EQUALS", "table": "t2", "metadata_location": state[t2].metadata_location},
         "update": {"type": "SET_METADATA", "table": "t2", "new_metadata_location": new_meta_t2}}
    ])

    # 4. Send commit request
    resp = send_commit_request(payload)
    if resp.success:
        return resp

    if resp.error.type == "REQUIREMENT_FAILED":
        # Identify which tables failed and loop to retry. Clean up any temporary objects if necessary.
        failed_tables = resp.error.details.failed_tables
        cleanup_local_temp_files(failed_tables)
        sleep(exponential_backoff(attempt))
        continue

    # for other errors, either retry or abort depending on type
    if should_retry(resp.error):
        sleep(exponential_backoff(attempt))
        continue

    raise CommitException(resp.error)
<!-- /wp:code -->

This pattern shows why multi-table commit clients must be able to recompute metadata for all tables in a commit. A partial retry that only updates t1 will not restore the intended atomicity unless you can guarantee the other updates remain valid as-is.

Client, protocol, server support layers

There are four layers that must align to achieve safe multi-table commits:

CLIENT, PROTOCOL, SERVER SUPPORT LAYERSInventoryversions and consumersTestfeature and failure pathsCanaryone bounded workloadDecideexpand or stopA failed gate returns to inventory with evidence. It does not become a production exception.
Client, protocol, server support layers. A reversible canary keeps an unsupported client or unsafe policy from becoming a fleet-wide incident.
  • 1) Client/engine: must prepare requirements, updates, and handle conflict/rollback semantics. The engine needs code to build the commit payload, call the catalog commit endpoint, and coordinate retries.
  • 2) Network/protocol: the REST commit endpoint and the OpenAPI contract define how clients express requirements and updates. Confirm your client library encodes JSON fields exactly as the server expects. The Iceberg OpenAPI file in the repository is the starting point for request shapes.
  • 3) Catalog server: must implement the commit operation and the semantics for evaluating requirements and applying updates atomically. The server also must define what visibility guarantees it provides to readers after commit.
  • 4) Storage layer: object stores and metadata backends must provide the consistency semantics expected by the catalog. For example, a catalog implementation that depends on atomic rename of metadata files must run on object stores with the required consistency characteristics or provide compensating logic.

Each layer can break the assumed atomicity. For instance, if the client builds incorrect requirements, the server will reject or misapply updates. If the server advertises the commit path but actually applies updates separately under the hood, you will lose atomicity. If your object-store is eventually consistent for renames, and the catalog relies on immediate consistency, you may observe weird visibility issues.

Implementation and evaluation sequence

Use the following practical sequence to implement or validate multi-table commit behavior in your environment. I have used a similar sequence when operating production clusters and it catches the common failure modes.

  • Step A, server capability check. Query the catalog server OpenAPI or documentation for the multi-table commit path and operation. If the server provides a copy of the Iceberg OpenAPI, compare the operation signatures to the Iceberg project spec. If the server lacks the operation, stop here and plan a different approach.
  • Step B, static contract test. Use a client that can issue raw REST requests and submit a crafted payload that uses a harmless pair of requirement/update operations, for example pointing requirements at the current metadata and updates to identical values, to confirm the endpoint accepts the format and returns a success or clear error schema. This tests request parsing and basic validation without modifying state.
  • Step C, simple atomicity test. Prepare two small tables, record their metadata locations, then issue a multi-table commit that sets both metadata locations to new values derived from benign no-op metadata files. Observe whether both tables change together from the perspective of concurrent readers. Run concurrent readers that poll for table snapshot ids to see if a reader can observe one table updated and the other not. Repeat this test multiple times. If you observe partial updates, the server is not providing catalog-level atomicity.
  • Step D, conflict test. Concurrently submit conflicting single-table updates for one of the tables while a multi-table commit is in flight. Verify the server rejects the multi-table commit with a requirement failed error naming the conflicting table. Confirm the server did not apply any update for the non-conflicting table either.
  • Step E, retry and failure semantics. Implement the client retry logic from the pseudocode and run it against real concurrent load. Verify that after a reasonable number of retries the client either succeeds or fails cleanly, without leaving dangling partial state such as unreferenced metadata objects. Confirm your storage cleanup policy handles temporary manifests and orphaned data files as determined by your data retention PSI.

Failure modes and operational consequences

Understand these failure modes before you roll multi-table commits into production. Each has practical consequences for data correctness and operational cost.

  • Partial client-side failures before commit. Clients may write data files and fail before sending the commit. These orphaned files increase storage and require a compaction and garbage collection strategy. Your engine must track temporary paths and implement a background cleanup process. If you use a managed engine, check how it manages temporary file lifecycles.
  • Requirement failures. The server rejects the whole request if any requirement fails. Client must then decide to abort or recompute and retry. Many engines implement a deterministic retry loop with bounded attempts. If retries are expensive, you should design operations to minimize conflicts, for example by isolating hot tables.
  • Server partial apply or bug. If the catalog server has a bug, it could apply updates partially even though the protocol promises atomic apply. Detect this by frequent integration tests that assert both tables change together. If you see partial apply, roll back to single-table commit strategies and file a bug with the server project. Record the server version, request payload, and response for debugging.
  • Visibility anomalies due to storage semantics. Some object stores have eventual consistency on list operations or rename semantics that the catalog depends on. You may observe readers that do not immediately see new metadata objects or see older metadata when lists are stale. Validate your object-store semantics and, if necessary, use stronger consistency layers or a catalog implementation that avoids relying on immediate listing semantics.
  • Operational scaling. Multi-table commit requests can contain many requirement/update pairs. Large payloads increase request parsing and transaction coordination costs on the server. If many concurrent multi-table commits are common, measure server CPU, request latency, and lock contention on metadata resources. Consider limiting the number of tables per commit in high-concurrency environments.

Rollout checklist

  • Confirm server supports the REST commit endpoint and the requirement/update model, by checking the server OpenAPI and running a static payload test.
  • Update engine code to emit requirement entries bound to the metadata locations or snapshot ids the engine read. Unit test the serialization against the OpenAPI JSON schemas.
  • Add reliable retry logic for requirement failures, with exponential backoff and a bounded number of attempts. Ensure retries recompute metadata for all tables in the commit, not just the failed table.
  • Add background cleanup for orphaned temporary files created during aborted attempts.
  • Add integration tests that run the simple atomicity test and conflict test under load. Automate those tests in CI against any catalog server image you will deploy.
  • Define and collect the operational metrics described below before rolling to production. Run a phased rollout to a subset of teams or tables and monitor.

What to measure after deployment

Collect these metrics to detect problems early and to understand the operational cost of multi-table commits.

  • Commit success rate, by single-table and multi-table commits. If multi-table commits have higher failure rates, investigate requirement mismatch frequency.
  • Requirement failure rate and cause. Log which table and which requirement type failed. This helps identify hot tables and contention patterns.
  • Average and p99 commit latency for the catalog commit endpoint. Large latencies can mean contention or heavy serialization work on the server, which increases time windows for concurrent modifications to introduce conflicts.
  • Number of retries per successful multi-table commit. High retry counts indicate contention and may prompt changes to partitioning, batching, or isolation design.
  • Orphaned file count and storage bytes reclaimed. Track how many temporary data files remain unreferenced after failures, and measure cleanup latency and cost.
  • Reader visibility anomalies. Instrument readers to detect if they see inconsistent cross-table states. This is hard to measure automatically but critical for correctness when cross-table joins assume snapshot alignment.

Verifying engine support and changes to expect

Do not assume an engine or catalog will automatically support multi-table commits. The Iceberg Go client discussion in the project issue tracker documents that client implementations must include logic to build and send properly formed multi-table commits and to handle conflicts. For example, the issue in the Iceberg Go client raised questions about implementing commit semantics and how the client should expose that to callers. If you use a language binding or engine plugin, inspect the code paths that build catalog requests and add tests that assert the client emits a single multi-table payload, rather than separate calls.

When evaluating an engine or client library, verify:

  • That the client implements a commit API that accepts multiple table updates, or exposes hooks to build raw REST payloads. If not, you may need to extend the client or contribute a patch.
  • That the client preserves preconditions read earlier, such as metadata locations, and binds them into the requirement entries. Some clients drop snapshot metadata for simplicity; they will not be able to perform safe multi-table commits without change.
  • That the client exposes retry configuration or allows caller control over retry policy. Blindly retrying unlimited times is a problem under load and can increase contention.

For more on how engines talk to the catalog, the Dremio blog has a practical writeup of the Polaris REST API and how engines interact with the catalog. Read that explanation to understand the call patterns engines typically follow. Also review Dremio's overview of Apache Iceberg REST catalogs when picking a catalog implementation, because it explains how catalogs that implement the REST protocol differ in deployment and operational expectations.

Links to those Dremio pages are included where they are most useful. The Dremio Polaris architecture post helps you reason about catalog server responsibilities and how Polaris implements catalog APIs in practice. The Dremio platform page on open catalog explains how Dremio exposes catalogs and why you might choose a managed catalog versus running your own.

Practical tips and rules of thumb

  • Avoid very large multi-table commits at first. Keep the number of tables per commit low during the rollout, for example under 10, until you observe stable behavior and acceptable latency.
  • Treat requirement failures as the normal path under contention. Design workflows to tolerate some retries, and avoid synchronous user-facing operations that block indefinitely waiting for a commit to succeed.
  • Record and propagate the server version and OpenAPI used for contract tests. If you must troubleshoot a compatibility issue, these artifacts are the first things a support engineer will ask for.
  • Use deterministic metadata naming for temporary artifacts if your cleanup process uses name patterns. That makes it easier to sweep orphaned objects without risking deletion of live data.

If you need practical examples and a vendor-specific perspective, the Dremio blog article on the Apache Iceberg REST catalog explains what the REST catalog is and how to use it with engines. That post helps when choosing a catalog and running basic REST calls. For engine-to-catalog interaction patterns and how Polaris implements REST APIs, read the Dremio post on the Apache Polaris REST API. To understand Polaris at an architectural level and catalog responsibilities, see the Polaris architecture explained post. Finally, if you are evaluating catalog models and integrations with Dremio Platform, review the open catalog platform overview which describes how Dremio can integrate with external catalogs.

These links are useful for different phases of the rollout: the Iceberg REST catalog post helps with initial server setup and testing, the Polaris REST API post helps instrument client call patterns, and the Polaris architecture post helps you set expectations for server-side guarantees.

Controlled implementation checklist for multi-table commits

Here is a practical, ordered checklist you can follow when you are ready to implement Iceberg multi-table commits using a REST catalog and an engine such as Apache Polaris or a Dremio execution layer. Follow each step, verify the listed observable, and gate the next step on passing tests. This checklist assumes the REST endpoint is present in your Iceberg catalog implementation. If the catalog does not expose the REST commit endpoint, stop and evaluate other coordination options.

Run these steps in a staging cluster that mirrors production metadata scale and concurrency. Each step includes the key probe, the expected outcome, and what to do if you fail the probe.

  • Verify REST commit endpoint presence, probe: fetch the OpenAPI specification from the catalog and confirm the presence of the /tables/commit operation. Expected: the OpenAPI document contains the commit path and the request/response schemas. If missing: stop. Either use a different catalog or implement coordinator logic that does not rely on the REST commit endpoint.
  • Unit test the multi-table commit request format, probe: construct the JSON body matching the worked example multi-table commit request used in this article and POST to the commit endpoint with a synthetic authorization header. Expected: 200 or documented success code and a commit snapshot identifier in the response schema. If you get a 4xx, validate the fields against the rest-catalog-open-api.yaml. If you get a 5xx, inspect server logs and verify the server supports multi-table commit logic rather than returning a generic error.
  • Requirement and update pair validation, probe: for each table in the request, send a pre-flight GET to fetch the table metadata version or snapshot id, then build a request where the requirement equals that snapshot id. Expected: the server accepts the request when the requirement matches the current state, and rejects when the requirement is stale. If the server does not reject stale requirements, treat this as unsupported behavior and do not enable automatic multi-table commits.
  • Single-client commit sequence, probe: run the worked example multi-table commit request from a single client changing two tables. Expected: both tables commit, visible when fetching table metadata and listing new snapshots. Verify data files referenced by the updates appear in the table metadata. If only one table shows the new snapshot, collect server audit logs for the commit transaction id and open a bug with the catalog implementer.
  • Concurrent conflicting clients, probe: run two clients that each prepare requirement/update pairs targeting the same table but different updates, timed so their commit requests arrive concurrently. Expected: one request succeeds, the other gets a conflict response per the OpenAPI schema. If both succeed, you do not have atomic multi-table guarantees.
  • Retry pseudocode validation, probe: implement the retry pseudocode in the client using backoff and idempotency handling. Simulate transient errors and verify the client either succeeds after retrying or surfaces a conflict. If retries cause duplicate visible effects in table metadata, audit how the server interprets repeated commit attempts and adjust client idempotency keys accordingly.
  • Orphan data cleanup test, probe: intentionally fail a commit after data files were uploaded but before the catalog recorded the table updates. Expected: the engine's or operator's cleanup process can locate unreferenced files and remove them, or your policy retains them for a retention window. If you have no method to identify orphaned files, add a process that cross-checks object store contents against current Iceberg metadata snapshots.
  • Scale and latency test, probe: run N concurrent multi-table commit attempts, where N matches expected production peak concurrency. Expected: commit latency remains within your SLO and commit failures remain at an acceptable rate. If latency or failure rate degrades sharply, collect traces for the catalog commit path and consider splitting logical transactions across fewer tables or moving high-churn tables to separate transactions.
  • Rollback and recovery drill, probe: simulate a controller crash after the server accepted the commit but before clients processed the response. Expected: subsequent client state queries confirm the commit outcome, and no duplicate commits occur when retry pseudocode runs. If clients cannot determine the committed state, add an explicit inquiry step to the catalog commit API before retrying a commit.

Label facts checked on September 22, 2026: the Iceberg REST OpenAPI file and the REST protocol text describe the commit path and requirement/update semantics. Verify your catalog implementation version against the Apache Iceberg codebase for exact behavior.

Failure injection tests and a concrete test harness

To be confident in production you need deterministic failure injection. Below is a minimal test harness design that exercises the worked example commit, requirement/update pairs, and the retry pseudocode. The harness purposefully injects specific failures so you can observe server and client behavior.

Design notes, prerequisites: an environment where you can manipulate network responses, a catalog instance with REST commit endpoint enabled, and an object store you can inspect for orphaned files. Use containers or VM snapshots so tests are repeatable.

  • Harness step 1, baseline success, action: send the worked example multi-table commit request with correct requirement ids. Assert: both table snapshots advance and the response contains a commit id. Record timings and commit id.
  • Harness step 2, mid-commit server crash, action: while the server is processing a commit, kill the catalog server process or drop the TCP connection. Expected: clients see a transient network error, then run the retry pseudocode. After server restart, query the table to see if the commit was applied. If the commit was recorded but client retried and attempted to reapply, ensure the server treats the retried request idempotently or rejects it with a conflict response.
  • Harness step 3, requirement mismatch, action: before sending the commit request, update one target table with a separate operation so the requirement for that table is stale. Expected: the REST commit responds with a conflict or precondition failure. The client should apply the retry pseudocode to fetch fresh metadata, rebuild the update, and attempt again. Verify that the client does not attempt blind retries without refreshing requirements.
  • Harness step 4, duplicate commit attempts, action: send the exact same JSON commit request twice in quick succession from two independent client processes, without changing requirements. Expected: the server either accepts the first and rejects the second as duplicate or treats them idempotently based on a provided client token if the OpenAPI supports it. If the server accepts both and produces two different commit ids that both change metadata, consider this a non-atomic implementation.
  • Harness step 5, orphaned data creation, action: allow the client to upload data files and then abort before the commit. Expected: those files remain in object storage and are not referenced by table metadata. Verify your cleanup policy finds them by comparing current manifests and snapshot file lists to object storage listings. Implement a timed sweep to remove objects older than your retention window that are not referenced.

For each harness step, capture server logs, catalog audit events, and object-store listings. Correlate them using timestamps and the commit id found in the response or logs. This correlation makes it possible to explain exactly what happened during a failure and to tune retry and cleanup behavior.

Decision table: when to enable multi-table commits

Multi-table commits reduce application-level coordination, but they are not always the right tool. Use this decision table to evaluate whether to enable multi-table commits in a given workload and catalog. Each row is a rule with recommended action and rationale.

  • High-frequency, single-table churn, condition: one table sees most writes and others are read-only or low-churn. Action: avoid multi-table commits. Rationale: the additional coordination adds latency and risks creating contention across unrelated tables.
  • Coordinated schema or partition changes across many tables, condition: you must update schema or partitioning for multiple tables in a single logical operation. Action: consider multi-table commits if the catalog supports them and you can tolerate slightly higher latency. Rationale: atomic visibility across tables avoids partial schema changes that break queries.
  • Cross-table referential update needed, condition: you must atomically swap data such that multiple tables transition together, for example updating a fact and its denormalized summary. Action: enable multi-table commits after validating concurrency behavior. Rationale: this is the most compelling use case, it prevents readers from seeing inconsistent states.
  • High concurrency with small updates, condition: hundreds of concurrent clients write small updates to overlapping table sets. Action: do not enable multi-table commits unless you have verified catalog scalability at that concurrency. Rationale: servers may serialize commits and become a throughput bottleneck.
  • Strict transactional isolation required, condition: you require serializable isolation across a distributed system. Action: multi-table commit alone is insufficient. Rationale: Iceberg catalog atomicity is not the same as distributed transaction isolation. Use an external transaction manager if strict isolation is required.
  • Limited engine support, condition: your execution engine does not expose or test the requirement/update pairing behavior. Action: do not enable multi-table commits until engine support is validated. Rationale: engine-level retries and state reconciliation are required for correct behavior on transient failures.

Each decision above should be accompanied by a plan for instrumentation and rollback criteria. For example, if you enable multi-table commits for the schema change use case, define a rollback window and monitor the commit error rate. If the error rate exceeds your threshold, automatically disable multi-table commits and revert to single-table coordinated changes.

Label facts checked on September 22, 2026: the OpenAPI spec and REST protocol describe the commit semantics that allow requirement and update pairs. The REST endpoint is optional in catalog implementations; verify presence before depending on it.

FAQ

Does the Iceberg REST commit endpoint guarantee multi-table transactions?

The endpoint defines a request shape that can express multi-table updates and requirements. Whether you get catalog-level atomicity depends on the catalog server implementation and the engine behavior. The endpoint is optional; verify the server exposes and implements it.

What is the difference between catalog atomicity and distributed transaction isolation?

Catalog atomicity means the catalog applies the set of table updates in one server operation or rejects them all. Distributed transaction isolation is a stronger property about how concurrent readers and writers observe partial or complete changes across multiple resources. A catalog could apply updates atomically but still expose intermediate states to readers if its snapshot visibility semantics allow it. Test the server to see how snapshot visibility behaves.

What should I verify in an engine before enabling multi-table commits?

Verify the engine builds requirement entries bound to the metadata locations or snapshots it reads, serializes the payload according to the server OpenAPI, and handles requirement failures with bounded retries that recompute metadata for all tables in the commit.

How do I handle orphaned data files from failed attempts?

Implement a background cleanup job that identifies temporary paths and removes files older than a safe retention window. Prefer deterministic temp naming and bucket lifecycle policies to make cleanup predictable. Track orphan metrics so you can tune cleanup cadence.

What are common causes of requirement failures?

Concurrent modifications to one of the tables, long-running commit preparation windows that allow other clients to change table state, and incorrect requirement construction in the client are the typical causes. Instrument the failure responses to identify which table and requirement type failed.

Where should I look for authoritative request/response schemas?

The authoritative shapes are in the Iceberg REST protocol documentation and the OpenAPI definition in the Iceberg repository. Confirm the server you run implements those shapes or provides server-specific documentation. Facts in this article checked on September 22, 2026 against those sources.

These guides cover adjacent implementation details that are outside this article's main scope.

Keep learning

For a deeper treatment, download Apache Iceberg: The Definitive Guide, co-authored by Alex Merced and available free from Dremio.

To put the catalog and table-format ideas into practice, explore Dremio's Apache Iceberg platform.

Sources

Try Dremio Cloud free for 30 days

Deploy agentic analytics directly on Apache Iceberg data with no pipelines and no added overhead.