This is an automated email from the ASF dual-hosted git repository.
yihua 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 72cdaf063863 docs(blog): add the hudi-rs 0.6.0 release announcement
(#20510)
72cdaf063863 is described below
commit 72cdaf0638635c879070dc4fcf064aa113054cbe
Author: Y Ethan Guo <[email protected]>
AuthorDate: Tue Nov 3 12:17:22 2026 -0800
docs(blog): add the hudi-rs 0.6.0 release announcement (#20510)
---
.github/scripts/validate-blog.py | 2 +-
...3-apache-hudi-rs-0-6-0-release-announcement.mdx | 151 +++++++++++++++++++++
.../1-cover.png | Bin 0 -> 460478 bytes
.../2-write-path.png | Bin 0 -> 206925 bytes
.../3-write-latency.png | Bin 0 -> 192218 bytes
.../4-write-bytes.png | Bin 0 -> 178228 bytes
.../5-python-reads.png | Bin 0 -> 142316 bytes
.../6-python-lookups.png | Bin 0 -> 175752 bytes
8 files changed, 152 insertions(+), 1 deletion(-)
diff --git a/.github/scripts/validate-blog.py b/.github/scripts/validate-blog.py
index d93ddb2bebf6..ea8214de3e4c 100644
--- a/.github/scripts/validate-blog.py
+++ b/.github/scripts/validate-blog.py
@@ -25,7 +25,7 @@ ALLOWED_TAGS = {
'streaming', 'querying', 'comparison', 'hudi streamer', 'apache flink',
'dml', 'concurrency control', 'cdc',
'bi', 'uber', 'tutorial', 'release', 'cow', 'apache kafka', 'minio',
- 'clustering', 'acid', 'walmart', 'python', 'mor', 'hudi timeline',
+ 'clustering', 'acid', 'walmart', 'python', 'rust', 'mor', 'hudi timeline',
'debezium', 'data skipping', 'daft', 'ai',
'table format', 'starrocks', 'halodoc', 'gdpr', 'schema', 'scd',
'observability', 'metadata', 'meetup', 'key generation', 'docker',
diff --git
a/website/blog/2026-11-03-apache-hudi-rs-0-6-0-release-announcement.mdx
b/website/blog/2026-11-03-apache-hudi-rs-0-6-0-release-announcement.mdx
new file mode 100644
index 000000000000..cbb2bee7ff6b
--- /dev/null
+++ b/website/blog/2026-11-03-apache-hudi-rs-0-6-0-release-announcement.mdx
@@ -0,0 +1,151 @@
+---
+title: "Apache Hudi-rs 0.6.0: Native Writes from Rust and Python"
+excerpt: "hudi-rs 0.6.0 adds native write support: create, append, upsert,
update, delete and overwrite Hudi tables from Rust and Python with no JVM, with
sub-second upserts and deletes backed by the record-level index. Keep a RAG
knowledge base or a training set current from one Python process, read from
Python up to 6 times faster, and upgrade to DataFusion 55 and Arrow 59."
+authors: [ethan-guo]
+category: community
+image:
/assets/images/blog/2026-11-03-apache-hudi-rs-0-6-0-release-announcement/1-cover.png
+tags:
+- release
+- python
+- rust
+- lakehouse
+- write
+- upsert
+- record level index
+---
+
+The Apache Hudi community is excited to announce the release of [hudi-rs
0.6.0](https://github.com/apache/hudi-rs/releases/tag/release-0.6.0), the
native Rust implementation of [Apache Hudi](/) with
[Python](https://pypi.org/project/hudi/0.6.0/) and
[C++](https://github.com/apache/hudi-rs/tree/release-0.6.0/cpp) bindings. Until
now, hudi-rs has been a reader: a fast, JVM-free way to query Hudi tables from
Rust, Python,
[DataFusion](https://github.com/apache/hudi-rs/blob/release-0.6.0/REA [...]
+
+Many of today's pipelines that update tables run in a plain Python process or
in a non-JVM engine. A retrieval pipeline embeds documents and has to keep its
knowledge base current as sources are added, edited and removed. A model team
curates a fine-tuning set between training runs, correcting and rejecting
examples, and has to reproduce the exact version each model was trained on. A
service or an agent corrects records in a dataset as it finds errors. Each of
them changes a small slice [...]
+
+The release carries [close to 80
commits](https://github.com/apache/hudi-rs/releases/tag/release-0.6.0) from 11
contributors, and alongside the write path the release makes reads from Python
noticeably faster, upgrades the query engine integration to DataFusion 55 and
Arrow 59, and enhances read compatibility and bindings. Let's dive into the
highlights.
+
+<div style={{ textAlign: 'center' }}>
+ <img
src="/assets/images/blog/2026-11-03-apache-hudi-rs-0-6-0-release-announcement/1-cover.png"
alt="hudi-rs 0.6.0: write Hudi tables natively from Python and Rust, query
them with Spark, Flink, Trino, Presto, DataFusion, Daft, Ray and DuckDB"
width="1000"/>
+</div>
+
+## Write Hudi Tables Without a JVM
+
+You can now build and maintain a Hudi table from a notebook, a script, a
service or an agent, without depending on Spark or a JVM-based engine. hudi-rs
0.6.0 adds [native, single-node write
support](https://github.com/apache/hudi-rs/blob/release-0.6.0/README.md#writing-tables)
covering create, append, upsert, update, delete, overwrite and dynamic
partition overwrite operations, on both [Copy-on-Write and
Merge-on-Read](/docs/table_types) tables.
[Upserts](https://github.com/apache/hudi-r [...]
+
+Spark, Flink, Trino, Presto and the other [query engines supporting
Hudi](/docs/querying_data) can all read tables written by hudi-rs, with the
metadata table and indexes in place. hudi-rs 0.6.0 serializes writers within a
process, so hand a table between engines instead of writing to the same table
from two engines at once. A table can start on a MacBook and move to a cluster
when the table outgrows one machine: Spark takes the table over and runs
ingestion, [compaction](/docs/compactio [...]
+
+The [example
below](/docs/python-rust-quick-start-guide#write-a-table-in-python) is the
sequence of writes behind a retrieval or training pipeline: a table of document
chunks with their [embeddings](/docs/use_cases#ai-native-data-lakehouse), keyed
by `chunk_id`. The example ingests three documents, re-embeds the one that
changed, drops the one that was removed, and then reads back only what changed
since the initial load. The write methods live on `HudiTable` and take Arrow
data in the f [...]
+
+```python
+import numpy as np
+import pyarrow as pa
+from hudi import HudiQueryType, HudiReadOptions, HudiTable
+
+def chunks(doc_ids, version):
+ rows = [(f"{d}#{i}", d, f"chunk {i} of {d}, version {version}")
+ for d in doc_ids for i in range(4)]
+ rng = np.random.default_rng(version)
+ embeddings = rng.random((len(rows), 384), dtype=np.float32)
+ return pa.table({
+ "chunk_id": [r[0] for r in rows],
+ "doc_id": [r[1] for r in rows],
+ "text": [r[2] for r in rows],
+ "embedding": pa.array(embeddings.tolist(), pa.list_(pa.float32())),
+ "updated_at": pa.array([version] * len(rows), pa.int64()),
+ })
+
+table = HudiTable.create(
+ "/tmp/contract_chunks",
+ record_key="chunk_id", # the record-level index key
+ table_type="mor", # upserts and deletes append to log files
+ ordering_fields="updated_at", # later version wins on a repeated key
+)
+base = table.append(chunks(["doc1", "doc2", "doc3"], version=1))
+
+# doc2 changed: re-embed its chunks and upsert them. The index finds the
+# file groups holding these keys; nothing else in the table is read.
+result = table.upsert(chunks(["doc2"], version=2))
+print(result.num_updates, result.num_inserts) # 4 0
+
+# doc3 was removed: delete its chunks.
+table.delete("doc_id = 'doc3'")
+
+# Re-index or retrain on what changed since the initial load,
+# not the whole table.
+changed = table.read(
+ HudiReadOptions()
+ .with_query_type(HudiQueryType.Incremental)
+ .with_start_timestamp(base.completion_time)
+)
+print(pa.Table.from_batches(changed).num_rows) # 4, doc2's chunks
+```
+
+Every write returns the commit's completion time, which is the bound an
[incremental
read](https://github.com/apache/hudi-rs/blob/release-0.6.0/README.md#incremental-query)
takes, so a pipeline writes and then processes only the rows that changed. For
a knowledge base that means re-embedding and re-indexing only the chunks that
changed. For a training set the next run sees only the corrected and newly
added examples, and a [time-travel
read](https://github.com/apache/hudi-rs/blob/release [...]
+
+<div style={{ textAlign: 'center' }}>
+ <img
src="/assets/images/blog/2026-11-03-apache-hudi-rs-0-6-0-release-announcement/2-write-path.png"
alt="Documents and training examples change; one Python process appends,
upserts and deletes them in a Hudi table, where the record-level index locates
the file groups and each write is one commit on a Copy-on-Write or
Merge-on-Read table; downstream, incremental reads re-index only what changed,
time travel reproduces a training run, snapshot reads return Arrow data for
DuckDB, Polars [...]
+</div>
+
+On a [Merge-on-Read](/docs/table_types#merge-on-read-table) table, upserts,
updates and deletes append to log files instead of rewriting base files, which
keeps write amplification low, and 0.6.0 adds
[compaction](/docs/python-rust-quick-start-guide#compact-a-merge-on-read-table)
to merge the log files into right-sized base files and reduce read cost.
Compaction runs on demand, inline after a configurable number of delta commits,
or asynchronously on a background task, and the metadata t [...]
+
+### Upsert and Delete Benchmarks
+
+We curate and benchmark five distinct and representative workloads involving
upserts and deletes on hudi-rs, with a [reproducible
benchmark](/docs/python-rust-quick-start-guide#use-cases-and-numbers). Each
workload is a bulk load followed by five upsert commits and five delete commits:
+
+| Scenario | Models | Table | Each commit |
+|---|---|---|---|
+| `rag-chunks` | Re-syncing a
[RAG](https://en.wikipedia.org/wiki/Retrieval-augmented_generation) knowledge
base as its sources change | 1M synthetic chunks with 384-float embeddings from
100,000 documents, keyed by `chunk_id` | re-ingest 1,000 changed documents
(10,000 chunks), delete 100 removed documents |
+| `training-set` | Curating a fine-tuning set between training runs | 1M
synthetic examples keyed by `example_id` | upsert 10,000 corrected examples,
delete 1,000 rejected ones, then an [incremental
read](https://github.com/apache/hudi-rs/blob/release-0.6.0/README.md#incremental-query)
of the changes and a [time-travel
read](https://github.com/apache/hudi-rs/blob/release-0.6.0/README.md#time-travel-query)
of the first version |
+| `uuid` | Random updates to a dimension table, with changed keys spread
across every file | 10M synthetic rows with random UUID keys | upsert 10,000
existing keys plus 1,000 new, delete 1,000 |
+| `nyc-taxi` | Correcting a public dataset in place | [NYC TLC yellow taxi
trips](https://www.nyc.gov/site/tlc/about/tlc-trip-record-data.page), January
2022, 2.5M rows | upsert 1% of trips with corrected fares, delete 0.1% |
+| `tpcds-web-returns` | A standard warehouse fact table |
[TPC-DS](https://www.tpc.org/tpcds/) `web_returns` at scale factor 10, 0.7M
rows, composite key | upsert 1% matched plus 10% new rows, delete 5% |
+
+The same workloads, run through
[delta-rs](https://github.com/delta-io/delta-rs) and
[PyIceberg](https://github.com/apache/iceberg-python) with each library's own
Python API and default settings, give the numbers context: hudi-rs upserts are
1.2 to 12 times faster than the faster of the two, and deletes are 4.8 to 24
times faster, except on `tpcds-web-returns`, where hudi-rs and delta-rs are
comparable.
+
+<div style={{ textAlign: 'center' }}>
+ <img
src="/assets/images/blog/2026-11-03-apache-hudi-rs-0-6-0-release-announcement/3-write-latency.png"
alt="Seconds per upsert and delete commit on five workloads: hudi-rs 0.6.0
Merge-on-Read with the record-level index against delta-rs and PyIceberg, with
hudi-rs 1.2 to 12 times faster on upserts and 4.8 to 24 times faster on
deletes, except tpcds-web-returns, where hudi-rs and delta-rs are comparable"
width="1000"/>
+</div>
+
+The upserts are fast because of the low write amplification and record-level
index. A commit on a Merge-on-Read table appends only the changed records to
log files, and the record-level index points each key at its file group, so an
upsert commit in hudi-rs writes 4 to 200 times fewer bytes than the same commit
in the other two libraries. Neither delta-rs nor PyIceberg writes deletion
vectors in their latest releases ([delta-rs
1.6.6](https://github.com/delta-io/delta-rs/releases/tag/pyt [...]
+
+<div style={{ textAlign: 'center' }}>
+ <img
src="/assets/images/blog/2026-11-03-apache-hudi-rs-0-6-0-release-announcement/4-write-bytes.png"
alt="MiB written per upsert and delete commit: hudi-rs writes 4 to 200 times
fewer bytes per upsert commit than delta-rs and PyIceberg" width="1000"/>
+</div>
+
+## Faster Reads from Python
+
+Reads from Python are faster with no code changes. A full-table read now
decodes file slices on all available cores instead of on the calling Python
thread, which makes full-table reads through the Python package 3.8 to 6 times
faster on local Copy-on-Write tables of 1M to 10M rows, for example 2.11 s down
to 0.35 s for 10M rows, each read timed in a fresh process on a 14-core MacBook
Pro.
+
+<div style={{ textAlign: 'center' }}>
+ <img
src="/assets/images/blog/2026-11-03-apache-hudi-rs-0-6-0-release-announcement/5-python-reads.png"
alt="Full-table read time through Python for hudi 0.5.0 and 0.6.0 on
Copy-on-Write tables of 1M, 5M and 10M rows: 0.22 s to 0.05 s, 1.11 s to 0.29 s
and 2.11 s to 0.35 s, 3.8 to 6 times faster" width="800"/>
+</div>
+
+Filtered reads get faster as well. [Read
filters](https://github.com/apache/hudi-rs/blob/release-0.6.0/README.md#read-configs)
are evaluated inside the Parquet reader, so a query that keeps a few rows
decodes only those rows' remaining columns. A point lookup by record key,
whether `key = 'x'` or `key IN (...)`, uses the record-level index to open only
the files that hold those keys, and `IN` and `NOT IN` filters on other columns
skip files using column stats, so a point lookup for one r [...]
+
+<div style={{ textAlign: 'center' }}>
+ <img
src="/assets/images/blog/2026-11-03-apache-hudi-rs-0-6-0-release-announcement/6-python-lookups.png"
alt="Seconds per single-key point lookup through Python on Copy-on-Write
tables of 1M, 5M and 10M rows: hudi 0.6.0 uses the record-level index and is 9
to 24 times faster than hudi 0.5.0, which scans the table" width="1000"/>
+</div>
+
+## DataFusion 55 and Arrow 59
+
+This release upgrades to [DataFusion](https://datafusion.apache.org/) 55,
[Arrow and Parquet](https://github.com/apache/arrow-rs) 59 and
[pyo3](https://pyo3.rs/) 0.29, so hudi-rs fits into projects on newer Arrow and
DataFusion versions.
+
+| | [DataFusion](https://crates.io/crates/datafusion) |
[Arrow](https://crates.io/crates/arrow) and
[Parquet](https://crates.io/crates/parquet) |
[pyo3](https://crates.io/crates/pyo3) |
[RocksDB](https://crates.io/crates/rocksdb) (for disk spilling) |
+|---|---|---|---|---|
+| hudi-rs 0.5.0 | 54.1 | 58 | 0.28 | 0.22, bundling RocksDB 8.10 |
+| hudi-rs 0.6.0 | 55.2 | 59.2 | 0.29 | 0.24, bundling RocksDB 10.4 |
+
+## Reader and Binding Enhancements
+
+Read compatibility with tables written by Hudi on Spark and Flink is broader,
with support for more types and formats: Avro local-timestamp types, Avro enum
columns, the older version 2 Avro data log blocks that writers before table
version 6 left behind, and Merge-on-Read log records whose timestamp precision
differs from the table schema, which are now reconciled on read the same way
the Java reader does. A read of a Spark-written table that failed on one of
these in 0.5.0 works in 0.6 [...]
+
+For engines using hudi-rs, the
[`hudi-ffi`](https://github.com/apache/hudi-rs/tree/release-0.6.0/crates/ffi)
crate exposes a plain C ABI over the Arrow C Data Interface for JVM and C++
callers. The [C++
binding](https://github.com/apache/hudi-rs/tree/release-0.6.0/cpp) reports
internal panics as ordinary errors, so one failed read no longer takes a
long-running host process down, and Python applications can catch
`HudiCoreError` like any other exception. The Rust API reference is on [doc
[...]
+
+## Thank You
+
+A huge thank you to everyone who contributed, reviewed and tested this
release, and a warm welcome to the first-time contributors:
[@alitrack](https://github.com/alitrack),
[@GayathriSrividya](https://github.com/GayathriSrividya),
[@morningstarxcdcode](https://github.com/morningstarxcdcode),
[@oglego](https://github.com/oglego), [@vinayus](https://github.com/vinayus),
[@voonhous](https://github.com/voonhous),
[@wirybeaver](https://github.com/wirybeaver) and [@vinothchandar](https://githu
[...]
+
+## Get Started
+
+```bash
+pip install hudi==0.6.0
+cargo add [email protected]
+```
+
+Start with the [Python and Rust quick
start](/docs/python-rust-quick-start-guide), which covers
[writing](/docs/python-rust-quick-start-guide#write-a-table-in-python),
[compaction](/docs/python-rust-quick-start-guide#compact-a-merge-on-read-table),
[reads](/docs/python-rust-quick-start-guide#read-a-table), [querying with
DuckDB, Polars and
DataFusion](/docs/python-rust-quick-start-guide#query-with-duckdb-polars-and-datafusion),
and [cloud storage](/docs/python-rust-quick-start-guide#work [...]
diff --git
a/website/static/assets/images/blog/2026-11-03-apache-hudi-rs-0-6-0-release-announcement/1-cover.png
b/website/static/assets/images/blog/2026-11-03-apache-hudi-rs-0-6-0-release-announcement/1-cover.png
new file mode 100644
index 000000000000..b0e3351e9515
Binary files /dev/null and
b/website/static/assets/images/blog/2026-11-03-apache-hudi-rs-0-6-0-release-announcement/1-cover.png
differ
diff --git
a/website/static/assets/images/blog/2026-11-03-apache-hudi-rs-0-6-0-release-announcement/2-write-path.png
b/website/static/assets/images/blog/2026-11-03-apache-hudi-rs-0-6-0-release-announcement/2-write-path.png
new file mode 100644
index 000000000000..7bc748ea7352
Binary files /dev/null and
b/website/static/assets/images/blog/2026-11-03-apache-hudi-rs-0-6-0-release-announcement/2-write-path.png
differ
diff --git
a/website/static/assets/images/blog/2026-11-03-apache-hudi-rs-0-6-0-release-announcement/3-write-latency.png
b/website/static/assets/images/blog/2026-11-03-apache-hudi-rs-0-6-0-release-announcement/3-write-latency.png
new file mode 100644
index 000000000000..c20f765e4a36
Binary files /dev/null and
b/website/static/assets/images/blog/2026-11-03-apache-hudi-rs-0-6-0-release-announcement/3-write-latency.png
differ
diff --git
a/website/static/assets/images/blog/2026-11-03-apache-hudi-rs-0-6-0-release-announcement/4-write-bytes.png
b/website/static/assets/images/blog/2026-11-03-apache-hudi-rs-0-6-0-release-announcement/4-write-bytes.png
new file mode 100644
index 000000000000..1a73737a69a3
Binary files /dev/null and
b/website/static/assets/images/blog/2026-11-03-apache-hudi-rs-0-6-0-release-announcement/4-write-bytes.png
differ
diff --git
a/website/static/assets/images/blog/2026-11-03-apache-hudi-rs-0-6-0-release-announcement/5-python-reads.png
b/website/static/assets/images/blog/2026-11-03-apache-hudi-rs-0-6-0-release-announcement/5-python-reads.png
new file mode 100644
index 000000000000..b7b7745f6c22
Binary files /dev/null and
b/website/static/assets/images/blog/2026-11-03-apache-hudi-rs-0-6-0-release-announcement/5-python-reads.png
differ
diff --git
a/website/static/assets/images/blog/2026-11-03-apache-hudi-rs-0-6-0-release-announcement/6-python-lookups.png
b/website/static/assets/images/blog/2026-11-03-apache-hudi-rs-0-6-0-release-announcement/6-python-lookups.png
new file mode 100644
index 000000000000..4220a804da88
Binary files /dev/null and
b/website/static/assets/images/blog/2026-11-03-apache-hudi-rs-0-6-0-release-announcement/6-python-lookups.png
differ