This is an automated email from the ASF dual-hosted git repository.

xushiyan pushed a commit to branch asf-site
in repository https://gitbox.apache.org/repos/asf/hudi.git


The following commit(s) were added to refs/heads/asf-site by this push:
     new d54da8cbc745 [BLOG] MOR in Lakehouse Table Formats (#13589)
d54da8cbc745 is described below

commit d54da8cbc745abcc5391a61818afcf959708611f
Author: Dipankar Mazumdar <[email protected]>
AuthorDate: Mon Jul 21 19:15:30 2025 -0400

    [BLOG] MOR in Lakehouse Table Formats (#13589)
---
 website/blog/2025-07-21-mor-comparison.md          | 262 +++++++++++++++++++++
 .../2025-07-21-mor-comparison/mor-1200x600.jpg     | Bin 0 -> 394518 bytes
 .../blog/2025-07-21-mor-comparison/mor_fig1.png    | Bin 0 -> 257490 bytes
 .../blog/2025-07-21-mor-comparison/mor_fig2.png    | Bin 0 -> 432108 bytes
 .../blog/2025-07-21-mor-comparison/mor_fig3.png    | Bin 0 -> 334427 bytes
 .../blog/2025-07-21-mor-comparison/mor_fig4.png    | Bin 0 -> 246890 bytes
 .../blog/2025-07-21-mor-comparison/mor_fig5.png    | Bin 0 -> 300596 bytes
 .../blog/2025-07-21-mor-comparison/mor_fig6.png    | Bin 0 -> 298891 bytes
 .../blog/2025-07-21-mor-comparison/mor_fig7.png    | Bin 0 -> 230502 bytes
 .../blog/2025-07-21-mor-comparison/mor_fig8.png    | Bin 0 -> 278578 bytes
 .../blog/2025-07-21-mor-comparison/mor_fig9.png    | Bin 0 -> 285671 bytes
 11 files changed, 262 insertions(+)

diff --git a/website/blog/2025-07-21-mor-comparison.md 
b/website/blog/2025-07-21-mor-comparison.md
new file mode 100644
index 000000000000..dd24acabd692
--- /dev/null
+++ b/website/blog/2025-07-21-mor-comparison.md
@@ -0,0 +1,262 @@
+---
+title: "A Deep Dive on Merge-on-Read (MoR) in Lakehouse Table Formats"
+excerpt: "How is MoR implemented in Hudi, Iceberg, Delta and how it impacts 
workloads"
+author: Dipankar Mazumdar
+category: blog
+image: /assets/images/blog/2025-07-21-mor-comparison/mor-1200x600.jpg
+tags:
+- Apache Hudi
+- Merge-on-Read (MoR)
+- Streaming
+---
+
+:::tip 
+TL;DR
+- Merge-on-Read tables help manage updates on immutable files without constant 
rewrites.
+- Apache Hudi’s MoR tables, with delta logs, file groups, asynchronous 
compaction, and event-time merging, are well-suited for update-heavy, 
low-latency streaming and CDC workloads.
+- Iceberg and Delta Lake also support MoR, but with design differences around 
delete files and deletion vectors.
+
+:::
+
+As [open table 
formats](https://www.onehouse.ai/blog/open-table-formats-and-the-open-data-lakehouse-in-perspective)
 like Apache Hudi, Apache Iceberg, and Delta Lake become foundational to modern 
data lakes, understanding how data is written and read becomes critical for 
designing high-performance pipelines. One such key dimension is the table's 
write mechanism, specifically, what happens when *updates or deletes* are made 
to these lakehouse tables.
+
+This is where [Copy-on-Write 
(CoW)](https://hudi.apache.org/docs/table_types#copy-on-write-table) and 
[Merge-on-Read 
(MoR)](https://hudi.apache.org/docs/table_types#merge-on-read-table) table 
types come into play. These terms were popularized by [Apache 
Hudi](https://hudi.apache.org), in the [original 
blog](https://www.uber.com/blog/hoodie/) from Uber Engineering, when the 
project was open-sourced in 2017\. These strategies exist to overcome a 
fundamental limitation: data file formats li [...]
+
+Viewed through the lens of the [RUM 
Conjecture](https://substack.com/home/post/p-159031300?utm_campaign=post&utm_medium=web)
 \- which states that optimizing for two of Read, Update, and Memory inevitably 
requires trading off the third. CoW and MoR emerge as two natural design 
responses to the trade-offs in lakehouse table formats:
+
+* Copy-on-Write tables optimize for read performance. They rewrite Parquet 
files entirely when a change is made, ensuring clean, columnar files with no 
extra merge logic at query time. This suits batch-style, read-optimized 
analytics workloads where write frequency is low.
+
+* Merge-on-Read, in contrast, introduces flexibility for write-intensive and 
latency-sensitive workloads by avoiding expensive writes. Instead of rewriting 
files for every change, MoR tables store updates in delta logs (Hudi), delete 
files (Iceberg V2), or deletion vectors (Delta Lake). Reads then stitch 
together the base data files with these changes to present an up-to-date view. 
This tradeoff favors streaming or near real-time workloads where low write 
latency is critical.
+
+Here is a generic comparison table between CoW and MoR tables.
+
+| Trade-Off | CoW | MoR |
+| ----- | ----- | ----- |
+| Write latency | Higher | Lower |
+| Query latency | Lower | Higher |
+| Update cost | High | Low |
+| File Size Guidance | Base files should be smaller to keep rewrites 
manageable | Base files can be larger, as updates don’t rewrite them directly |
+| Read Amplification | Minimal \- all changes are already materialized into 
base files | Higher \- readers must combine base files with change logs or 
metadata (e.g., delete files or vectors) |
+| Write Amplification | Higher \- changes often rewrite full files, even for 
small updates | Lower \- only incremental data (e.g., updates/deletes) is 
written as separate files or metadata |
+
+In this blog, we will understand how various lakehouse table formats implement 
**MoR** strategy and how the design influences performance and other related 
factors.
+
+## How Merge-on-Read Works Across Table Formats
+
+Although Merge-on-Read is a shared concept across open table formats, each 
system implements it using different techniques, influenced by their internal 
design philosophy and read-write optimization goals. Here’s a breakdown of how 
Apache Hudi, Apache Iceberg, and Delta Lake enable Merge-on-Read behavior.
+
+### Apache Hudi
+
+Hudi implements Merge-on-Read as one of its two core table types (along with 
Copy-on-Write), offering a trade-off between read and write costs by 
maintaining base files alongside delta log files. Instead of rewriting columnar 
files for every update or delete, MoR tables maintain a combination of base 
files and log files that encode delta updates/deletes to the base file, 
enabling fast ingestion and deferred file merging via asynchronous 
[compaction](https://hudi.apache.org/docs/compactio [...]
+
+#### Storage Layout
+
+At the physical level, a Hudi MoR table stores data in [**File 
Groups**](https://hudi.apache.org/tech-specs/#file-layout-hierarchy), each 
uniquely identified by a `fileId`. A file group consists of:
+
+* Base File (`.parquet, .orc`): Stores the base snapshot of records in 
columnar format.  
+* Delta Log Files (`.log`): Append-only files that capture incremental 
updates, inserts, and deletes since the last compaction, in either row-oriented 
data formats like Apache Avro, Hudi’s native SSTable format or columnar-formats 
like Apache Parquet
+
+This hybrid design enables fast writes and defers expensive columnar file 
writing to asynchronous compaction.
+
+#### Write Path
+
+In a Merge-on-Read table, insert and update operations are handled differently 
to strike a balance between write efficiency and read performance.
+
+* Insert operations behave similarly to those in Copy-on-Write tables. New 
records are written to freshly created *base files*, aligned to a configured 
block size. In some cases, these inserts may be merged into the smallest 
existing base file in the partition to control file counts and sizes.
+
+* Update operations, however, are written to *log files* associated with the 
corresponding file group. These updates in the log files are written using 
Hudi’s 
[`HoodieAppendHandle`](https://github.com/apache/hudi/blob/45312d437a51ccd1d8c75ba0bd8af21a47dbb9e0/hudi-client/hudi-spark-client/src/main/java/org/apache/hudi/table/HoodieSparkMergeOnReadTable.java#L205)
 class. At runtime, a new instance of `HoodieAppendHandle` is created with the 
target *partition* and *file ID*. The update recor [...]
+
+```java
+HoodieAppendHandle appendHandle = new HoodieAppendHandle(config, instantTime, 
this,
+    partitionPath, fileId, recordMap.values().iterator(), taskContextSupplier, 
header);
+appendHandle.write(recordMap);
+List<WriteStatus> writeStatuses = appendHandle.close();
+return Collections.singletonList(writeStatuses).iterator();
+```
+
+* Delete operations are also appended to log files as either delete keys or 
deleted vector positions, to refer to the base file records that were deleted. 
These delete entries are not applied to the base files immediately. Instead, 
they are taken into account during snapshot reads, which merge the base and log 
files to produce the latest view, and during compaction, which merges the 
accumulated log files (including deletes) into new base files.
+
+This design ensures that write operations remain lightweight and fast, 
regardless of the size of the base files. Writers are not blocked by background 
compaction or cleanup operations, making the system well-suited for streaming 
and CDC workloads.
+
+#### Read Path
+
+Hudi MoR tables offer flexible read semantics by supporting both [snapshot 
queries](https://hudi.apache.org/docs/sql_queries/#snapshot-query) and 
[read-optimized queries](https://hudi.apache.org/docs/table_types#query-types), 
depending on the user's performance and freshness requirements.
+
+* Snapshot queries provide the most current view of the dataset by dynamically 
merging base files with their corresponding log files at read time. The system 
selects between different reader types based on the nature of the query and the 
presence of log files:  
+  * A **full-schema** reader reads the complete row data to ensure correct 
application of updates and deletes.  
+  * A **required-schema** reader projects only the needed columns to reduce 
I/O, while still applying log file merges.  
+  * A **skip-merging** reader is used when log files are absent for a file 
group, allowing the query engine to read directly from base files without 
incurring merge costs.
+
+* Read-optimized queries, in contrast, skip reading the delta log files 
altogether. These queries only scan the base Parquet files, providing faster 
response times at the cost of not reflecting the latest un-compacted changes. 
This mode is suitable for applications where slightly stale data is acceptable 
or where performance is critical.
+
+Together, these two read strategies allow Hudi MoR tables to serve both 
real-time and interactive queries from the same dataset, adjusting behavior 
depending on the workload and latency constraints.
+
+#### Compaction
+
+As log files accumulate new updates and deletes, Hudi triggers a compaction 
operation to merge these log files back into columnar base files. This process 
is [configurable and 
asynchronous](https://hudi.apache.org/docs/compaction#async--offline-compaction-models),
 and plays a key role in balancing write and read performance.
+
+Compaction in Hudi is triggered based on thresholds that can be configured by 
the user, such as the *number of commits (NUM\_COMMITS)*. During compaction, 
all log files associated with a file group are read and merged with the 
existing base file to produce a new compacted base file.
+
+### Apache Iceberg
+
+Apache Iceberg supports Merge-on-Read (MoR) semantics by maintaining immutable 
base data files and tracking updates and deletions through separate [*delete 
files*](https://iceberg.apache.org/spec/#delete-formats). This design avoids 
rewriting data files for every update or delete operation. Instead, these 
changes are applied at query time by merging delete files with the base files 
to produce an up-to-date view. 
+
+#### Storage Layout
+
+An Iceberg table consists of:
+
+* Base Data Files: Immutable Parquet, ORC, or Avro files that contain the 
primary data.  
+* Delete Files: Auxiliary files that record row-level deletions. 
+
+#### Write Path
+
+In Iceberg’s MoR tables, write operations implement row-level updates by 
encoding them as a delete of the old record and an insert of the new one. 
Rather than modifying existing Parquet base files directly, Iceberg maintains a 
clear separation between new data and logical deletes by introducing delete 
files alongside new data files.
+
+* Inserts behave in the same way as CoW tables. The new data is appended to 
the table as part of a new snapshot.  
+* For delete operations, Iceberg writes a delete file containing rows to be 
logically removed across multiple base files. Delete files are of two types:  
+  * Position Deletes: Reference row positions in a specific data file.  
+  * Equality Deletes: Encode a predicate that matches rows based on one or 
more column values.
+
+Equality deletes are typically not favored in performance sensitive data 
platforms, since it forces predicate evaluation against every single base file 
during snapshot reads. 
+
+The `DeleteFile` interface captures these semantics:
+
+```java
+public interface DeleteFile extends ContentFile<StructLike> {
+  enum DeleteType {
+    EQUALITY, POSITION
+  }
+}
+```
+
+**Note:** Iceberg v3 introduces Deletion Vectors as a more efficient 
alternative to positional deletes. Deletion vectors attach a *bitmap* to a data 
file to indicate deleted rows, allowing query engines to skip over deleted rows 
at read time. Deletion Vectors are already supported by Delta Lake and Hudi and 
this is now borrowed into the Iceberg spec as well.
+
+* For update operations, Iceberg uses a two-step process. An update is 
implemented as a delete \+ insert pattern. First, a delete file is created to 
logically remove the old record, using either a position or equality delete. 
Then, a new data file is written that contains the full image of the updated 
record. Both the delete file and the new data file are added in a single atomic 
commit, creating a new snapshot of the table. This behavior is implemented via 
the `RowDelta` interface:
+
+```java
+RowDelta rowDelta = table.newRowDelta()
+    .addDeletes(deleteFile)
+    .addRows(dataFile);
+rowDelta.commit();
+```
+
+All write operations, whether adding new data files or new delete files, 
produce a new snapshot in Iceberg’s timeline. This guarantees consistent 
isolation across readers and writers while avoiding any rewriting of immutable 
data files.
+
+#### Read Path
+
+During query execution, Iceberg performs a Merge-on-Read query by combining 
the immutable base data files with any relevant delete files to present a 
consistent and up-to-date view. Before reading, the scan planning logic 
identifies which delete files apply to each data file, ensuring that deletes 
are correctly associated with their targets.
+
+This planning step guarantees that any row marked for deletion through either 
position deletes or equality deletes is filtered out of the final results, 
while the original base files remain unchanged. The merging of base data with 
delete files is applied dynamically by the query engine, allowing Iceberg to 
preserve the immutable file structure and still deliver row-level updates.
+
+### Delta Lake
+
+Delta Lake supports Merge-on-Read semantics using [*Deletion Vectors 
(DVs)*](https://docs.delta.io/latest/delta-deletion-vectors.html), a feature 
that allows rows to be logically removed from a dataset without rewriting the 
base Parquet files. This enables efficient row-level delete   while preserving 
immutability of data files. For updates, Delta Lake encodes changes as a 
combination of DELETE and INSERT operations, i.e. the old row is marked as 
deleted, and a new row with updated value [...]
+
+#### Storage Layout
+
+In Delta Lake, the storage layout consists of:
+
+* Base Data Files: Immutable Parquet files that hold the core data  
+* Deletion Vectors: Structures that track rows that should be considered 
deleted during reads, instead of physically removing them from Parquet
+
+A deletion vector is described by a descriptor which captures its storage type 
(inline, on-disk, or UUID-based), its physical location or inline data, an 
offset if stored on disk, its size in bytes, and the cardinality (number of 
rows it marks as deleted).
+
+Small deletion vectors can be embedded directly into the Delta transaction log 
(inline), while larger ones are stored as separate files, with the UUIDs 
referencing them by a unique identifier. 
+
+#### Write Path
+
+When a DELETE, UPDATE, or MERGE operation is performed on a Delta table, Delta 
Lake does not rewrite the affected base Parquet files. Instead, it generates a 
deletion vector that identifies which rows are logically removed. These 
deletion vectors are built as compressed bitmap structures (using Roaring 
Bitmaps), which efficiently encode the positions of the deleted rows.
+
+Smaller deletion vectors are kept inline in the transaction log for quick 
lookup, while larger ones are persisted as separate deletion vector files. All 
write operations that affect rows in this way update the metadata to track the 
associated deletion vectors, maintaining a consistent and atomic snapshot view 
for downstream reads.
+
+#### Read Path
+
+During query execution, Delta Lake consults any deletion vectors attached to 
the current snapshot. The query execution loads these deletion vectors and 
applies them dynamically, filtering out rows marked as deleted before returning 
results to the user. This happens without rewriting or modifying the base 
Parquet files, preserving their immutability while still providing correct 
row-level semantics.
+
+This Merge-on-Read approach allows Delta Lake to combine efficient write 
operations with the ability to serve up-to-date views, ensuring that queries 
see a consistent, deletion-aware representation of the dataset.
+
+## Comparative Design Analysis
+
+Merge-on-Read semantics are implemented differently across open table formats, 
with each approach reflecting distinct trade-offs that influence workload 
performance, complexity, and operational flexibility. MoR is generally 
well-suited for high-throughput, low-latency streaming ingestion scenarios in a 
lakehouse, where frequent updates and late-arriving data are expected. In 
contrast, Copy-on-Write (CoW) tables often work best for simpler, 
batch-oriented workloads where updates are infre [...]
+
+In this section, we focus on Apache Hudi and Apache Iceberg table formats and 
explore how their MoR designs influence real-world workloads.
+
+### Streaming Data Support & Event-Time Ordering
+
+Hudi’s Merge-on-Read design supports event-time ordering and late-arriving 
data for streaming workloads by providing 
[`RecordPayload`](https://hudi.apache.org/docs/record_merger#record-payloads) 
and [`RecordMerger`](https://hudi.apache.org/docs/record_merger) APIs. These 
allow updates to be merged based on database sequence numbers or event 
timestamps, so that if data arrives out of order or has late arriving data, the 
final state is still correct from a temporal perspective. 
+
+<img src="/assets/images/blog/2025-07-21-mor-comparison/mor_fig1.png" 
alt="index" width="1000" align="middle"/>
+
+Iceberg uses a last-writer-wins approach, where the most recent commit 
determines record values regardless of event time. This design may be tricky to 
deal with late-arriving data  in streaming workloads or CDC ingestion. For e.g. 
if the source stream is ever repositioned to an earlier time, it will cause the 
table to move backwards in time where older record values from the replayed 
stream overwrite newer record images in the table.
+
+### Scalable Incremental Write Costs
+
+One of the main goals of MoR is to reduce write costs and latencies by 
avoiding full file rewrites. Hudi achieves this by appending changes to *delta 
logs* and using [**indexing**](https://hudi.apache.org/docs/indexes) to quickly 
identify which file group an incoming update belongs to. Hudi supports 
different index types to accelerate this lookup process, so it does not need to 
scan the entire table on every update. This ensures that even if you are 
updating a relatively small amount of  [...]
+
+<img src="/assets/images/blog/2025-07-21-mor-comparison/mor_fig2.png" 
alt="index" width="1000" align="middle"/>
+
+Iceberg handles row-level updates and deletes by recording them as [*delete 
files*](https://iceberg.apache.org/spec/#delete-formats). To identify which 
records to update or delete, Iceberg relies on scanning table metadata, and in 
some cases file-level data, to locate affected rows. This design uses a simple 
metadata approach but if partitioning is not highly selective, this lookup step 
can become a bottleneck for write performance on large tables with frequent 
small updates.
+
+### Asynchronous Compaction during ‘Merge’
+
+Hudi employs [optimistic concurrency 
control](https://hudi.apache.org/blog/2025/01/28/concurrency-control#occ-multi-writers)
 (OCC) between writers and maintains blocking-free [multi-version concurrency 
control](https://hudi.apache.org/blog/2025/01/28/concurrency-control#mvcc-writer-table-service-and-table-service-table-service)
 (MVCC) between writers and its asynchronous compaction process. This means 
writers can continue appending updates to the same records while earlier 
versions are b [...]
+
+<img src="/assets/images/blog/2025-07-21-mor-comparison/mor_fig3.png" 
alt="index" width="1000" align="middle"/>
+
+Iceberg maintains consistent snapshots across all operations, but it does not 
separate a dedicated compaction action from other write operations. As a 
result, if both a writer and a maintenance process try to modify overlapping 
data, standard snapshot conflict resolution ensures only one succeeds and might 
require retries in some concurrent write scenarios, but there is no 
asynchronous way to run compaction services. This could lead to livelocking 
between the writer and table maintenance [...]
+
+### Non-Blocking Concurrency Control (NBCC) for Real-time applications
+
+Hudi 1.0 further extends its concurrency model to allow multiple writers to 
safely update the same record at the same time with [non-blocking conflict 
resolution](https://hudi.apache.org/blog/2025/01/28/concurrency-control#non-blocking-concurrency-control-multi-writers).
 It supports serializability guarantees based on write completion timestamps 
(arrival-time processing), while also allowing record merging according to 
event-time order if required. This flexible concurrency strategy enab [...]
+
+<img src="/assets/images/blog/2025-07-21-mor-comparison/mor_fig4.png" 
alt="index" width="700" align="middle"/>
+
+Iceberg applies OCC through its snapshot approach, where writers commit 
updates against the latest known snapshot, and if conflicts are detected, 
retries are required. There is no explicit distinction between arrival-time and 
event-time semantics for concurrent record updates.
+
+### Minimizing Read Costs
+
+Hudi organizes records into *file groups,* ensuring that updates are 
consistently routed back to the same group where the original records were 
stored. This approach means that when a query is executed, it only needs to 
scan the base file and any delta log files within that specific file group, 
reducing the data that must be read and merged at query time. By tying updates 
and inserts to a consistent file group, Hudi preserves locality and limits 
merge complexity.
+
+<img src="/assets/images/blog/2025-07-21-mor-comparison/mor_fig5.png" 
alt="index" width="1000" align="middle"/>
+
+Iceberg applies updates and deletes using *delete files*, and these can 
reference any row in any base file. As a result, readers must examine all 
relevant delete files along with all associated base data files during scan 
planning and execution, which can increase I/O and metadata processing 
requirements for large tables.
+
+### Performant Read-Side Merge
+
+Hudi’s MoR implementation uses *key-based* merging to reconcile delta log 
records with base files, which allows query engines to push down filters and 
still correctly merge updates based on record keys. This selective merging 
reduces unnecessary I/O and improves performance for queries that only need a 
subset of columns or rows.
+
+<img src="/assets/images/blog/2025-07-21-mor-comparison/mor_fig6.png" 
alt="index" width="800" align="middle"/>
+
+Iceberg historically required readers (particularly Spark readers) to load 
entire base files when applying positional deletes. This was because pushing 
down filters could change the order or number of rows returned by the Parquet 
reader, making positional delete applications incorrect. As a result, filter 
pushdowns could not be safely applied, forcing a full file scan to maintain 
correctness. There has been ongoing work in the Iceberg community to address 
this limitation by improving how [...]
+
+### Efficient Compaction Planning
+
+Hudi’s compaction strategy operates at the level of individual file groups, 
which means it can plan and execute small, predictable units of compaction 
work. This fine-grained approach allows compaction to proceed *incrementally* 
and avoids large, unpredictable workloads. 
+
+<img src="/assets/images/blog/2025-07-21-mor-comparison/mor_fig7.png" 
alt="index" width="800" align="middle"/>
+
+In Iceberg, compaction must consider all base files and their related delete 
files together, because delete files reference rows in the base data files. 
This creates a dependency graph where all related files must be handled in a 
coordinated way. As delete files accumulate over time, these compaction 
operations can become increasingly large and complex to plan, making it harder 
to schedule resources efficiently. If compaction falls behind, the amount of 
data that must be compacted in fut [...]
+
+### Temporal and Spatial Locality for Event-Time Filters
+
+Hudi maintains temporal and spatial locality by ensuring that updates and 
deletes are routed back to the same file group where the original record was 
first stored. This preserves the time-based clustering or ordering of records, 
which is especially beneficial for queries filtering by event time or operating 
within specific time windows. By keeping related records together, Hudi enables 
efficient pruning of file groups along with partition pruning, during 
time-based queries.
+
+<img src="/assets/images/blog/2025-07-21-mor-comparison/mor_fig8.png" 
alt="index" width="1000" align="middle"/>
+
+Iceberg handles updates by deleting the existing record and inserting a new 
one, which may place the updated record in a different data file. Over time, 
this can scatter records that belong to the same logical or temporal group 
across multiple files, reducing the effectiveness of partition pruning and 
requiring periodic clustering or optimization to restore temporal locality.
+
+### Partial Updates for Performant Merge
+
+Hudi supports partial updates by encoding only the columns that have changed 
into its delta log files. This means the cost of merging updates is 
proportional to the number of columns actually modified, rather than the total 
width of the record. For columnar datasets with wide schemas, this can 
significantly reduce write amplification and improve merge performance.
+
+<img src="/assets/images/blog/2025-07-21-mor-comparison/mor_fig9.png" 
alt="index" width="800" align="middle"/>
+
+In Iceberg, updates are implemented as a delete plus a full-row insert, which 
requires rewriting the entire record even if only a single column has changed. 
As a result, update costs in Iceberg scale with the total number of columns in 
the record, increasing I/O and storage requirements for wide tables with 
frequent column-level updates.
+
+## Conclusion
+
+Merge-on-Read (MoR) table type provides an alternative approach to managing 
updates and deletes on immutable columnar files in a lakehouse. While multiple 
open table formats support MoR semantics, their design choices significantly 
affect suitability for real-time and change-data driven workloads.
+
+Apache Hudi’s MoR implementation specifically addresses the needs of 
high-ingestion, update-heavy pipelines. By appending changes to delta logs, 
preserving file-group-based data locality, supporting event-time ordering, and 
enabling asynchronous, non-blocking compaction, Hudi minimizes write 
amplification and supports low-latency data availability. These design 
primitives directly align with streaming and CDC patterns, where data arrives 
frequently and potentially out of order. Iceberg a [...]
+
+---
\ No newline at end of file
diff --git 
a/website/static/assets/images/blog/2025-07-21-mor-comparison/mor-1200x600.jpg 
b/website/static/assets/images/blog/2025-07-21-mor-comparison/mor-1200x600.jpg
new file mode 100644
index 000000000000..e55f5ad2e9b7
Binary files /dev/null and 
b/website/static/assets/images/blog/2025-07-21-mor-comparison/mor-1200x600.jpg 
differ
diff --git 
a/website/static/assets/images/blog/2025-07-21-mor-comparison/mor_fig1.png 
b/website/static/assets/images/blog/2025-07-21-mor-comparison/mor_fig1.png
new file mode 100644
index 000000000000..1aecf62ee69d
Binary files /dev/null and 
b/website/static/assets/images/blog/2025-07-21-mor-comparison/mor_fig1.png 
differ
diff --git 
a/website/static/assets/images/blog/2025-07-21-mor-comparison/mor_fig2.png 
b/website/static/assets/images/blog/2025-07-21-mor-comparison/mor_fig2.png
new file mode 100644
index 000000000000..fd7b306f78ba
Binary files /dev/null and 
b/website/static/assets/images/blog/2025-07-21-mor-comparison/mor_fig2.png 
differ
diff --git 
a/website/static/assets/images/blog/2025-07-21-mor-comparison/mor_fig3.png 
b/website/static/assets/images/blog/2025-07-21-mor-comparison/mor_fig3.png
new file mode 100644
index 000000000000..31a266e02440
Binary files /dev/null and 
b/website/static/assets/images/blog/2025-07-21-mor-comparison/mor_fig3.png 
differ
diff --git 
a/website/static/assets/images/blog/2025-07-21-mor-comparison/mor_fig4.png 
b/website/static/assets/images/blog/2025-07-21-mor-comparison/mor_fig4.png
new file mode 100644
index 000000000000..e1d05d53a004
Binary files /dev/null and 
b/website/static/assets/images/blog/2025-07-21-mor-comparison/mor_fig4.png 
differ
diff --git 
a/website/static/assets/images/blog/2025-07-21-mor-comparison/mor_fig5.png 
b/website/static/assets/images/blog/2025-07-21-mor-comparison/mor_fig5.png
new file mode 100644
index 000000000000..6bb11f516c23
Binary files /dev/null and 
b/website/static/assets/images/blog/2025-07-21-mor-comparison/mor_fig5.png 
differ
diff --git 
a/website/static/assets/images/blog/2025-07-21-mor-comparison/mor_fig6.png 
b/website/static/assets/images/blog/2025-07-21-mor-comparison/mor_fig6.png
new file mode 100644
index 000000000000..b9aa45d11b82
Binary files /dev/null and 
b/website/static/assets/images/blog/2025-07-21-mor-comparison/mor_fig6.png 
differ
diff --git 
a/website/static/assets/images/blog/2025-07-21-mor-comparison/mor_fig7.png 
b/website/static/assets/images/blog/2025-07-21-mor-comparison/mor_fig7.png
new file mode 100644
index 000000000000..1a3b5c4c1e96
Binary files /dev/null and 
b/website/static/assets/images/blog/2025-07-21-mor-comparison/mor_fig7.png 
differ
diff --git 
a/website/static/assets/images/blog/2025-07-21-mor-comparison/mor_fig8.png 
b/website/static/assets/images/blog/2025-07-21-mor-comparison/mor_fig8.png
new file mode 100644
index 000000000000..a6abadd7990f
Binary files /dev/null and 
b/website/static/assets/images/blog/2025-07-21-mor-comparison/mor_fig8.png 
differ
diff --git 
a/website/static/assets/images/blog/2025-07-21-mor-comparison/mor_fig9.png 
b/website/static/assets/images/blog/2025-07-21-mor-comparison/mor_fig9.png
new file mode 100644
index 000000000000..d275e07416a3
Binary files /dev/null and 
b/website/static/assets/images/blog/2025-07-21-mor-comparison/mor_fig9.png 
differ

Reply via email to