Please check the build logs for more information.
See Builds for ideas on how to fix a failed build, or Metadata for how to configure docs.rs builds.
If you believe this is docs.rs' fault, open an issue.
The Hudi-rs project aims to standardize the core Apache Hudi APIs, and broaden the Hudi integration in the data ecosystems for a diverse range of users and projects.
| Source | Downloads | Installation Command |
|---|---|---|
| PyPi.org | pip install hudi |
|
| Crates.io | cargo add hudi |
The hudi crate carries two features: datafusion (off by default, see
Apache DataFusion) and spill-rocksdb (on by default), the merge map's
on-disk tier, which a merge-on-read merge spills to when a file group's log records exceed
hoodie.memory.merge.max.size. RocksDB is built from source with bindgen, so it needs libclang
and a C++ toolchain; default-features = false drops it, and a merge that would have spilled then
fails instead.
Usage Examples
[!NOTE] These examples expect a Hudi table exists at
/tmp/trips_table, created using the quick start guide.
For the full reader API reference (ReadOptions, filter expressions, behavioral guarantees), see docs/reader-spec.md.
Snapshot Query
Snapshot query reads the latest version of the data from the table. The table API also accepts column filters that drive partition + file pruning and row-level filtering.
Python
=
=
# convert to PyArrow table
=
=
Rust
use Result;
use ReadOptions;
use TableBuilder as HudiTableBuilder;
use concat_batches;
async
To run read-optimized (RO) query on Merge-on-Read (MOR) tables, set hoodie.read.use.read_optimized.mode in ReadOptions.
Python
=
Rust
let options = new
.with_hudi_option;
let batches = hudi_table.read.await?;
Time-Travel Query
Time-travel query reads the data at a specific timestamp from the table. The table API also accepts column filters that drive partition + file pruning and row-level filtering.
Python
=
Rust
let options = new
.with_as_of_timestamp
.with_filters?;
let batches = hudi_table.read.await?;
The supported formats for the timestamp argument are:
- Hudi Timeline format (highest matching precedence):
yyyyMMddHHmmssSSSoryyyyMMddHHmmss. - Unix epoch time in seconds, milliseconds, microseconds, or nanoseconds.
- RFC 3339 / ISO 8601 with timezone offset, including:
yyyy-MM-dd'T'HH:mm:ss.SSS+00:00yyyy-MM-dd'T'HH:mm:ss.SSSZyyyy-MM-dd'T'HH:mm:ss+00:00yyyy-MM-dd'T'HH:mm:ssZ
Timestamp strings without a timezone offset (for example yyyy-MM-dd'T'HH:mm:ss) and date-only strings (for example yyyy-MM-dd) are not accepted.
Incremental Query
Incremental query reads the changed data from the table for a given time range.
Python
# read the records between t1 (exclusive) and t2 (inclusive)
=
# read the records after t1 (end defaults to the latest commit)
=
# with column filters applied to the changed records
=
Rust
use QueryType;
// read the records between t1 (exclusive) and t2 (inclusive)
let options = new
.with_query_type
.with_start_timestamp
.with_end_timestamp;
let batches = hudi_table.read.await?;
// read the records after t1 (end defaults to the latest commit)
let options = new
.with_query_type
.with_start_timestamp;
let batches = hudi_table.read.await?;
// with column filters applied to the changed records
let options = new
.with_query_type
.with_start_timestamp
.with_end_timestamp
.with_filters?;
let batches = hudi_table.read.await?;
Incremental queries support the same timestamp formats as time-travel queries.
Streaming Read
Streaming reads yield RecordBatches one at a time without loading the full result into memory.
The same ReadOptions knobs apply, plus batch_size and projection.
Python
=
Rust
use StreamExt;
let options = new
.with_filters?
.with_projection
.with_batch_size?;
let mut stream = hudi_table.read_stream.await?;
while let Some = stream.next.await
File Group Reading (Experimental)
File group reading allows you to read data from a specific file slice. This is useful when integrating with query engines, where the plan provides file paths.
Python
=
# Returns a PyArrow RecordBatch
=
Rust
use FileGroupReader;
use ReadOptions;
// Inside an async context
let reader = new_with_options.await?;
// Returns an Arrow RecordBatch
let record_batch = reader
.read_file_slice_from_paths
.await?;
C++
// Functions may throw rust::Error on failure
auto reader = ;
// Returns an ArrowArrayStream pointer
std::vector<std::string> ;
ArrowArrayStream* stream_ptr = reader->;
Query Engine Integration
Hudi-rs provides APIs to support integration with query engines. The sections below highlight some commonly used APIs.
Table API
Create a Hudi table instance using its constructor or the TableBuilder API.
All read APIs accept a ReadOptions (Rust) / HudiReadOptions (Python) value. It stores three fields — filters, projection, and hudi_options — and exposes chainable with_* builders for the rest. The available knobs:
query_type(with_query_type) —Snapshot(default) orIncremental. Drives dispatch inread,read_stream, andget_file_slices.filters— column filters as(field, op, value)tuples. The field can be any column (partition or data). Used for partition pruning, file-level stats pruning (snapshot only), and row-level filtering.projection— columns to return. Streaming pushes the projection down to the parquet reader; eager reads project after merging.batch_size(with_batch_size) — rows per batch (streaming only; eager reads return one batch per file slice).as_of_timestamp(with_as_of_timestamp) — snapshot/time-travel timestamp (defaults to latest commit).start_timestamp/end_timestamp(with_start_timestamp/with_end_timestamp) — incremental range (defaults to earliest…latest).hudi_options— Hudi configs for this read (e.g.hoodie.read.use.read_optimized.mode). A config that selects which read to perform —hoodie.read.query.typeand the as-of/start/end timestamps — is per-read only and is dropped when set on the table. The rest describe how to read: set them on the table and override them here. See Read configs.
| Stage | API | Description |
|---|---|---|
| Query planning | get_file_slices(options) |
Get the file slices the read targets, dispatched on options.query_type. To bucket for parallel reads, call hudi::util::collection::split_into_chunks on the result. |
compute_table_stats(options) |
Estimated (num_rows, byte_size) for scan planning, derived from the metadata table. Snapshot only. Returns None for incremental queries, and whenever the estimate cannot be computed (no metadata table, non-Parquet base files, footer sampling failure). |
|
| Query execution | create_file_group_reader_with_options(read_options, extra_storage_overrides) |
Create a file group reader with the table's configs. In Python both args are optional; in Rust read_options is an Option and extra_storage_overrides takes a (possibly empty) iterator. Timestamps are resolved automatically (e.g. AsOfTimestamp → EndTimestamp), so callers can pass the same options used for get_file_slices. |
read(options) / read_stream(options) |
Record-read APIs. Dispatch on options.query_type. read_stream errors on Incremental for now. Per-slice streaming lives on FileGroupReader. |
Read configs
Read configs reach a read either through ReadOptions / HudiReadOptions or, for those scoped
table or read, through the table (TableBuilder, hoodie.properties, hudi-defaults.conf), where
a per-read value wins. A per read config set on the table is dropped: baked in there, it would
silently redirect every later read.
| Config | Default | Scope | Notes |
|---|---|---|---|
hoodie.read.query.type |
snapshot |
per read | snapshot or incremental. |
hoodie.read.as.of.timestamp |
latest commit | per read | Snapshot time-travel point. |
hoodie.read.start.timestamp / hoodie.read.end.timestamp |
earliest / latest | per read | Incremental window, half-open (start, end]. |
hoodie.read.file.group.reader.version |
2 |
table or read | Which file group reader merges a slice. Version 2 is the default; a read it cannot serve falls back to version 1. |
hoodie.read.use.read_optimized.mode |
false |
table or read | Read base files only, skipping the log files, on MOR tables. |
hoodie.read.stream.batch_size |
1024 |
table or read | Rows per batch for streaming reads. |
hoodie.read.file.slice.read.concurrency |
4 |
table or read | File slices read concurrently. |
hoodie.read.scan.max.memory.size |
unset | table or read | Total bytes a whole scan may use for concurrent slice reads; when set, the concurrency is derived from it. Reaching the limit lowers throughput, it never fails the read. |
hoodie.read.input.partitions |
0 |
table or read | How many partitions the DataFusion table provider buckets the file slices into. 0 defers to DataFusion's target_partitions. |
hoodie.merge.use.record.positions |
false |
table or read | Match a log record to the base row it updates by position rather than by record key. Honored by reader version 2 only; a log block written without positions is merged by key regardless. |
A table whose hoodie.record.merge.mode is CUSTOM needs a merger for its payload class. When
neither reader has one, the read fails rather than returning wrong rows; set
hoodie.read.file.group.reader.version=1 to read it the way it was read before, unless its base
files are HFile, which version 1 cannot read.
Base files are read from Parquet, Lance (hoodie.table.base.file.format=lance), and HFile (metadata
tables only).
File Group API
Create a Hudi file group reader instance using its constructor or the Hudi table API create_file_group_reader_with_options().
| Stage | API | Description |
|---|---|---|
| Query execution | read_file_slice() |
Read records from a given file slice; based on the configs, read records from only base file, or from base file and log files, and merge records based on the configured strategy. |
read_file_slice_from_paths() |
Read records from an explicit base file path and a list of log file paths. Pass an empty log path list to read just the base file. | |
read_file_slice_stream() |
Streaming version of read_file_slice(). A base-file-only or read-optimized slice streams straight from the base file. A MOR slice with log files streams merged chunks under file group reader version 2 (the default), and collapses to a single merged batch under version 1, whose merge has no incremental form. |
|
read_file_slice_from_paths_stream() |
Streaming version of read_file_slice_from_paths(). |
Apache DataFusion
Enabling the hudi crate with datafusion feature will provide a DataFusion
extension to query Hudi tables.
cargo new my_project --bin && cd my_project
cargo add tokio@1 datafusion@54
cargo add hudi --features datafusion
Update src/main.rs with the code snippet below then cargo run.
pip install hudi[datafusion]
Rust
use Arc;
use Result;
use ;
use HudiDataSource;
async
Python
=
=
Other Integrations
Hudi is also integrated with
Work with cloud storage
Ensure cloud storage credentials are set properly as environment variables, e.g., AWS_*, AZURE_*, or GOOGLE_*.
Relevant storage environment variables will then be picked up. The target table's base uri with schemes such
as s3://, az://, or gs:// will be processed accordingly.
Alternatively, you can pass the storage configuration as options via Table APIs.
Python
=
Rust
use Result;
use TableBuilder as HudiTableBuilder;
async
Contributing
Check out the contributing guide for all the details about making contributions to the project.