Dremio is now part of SAP
Dremio Blog

45 minute read · October 27, 2026

Apache Iceberg DML Support Matrix: MERGE, UPDATE, DELETE, and Branch Writes by Engine

Alex Merced Alex Merced Head of DevRel, Dremio
Apache Iceberg DML Support Matrix: MERGE, UPDATE, DELETE, and Branch Writes by Engine
Copied to clipboard

Quick answer

Iceberg DML, meaning MERGE, UPDATE, DELETE, row-level upserts, and branch-targeted writes, is implemented by query engines, not by the Iceberg table format itself. Engines vary in which SQL surfaces they expose, how they map those surfaces to file operations, and which branch or snapshot targets they can write to. This article documents those differences, shows how to test and verify behavior across engines, explains operational consequences, and gives a practical rollout and measurement checklist. The matrix and examples below were verified on October 27, 2026 where freshness matters.

How to read this matrix and why it matters

I will not present a single yes/no table and leave you to assume compatibility. You need to know three things to make a safe operational decision: the SQL syntax the engine exposes, the underlying write mode the engine uses for a DML operation, and the branch or snapshot targets the engine supports. SQL syntax alone does not prove identical conflict semantics or file layout changes. I will show concrete tests you can run to confirm behavior in your environment and explain what to measure after deployment.

ENGINE DML CAPABILITY LAYERS1Cross-engine DML smoke tes…2Copy-on-write and merge-on…3Branch write acceptance ma…4Verify the resultA useful implementation has an observable result at every boundary. A successful command alone is not the acceptance test.
Engine DML capability layers. Each stage has a result that can be checked before the next stage begins.

Engine DML capability layers

Think of engine DML support as stacked capabilities. From lowest to highest: basic file writes; partition overwrite and dynamic partition insert overwrite; atomic replace or overwrite of table snapshot; copy-on-write file replacements produced by engine-coordinated MERGE/UPDATE/DELETE; and finally, branch- or snapshot-targeted writes, where the engine can point a mutation at a non-default branch or snapshot. Each layer introduces different requirements for coordination, metadata updates, and conflict handling.

Practically, that means an engine that documents MERGE support still may implement it by rewriting entire partitions or by doing a row-level file rewrite. The semantics that matter for correctness and performance are:

  • Conflict detection and retry model, for example optimistic compare-and-set at manifest list or manifest level.
  • Whether the operation rewrites entire data files for matched partitions, or writes new files for updated rows only.
  • Atomicity at Iceberg metadata layer, for example whether the engine writes a new manifest list and completes a commit with a single rename and metadata pointer update.
  • Branch or snapshot write targets, including whether the engine can commit to a non-default branch or create branch snapshots directly.

These layers are visible in the primary sources for each engine. Spark documents its write behaviors and the supported write modes on the Iceberg project documentation page for Spark writes. Trino documents both its Iceberg connector write operations and its SQL MERGE documentation. Dremio documents MERGE, UPDATE, and DELETE for Iceberg tables, and calls out source-specific branch limits. Flink describes INSERT, overwrite, upsert sinks, and branch-targeted writes rather than a universal SQL MERGE surface. Links to these pages are listed in Sources and I reference them in-line where relevant.

Copy-on-write versus merge-on-read: what engines actually do

Two different physical strategies dominate row-level mutations: copy-on-write and merge-on-read. Copy-on-write means the engine computes which files must change, writes replacement files, updates manifests, and commits a new snapshot. Merge-on-read means the engine appends delta files or rewrite files and a reader must merge base files and deltas at read time, or a compaction job later consolidates changes.

COPY-ON-WRITE VERSUS MERGE-ON-READRisk 1The matrix needs a visible checked dateTest itRisk 2SQL syntax does not prove identical conflict semanticsBound itRisk 3Branch writes are source and engine specificMonitor it
Copy-on-write versus merge-on-read. Each technical risk needs a matching test, boundary, or operating signal.

Why this matters. Copy-on-write will produce fewer readers-side surprises, because readers see a new snapshot where rows are already materialized in new files. Its downside is write amplification: rewriting large files when only a few rows change. Merge-on-read reduces write amplification but increases read-time CPU and adds complexity for snapshot visibility and compaction scheduling.

Engine examples from the docs: Spark's integration documents commit behavior for MERGE, UPDATE, DELETE and multiple write modes in the project documentation. Flink emphasizes upsert and sink semantics and leaves the merge semantics to specific sink implementations. Trino documents MERGE at SQL level through its SQL MERGE guide and describes connector capabilities. Dremio's docs list MERGE, UPDATE, and DELETE and mention limits by source for branch writes. None of these pages claim that SQL syntax alone guarantees identical conflict resolution or identical file layout across engines. You should verify both the syntax and the low-level file result when portability matters.

Branch writes: what "write to branch" actually means

Branch writes let you target a non-default named branch or snapshot. This is powerful for transactional workflows, previewing changes, and multi-developer workflows. But in practice branch write support is source and engine specific. Some engines can create and update branches via their Iceberg integrations, others can only write to the default branch or require a table rename. You must test the exact branch behavior for your engine version and connector.

BRANCH WRITE SUPPORT BOUNDARIESObservecollect the signalCompareuse a baselineDiagnoselocate the boundaryActchange one variablemeasured evidenceunexpected changesmallest safe responsenew baseline
Branch write support boundaries. The loop turns table or catalog signals into controlled operational changes.

Concrete cautions: the matrix below includes a visible checked date. Also, SQL syntax does not prove identical conflict semantics. If your workflow assumes an engine can commit to branch X while another engine reads branch Y, verify whether the engine updates branch pointers atomically or relies on out-of-band metadata operations. For Dremio, the documentation explains branch-specific limits for some sources. For Spark and Flink, the Iceberg project pages describe how writes are executed but do not claim identical branch semantics across engines. Trino documents branch behavior in its connector documentation; you should verify branch pointer updates by checking the Iceberg metadata files after a commit.

Verified support matrix, checked October 27, 2026

This matrix summarizes the documented behaviors for Apache Iceberg DML across Spark, Flink, Trino, and Dremio as of October 27, 2026. Treat each cell as a summary of documented capability, not a promise of identical conflict resolution. Verify your engine version and connector configuration against the primary sources listed in Sources.

Key headings used in the matrix: SQL surface describes whether MERGE/UPDATE/DELETE are available as SQL statements; write strategy summarizes typical on-disk behavior (copy-on-write versus append/upsert/delta); branch target indicates whether the engine can commit to a named branch or only to the default branch; source limits notes any engine-documented source-specific exceptions.

  • Spark: SQL surface, documentation of MERGE, UPDATE, DELETE, overwrite, and branch writes are described on the Iceberg Spark writes page. Write strategy: typically copy-on-write file replacement for MERGE/UPDATE/DELETE. Branch target: Spark can point to branches depending on the Iceberg API usage, but confirm for your Spark/Iceberg connector version. Source limits: check the Iceberg docs for nuances in specific catalog implementations.
  • Flink: SQL surface: Flink focuses on INSERT, overwrite, and upsert sink semantics; it does not present a universal canonical SQL MERGE in the Iceberg docs for Flink. Write strategy: often upsert/append with sink-dependent semantics; some deployments use changelog tables with compaction. Branch target: Flink integrations can target branches when implemented by the sink, but behavior depends on the sink implementation and Flink version.
  • Trino: SQL surface: MERGE is documented in Trino's SQL MERGE guide and the Iceberg connector docs. Write strategy: Trino generally performs copy-on-write replacement files for matched rows and commits a new snapshot. Branch target: the connector documents branch handling; verify whether the Trino version updates branch pointers atomically for your use case.
  • Dremio: SQL surface: MERGE, UPDATE, DELETE are documented in Dremio's Iceberg commands, but branch writes are source-specific. Write strategy: Dremio commits use Iceberg metadata APIs and typically perform copy-on-write file replacements for DML. Branch target: Dremio documents source-specific branch limits; see the Dremio Apache Iceberg commands and the Dremio MERGE specifics for details.

Do not treat this as exhaustive. The goal is to make you test hypotheses where you cannot accept subtle differences. For example, SQL MERGE between Trino and Spark may syntactically look similar, but conflict retries and the exact files rewritten can differ. The following sections show practical tests and what to inspect in the catalog and metadata to confirm behavior.

Practical verification and smoke tests

I recommend a small battery of smoke tests you can run in any environment that has your engine, the Iceberg catalog, and a safe test table. Use these to validate SQL surface, write strategy, commit atomicity, and branchability. I will give SQL examples where applicable and explain what metadata files to inspect. The SQL examples are illustrative of the syntax documented by each project; always check your engine's supported version and connector configuration.

1) Basic MERGE smoke test (syntax and semantic existence)

Purpose, confirm the engine accepts MERGE and commits a snapshot visible to readers.

Steps:

  • Create base table with a small set of rows and known file layout. Example: partition by date with two data files.
  • Run MERGE to update one row, insert one row, and delete one row in a single statement.
  • Immediately query the default branch (or table) in a new session to confirm snapshot visibility.
  • Inspect Iceberg metadata: new snapshot id, manifests changed, and data file additions. If your catalog is filesystem-based, examine the metadata JSON files in the table metadata directory. If your catalog is Hive/Glue, use the Iceberg API or the engine's catalog tools to fetch metadata.

What to expect:

  • Snapshot incremented and a new metadata JSON exists.
  • Manifests either contain replaced file entries or added entries depending on copy-on-write vs append.
  • Reader in a new session sees the new snapshot state. If readers see a partial state, investigate engine commit atomicity.

2) Conflict test (concurrent commits)

Purpose, observe conflict detection and retry model.

Steps:

  • Session A reads snapshot S and prepares a MERGE that will rewrite file F.
  • Session B concurrently runs an UPDATE that rewrites file F and commits.
  • Session A now runs its MERGE and either succeeds, fails with a conflict, or retries based on engine behavior.

What to inspect:

  • Whether Session A gets an optimistic conflict exception, or whether the engine automatically retries.
  • How many times the engine attempted a commit, and whether partial files were left behind. Check the metadata and temporary staging directories used by the engine.

3) Copy-on-write and merge-on-read checks

Purpose, confirm whether the engine rewrites entire data files or writes small replacement files, and evaluate read-time impact.

Steps:

  • Start with a large data file that contains 10,000 rows. Run an UPDATE that affects 10 rows.
  • Inspect data files post-commit: did the engine rewrite the large file, or create small replacement files containing only updated rows?
  • Run a reader workload that scans the table and measure CPU and IO. For merge-on-read you should see additional work merging deltas at read time unless compaction ran.

Metrics to capture: write amplification ratio (bytes written versus bytes logically changed), reader CPU per scan, and number of files scanned per query. This will tell you if copy-on-write is causing expensive writes or if merge-on-read is causing expensive reads.

4) Branch write acceptance matrix (branch write checks)

Purpose, verify whether target engine can create/update named branches and whether the branch pointer update is atomic and visible to readers.

Steps:

  • Create a branch B1. If your engine exposes branch creation SQL or APIs, use them. Otherwise use the Iceberg API to create a branch in the catalog, or create a snapshot and name a branch pointing to it.
  • Run a MERGE/UPDATE/DELETE targeted at branch B1 using the engine's documented syntax or connector options. For engines that do not support direct branch targeting, test whether you can achieve the same outcome by updating metadata through the Iceberg API or a manual commit.
  • Verify branch pointer: check the branch pointer in the table metadata, confirm that the default branch did not change unless intended, and confirm snapshot ids on the branch.

What to expect and inspect:

  • If the engine supports branch writes, the branch pointer should be updated to the new snapshot atomically. If it does not support branch writes, the operation may either fail or write to the default branch instead.
  • Check manifests and snapshot lineage for the branch snapshot. If the branch pointer is updated via a separate metadata operation, you may observe a brief window where the branch pointer is not yet updated. That is why you must verify pointer updates in the metadata files.

For readers using Dremio, consult the Dremio MERGE documentation for specifics on branch limits and operational notes. The Dremio Apache Iceberg commands page is useful for a complete listing of the commands and examples. For pattern guidance on upserts and SCD type 2, the Dremio blog post on merge patterns is practical and contains examples that map to operational steps. For version differences between Iceberg v2 and v3 and what it means for table layout and metadata, read the Dremio analysis on Iceberg versions to decide if you need a migration strategy for your tables. These Dremio resources are designed to help you map documented engine behavior to operational practices in Dremio environments and should be read alongside the Iceberg and engine docs cited in Sources.

Worked examples

The following worked examples are designed to be reproducible. They show the specific metadata checks and measurements I use when validating engine behavior. Replace placeholders with your environment values and verify versions against the primary sources.

Worked example A, cross-engine DML smoke tests

Goal, compare MERGE semantics across Spark, Trino, and Dremio for the same test table stored in a filesystem-backed Iceberg catalog.

Setup steps:

  • Create a table customers with partition date, and insert 1000 rows across two files.
  • Record the initial snapshot id and manifest list file paths.
  • Run the same logical MERGE statement on each engine, for example: MERGE INTO customers c USING updates u ON c.id = u.id WHEN MATCHED THEN UPDATE SET c.status = u.status WHEN NOT MATCHED THEN INSERT (id, name, status) VALUES (u.id, u.name, u.status) WHEN MATCHED AND u.status = 'REMOVE' THEN DELETE. Use engine-specific SQL if syntax differs.

Verification steps:

  • After each engine commit, record the new snapshot id, the number and size of new data files, and manifests changed.
  • Measure bytes written during the operation using OS or cloud storage metrics and compare to the logical data changed.
  • Run a consistent reader from a separate session and confirm visibility of the result. If the reader sees partial state, inspect whether the engine uses multi-step pointer updates.
  • Compare the final table state across engines by reading the table at the committed snapshot ids.

What I have seen in practice: engines that document MERGE usually implement a copy-on-write strategy that replaces affected files. That leads to substantial write amplification if your MERGE touches many rows in large files. Trino and Spark document MERGE in their respective docs, and you should consult the Trino MERGE docs for any syntax differences. Dremio documents MERGE and has notes about branch limits in the Apache Iceberg commands reference. The Iceberg Spark writes page describes the file-level behavior to help you interpret what you see in the metadata.

Worked example B, copy-on-write and merge-on-read checks

Goal, quantify write amplification and read cost differences.

Setup:

  • Write a single table with files of 1 GB each, five files total, containing a total of 5 GB.
  • Run an UPDATE that changes one row in each file, or update a single row across files.

Measurements:

  • Bytes written by the update job. If the engine rewrites the full files, you will see roughly 5 GB written.
  • Time for subsequent scans. If merge-on-read is used, you will see more CPU spent per scan until compaction finishes.
  • Number of files returned by a table scan. Copy-on-write will typically result in fewer files to read for the new snapshot.

Interpretation: use the numbers to decide whether you prefer write-side amplification or read-time merging. If your workload is write-heavy small updates, merge-on-read plus periodic compaction may be better. If most queries read full table snapshots, copy-on-write produces simpler read paths.

Worked example C, branch write acceptance matrix

Goal, verify which engines in your environment can create and commit to branches, and whether branch pointer updates are atomic.

Test matrix steps:

  • For each engine (Spark, Flink, Trino, Dremio), attempt to create branch feature/A via the engine API or Iceberg API. If the engine has explicit SQL or connector options for branch writes, use them.
  • Attempt a MERGE into the branch. If the engine rejects the attempt, record the error and consult engine docs for supported approaches. Dremio documentation notes source-specific branch constraints; consult the Dremio MERGE docs for those specifics.
  • After each attempted commit, read the branch pointer in Iceberg metadata to confirm that it points to the new snapshot id and check the default branch pointer to ensure it did not change unexpectedly.

Accept criteria:

  • Branch pointer is updated atomically and readers of that branch see the result immediately after commit.
  • No unexpected changes to default branch unless the operation intended to affect it.
  • Documented errors are captured and correlate to documented limitations in the engine docs.

Failure modes and what to watch for

Row-level DML introduces several failure modes you must guard against. I list the ones I have seen in production and explain how to detect and mitigate each.

  • Partial commits and dangling files, caused by incomplete cleanups on client failure. Detect this by scanning staging directories and orphan data files, and by checking manifests referenced by the latest committed metadata. Mitigate by ensuring your engine version has the documented cleanup paths and by running periodic garbage collection or compaction jobs.
  • High write amplification on copy-on-write, causing increased cloud storage costs and longer commit times. Detect with bytes written metrics and compare to logical bytes changed. Mitigate by reducing file sizes or batching updates and scheduling compaction at low-load windows.
  • Inconsistent branch pointer updates, where a branch pointer is updated separately from the snapshot creation, leaving a small window with inconsistent read behavior. Detect by inspecting the snapshot lineage and observing transient reader failures. Mitigate by using versions and connectors that guarantee atomic branch pointer updates, or accept a coordination step for branch management in your workflow.
  • Conflict-related retries causing long-running jobs or cascading failures, seen when many concurrent writers target the same small set of files. Detect by measuring commit retries and latency. Mitigate by sharding writes, reducing contention on hot partitions, or using pre-splitting strategies.
  • Reader performance regressions with merge-on-read, because many small delta files accumulate before compaction. Detect via query latency and CPU metrics. Mitigate by scheduling compaction and monitoring delta file growth.

Rollout checklist

Use this checklist when introducing row-level DML or branch writes into production.

  • Confirm engine version and connector configuration against the authoritative docs listed in Sources.
  • Run the cross-engine smoke tests and branch write acceptance tests in a staging environment connected to your catalog. Use the write-tuning test environment, the schema-evolution test environment, or the incremental-read test environment as test clusters if they are available to you. Record results and artifact locations for later audit.
  • Measure baseline metrics: bytes written per DML operation, number of files per snapshot, average query latency for common scans, and commit latency and retry counts.
  • Decide file sizing policy. If your MERGE/UPDATE operations target small row counts per large file, consider smaller file sizes or targeted compaction windows.
  • Deploy DML operations with feature gates. Start with low traffic or staged branches before permitting default-branch commits.
  • Add automated checks to detect dangling files, failed commits, and rapid growth in manifest lists or delta files.
  • Document operational procedures, including how to rollback branches, how to recover from partial commits, and how to run compaction and cleanup.

What to measure after deployment

After you deploy DML support, track these metrics continuously for at least the first three weeks. They will alert you to both correctness and cost problems.

  • Commit success rate and average commit latency, including retry counts per commit.
  • Bytes written per logical change, and the ratio of bytes written to bytes changed (write amplification).
  • Number of new data files produced per DML operation and average file sizes.
  • Reader CPU per GB scanned and average query latency for common analytical queries.
  • Number of snapshots and manifests retained, and growth rate of table metadata.
  • Branch pointer updates per hour, and any unexpected default branch modifications.

Decision points and trade-offs

You will need to choose between simpler read paths and lower read CPU at the cost of write amplification, or lower immediate write cost at the expense of read-time merging and compaction complexity. You will also need to decide whether to allow multiple engines to perform DML on the same tables. If you do, plan for strict version and connector control, and enforce testing across all engine combinations used in production.

Another decision is whether to rely on branches for developer workflows or use isolated test tables. Branches can be powerful but are only useful if your engine’s branch pointer behavior matches your workflow assumptions. Test the exact branch pointer atomicity and visibility as described earlier.

Appendix: metadata and inspection commands

Exact commands depend on your catalog. The general approach is the same: after a commit, list the table metadata directory, inspect latest snapshot JSON, and examine the manifest list for file adds/deletes. If you use an object store, you can also query storage metrics during the commit window to measure bytes written.

For Dremio users, the Dremio Apache Iceberg commands pages show the available commands and how Dremio exposes Iceberg table operations. The Dremio merge patterns blog post is useful for mapping common change-data patterns to actual MERGE implementations and showing practical SQL patterns that work in Dremio. The Dremio article on Iceberg v2 versus v3 helps you understand metadata layout differences that affect how you inspect files and snapshots. Use those pages to tailor the generic metadata inspection steps to a Dremio cluster.

MATRIX VERIFICATION PIPELINEInventoryversions and consumersTestfeature and failure pathsCanaryone bounded workloadDecideexpand or stopA failed gate returns to inventory with evidence. It does not become a production exception.
Matrix verification pipeline. A reversible canary keeps an unsupported client or unsafe policy from becoming a fleet-wide incident.

Cross-engine DML smoke tests: exact steps to compare behavior

Short answer up front, in practice: running the same MERGE, UPDATE, or DELETE SQL against two engines proves only syntactic acceptance, not identical conflict resolution. To learn whether two engines behave the same under concurrent writes, run paired smoke tests that exercise identical timing, isolation, and commit patterns. The following is a concrete sequence you can run in your lab to compare Trino, Spark, Flink, and Dremio against a single Iceberg table. The verified support matrix referenced earlier was checked October 27, 2026 and your results need the same timestamp and engine versions recorded.

Prepare a baseline table with small file sizes so conflicts are likely, using table partitioning if your engines require it. Use a simple business key and an increasing event time column so you can deterministically order updates and detect lost updates.

  • Step 0, create the table once with Spark or Dremio, record Iceberg metadata location and current snapshot id.
  • Step 1, load N rows, where N is small enough to examine file contents, for example 1000 rows split into 10 files. Persist file list in test logs.
  • Step 2, define two operations that will conflict. For MERGE, use a WHEN MATCHED UPDATE that changes a payload field based on a deterministic rule. For UPDATE, modify a single column. For DELETE, remove rows matching a key range.
  • Step 3, run two concurrent clients. One client uses Engine A, the other uses Engine B. Start Engine A, wait 200ms, start Engine B. Vary the delay in repeat runs to exercise different interleavings.
  • Step 4, after both commits complete, capture Iceberg snapshot history from the metadata location, and compare resultant data files and manifest lists.
  • Step 5, validate application-visible semantics: row counts, equality of key columns, and which update won when both changed the same key. Record differing outcomes as a behavior divergence.

Run at least 50 iterations per engine pair. For each iteration, keep these items in the test output: engine versions, catalog connector configs, Iceberg table path, snapshot ids before and after, and the complete log of operations. Without these artifacts you cannot diagnose whether a divergence is a timing effect, a configuration difference, or an engine bug.

What to expect by engine based on the primary sources: Spark writes document the Spark writer behavior for Iceberg, Flink writes describe Flink's two-phase commit semantics, and Trino documents its connector and MERGE SQL. Consult each page for the exact semantics your engine version declares. Do not assume identical conflict semantics even when SQL is identical. Verify.

Failure injection tests for copy-on-write versus merge-on-read

This section gives an actionable failure injection plan to prove whether your engine performs copy-on-write or merge-on-read and to expose intermediate inconsistent states. The tests require control over the compute node or container processes that perform commits so you can kill a writer at defined points. The metadata behavior described here was checked October 27, 2026 against the listed primary sources and you should record your engine versions when you run these tests.

  • Test A, interrupt after data file write but before commit metadata write. Procedure: start a MERGE that will rewrite files. Watch the table directory for new data files created by the writer. Kill the writer process after you see new data files, but before it issues the metadata commit. Inspect the table metadata location for no new snapshot, and verify the table still points at the previous snapshot. Expected outcome for copy-on-write engines: dangling data files on storage, no snapshot change. For merge-on-read engines: there may be staged delta files or logs that likewise do not commit. Record file names, sizes, and timestamps.
  • Test B, interrupt during metadata commit, after new manifest list appears but before completing the commit action. Procedure: instrument the writer JVM or process to pause after writing new manifests but before atomically moving the table metadata file. Kill the process then inspect the metadata directory. Expected outcomes vary by engine. Some writers write a temporary metadata file and then atomically rename, so you may see a temporary file that needs cleanup. Others may leave an inconsistent manifest chain. Verify whether the engine provides a repair or rollback command documented in its write docs.
  • Test C, induce network partition between compute and metadata service. Procedure: for engines that rely on a metastore or object storage with eventual consistency, throttle or drop requests at the object storage level while a commit is in flight. Observe whether manifests become visible to other clients and whether a concurrent write accepts the partial state. Expected: eventual consistency mismatches can cause apparent lost updates or snapshot forks; collect stack traces and storage operation traces to diagnose.

After each failure injection run, run a metadata inspection sequence from at least two different engines. Compare snapshot ids, manifests, and the file lists returned from Iceberg metadata APIs. If any engine offers a documented recovery or repair command, try it in a nonproduction clone of the table and record its effect. Systems that provide atomic metadata rename semantics tend to be easier to recover.

Branch write acceptance matrix: reproducible checks and automation hints

This section converts the branch write acceptance matrix into a set of automated checks you can run in CI when validating a connector or engine upgrade. Branch writes mean different things to different engines and to the catalog implementation. The following checks go beyond a syntax check and exercise acceptance, visibility, and rebasing behavior as applicable. Record results with the table path, branch name, and engine version. The matrix in the draft is dated and was checked October 27, 2026.

  • Check 1, branch creation and write acceptance. Create a named branch using the engine or catalog mechanism if supported. If the engine lacks an API for branch creation, create the branch using the Iceberg Java API or catalog operations that your environment documents. Then run a small INSERT or MERGE to that branch. Verify the snapshot id on that branch differs from main and that the main branch snapshot remains unchanged.
  • Check 2, visibility from another engine. After composing a branch commit with Engine A, query the same branch name with Engine B. Expected outcomes vary. Some engines will read the branch snapshot by name if the catalog passes branch names through, others will ignore branch semantics and read the default branch. If Engine B does not return the branch-specific snapshot, log this as a branch visibility mismatch.
  • Check 3, commit rebasing or merge acceptance. Create concurrent commits to the same branch from two different engines. If the engine supports optimistic concurrency with rebase or conflict detection, one commit should fail or require explicit rebase. If the engine allows blind fast-forward, both may succeed producing a forked snapshot graph. Detect forked histories by walking parent pointers in Iceberg metadata.
  • Check 4, branch delete or rename behavior. Delete or rename the branch from Engine A and verify Engine B sees the change. Not all engines implement branch delete via the catalog. Record whether the catalog operation affects only the catalog or writes Iceberg metadata to mark branch heads.
  • Check 5, promotion to main. Promote a branch head to main or merge snapshots using the catalog or Iceberg API. Verify the main branch snapshot id updates and that competing readers see the promotion on subsequent queries. Engines that require explicit refresh calls may not reflect the promotion until refreshed.

Automate these checks in CI as table-level integration tests. For each check, capture the following metrics and artifacts: engine logs around commit time, object storage operation counts, commit latency, and snapshot graph before and after. Use these artifacts to distinguish a visibility bug from a commit acceptance issue. If a test fails, the recorded snapshot ids and manifest lists are your primary troubleshooting inputs.

Practical evaluation sequence and decision table for rolling an engine upgrade

When you upgrade a query engine or connector that writes Iceberg, you need a reproducible evaluation sequence. Below is a concise, ordered plan you can follow in a staging environment before touching production. Each step maps to a failure impact and a suggested gate.

  • Step 1, baseline capture. Record current engine version, connector config, Iceberg table metadata location, and current head snapshot id. Gate: snapshot id saved and artifacts uploaded to your test storage.
  • Step 2, syntax and acceptance tests. Run the Cross-engine DML smoke tests for each DML flavor. Gate: all engines accept the SQL without error and produce consistent row counts in a nonconcurrent run.
  • Step 3, concurrency stress. Run the concurrent paired tests described above for 1 hour. Gate: divergence rate below your acceptable threshold. Define threshold by business need, for example less than 0.1 percent of keys diverge.
  • Step 4, failure injection. Run the copy-on-write versus merge-on-read failure injection tests. Gate: no unrecoverable table metadata states and documented recovery steps succeed in staging.
  • Step 5, branch semantics. If your deployment uses branches, run the branch acceptance matrix. Gate: branch visibility between engines and promotion behavior meet application requirements.
  • Step 6, monitoring and rollback. Deploy engine upgrade to a small subset of workers. Monitor commit latency, object storage write errors, and snapshot creation rate. Gate: commit error rate and commit latency within predefined bounds for 24 hours.

Decision table. Use this table to decide whether to proceed, pause, or rollback. These are suggested thresholds you can tune for your environment.

  • If concurrent divergence rate is 0 percent and recovery trials succeed, proceed to staged deploy.
  • If divergence is nonzero but under 0.1 percent and all failures recoverable without manual table surgery, pause and expand staging tests to 72 hours.
  • If divergence is greater than 0.1 percent, or any failure injection produces unrecoverable metadata, rollback the upgrade and open an investigation with engine project maintainers and your catalog vendor.

Record all of these test results with an explicit checked date. The matrix in this article was verified October 27, 2026. When you publish your verification, include engine versions, catalog configs, and Iceberg snapshots so others can reproduce your findings.

FAQ

1. Does Iceberg itself provide MERGE, UPDATE, and DELETE?

No. Iceberg is a table format and metadata layer. DML surfaces are provided by engines and connectors. Check the engine documentation to know what SQL is supported and how it maps to Iceberg commits. Facts checked on October 27, 2026.

2. If Trino and Spark both accept the same MERGE SQL, can I assume identical conflict behavior?

No. SQL syntax does not prove identical conflict semantics. You must verify conflict detection, retry behavior, and file rewrite patterns by running the concurrent commit tests described above. This is critical if two engines will both write to the same tables. Facts checked on October 27, 2026.

3. Can every engine write to a named branch?

Not necessarily. Branch writes are source and engine specific. Some engines expose branch-targeted commits, others do not. You must test branch pointer updates and consult your engine docs. Dremio documents source-specific branch limits in its Iceberg commands. Facts checked on October 27, 2026.

4. Which strategy has better performance, copy-on-write or merge-on-read?

It depends on workload. Copy-on-write reduces read-time complexity but increases write amplification. Merge-on-read reduces write IO for small updates but increases read CPU and requires compaction. Measure write amplification and reader CPU as described to decide for your workload.

5. How do I detect partial commits or dangling files?

Scan the table metadata directory for orphan data files and check staging or temporary directories used by the engine. Compare manifests referenced by the latest committed metadata to actual files present. If you find files not referenced by any snapshot, they are candidates for cleanup. Add automated checks to flag rapid growth in unreferenced files.

6. Where can I find concrete examples for MERGE patterns and Iceberg version differences?

See the Dremio blog post on merge-into patterns for upserts and SCD type 2 for practical SQL examples and patterns, and the Dremio article comparing Iceberg v2 and v3 for versioned behavior that affects metadata inspection and migration. These complement the engine and Iceberg project docs linked in 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.