This is an automated email from the ASF dual-hosted git repository.
vinothchandar 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 542a37e8c0fc docs(blog): XTable interop post and Parquet/Hive
migration guides (#19271)
542a37e8c0fc is described below
commit 542a37e8c0fc29f327521fa09aaf87878833d1f6
Author: vinoth chandar <[email protected]>
AuthorDate: Tue Jul 28 11:55:24 2026 +0530
docs(blog): XTable interop post and Parquet/Hive migration guides (#19271)
---
...-efficient-migration-of-large-parquet-tables.md | 4 +
...28-using-hudi-with-apache-iceberg-via-xtable.md | 149 +++++++++++++++
.../2026-07-29-migrating-from-parquet-to-hudi.md | 193 +++++++++++++++++++
.../blog/2026-07-30-migrating-from-hive-to-hudi.md | 207 +++++++++++++++++++++
website/src/pages/faq/general.md | 2 +-
website/static/llms.txt | 3 +
6 files changed, 557 insertions(+), 1 deletion(-)
diff --git
a/website/blog/2020-08-20-efficient-migration-of-large-parquet-tables.md
b/website/blog/2020-08-20-efficient-migration-of-large-parquet-tables.md
index 99fa067e1176..4c56bb11922d 100644
--- a/website/blog/2020-08-20-efficient-migration-of-large-parquet-tables.md
+++ b/website/blog/2020-08-20-efficient-migration-of-large-parquet-tables.md
@@ -14,6 +14,10 @@ last_update:
We will look at how to migrate a large parquet table to Hudi without having to
rewrite the entire dataset.
+:::info Updated guide available
+For a current, step-by-step walkthrough of all migration paths — including
bootstrap and full rewrite — see [Migrating from Parquet to Apache
Hudi](/blog/2026/07/29/migrating-from-parquet-to-hudi).
+:::
+
<!--truncate-->
## Motivation:
diff --git
a/website/blog/2026-07-28-using-hudi-with-apache-iceberg-via-xtable.md
b/website/blog/2026-07-28-using-hudi-with-apache-iceberg-via-xtable.md
new file mode 100644
index 000000000000..4937c7cf8f24
--- /dev/null
+++ b/website/blog/2026-07-28-using-hudi-with-apache-iceberg-via-xtable.md
@@ -0,0 +1,149 @@
+---
+title: "Using Apache Hudi with Apache Iceberg: Interoperability via Apache
XTable"
+excerpt: "Choosing Apache Hudi does not mean giving up Apache Iceberg
compatibility. Apache XTable translates Hudi table metadata into Iceberg in
place, so one copy of data serves every engine."
+description: "How Apache XTable lets Iceberg engines and catalogs read Hudi
tables as Iceberg by translating table metadata in place — no data copies, no
rewrites."
+authors: [sivabalan]
+category: how-to
+image: /assets/images/hudi_stack/pluggable_tf.png
+tags:
+- apache xtable
+- apache iceberg
+- interoperability
+- data lakehouse
+- table format
+---
+
+Yes — Apache Iceberg engines and catalogs can read Apache Hudi tables. [Apache
XTable](https://xtable.apache.org) (incubating) translates Hudi table metadata
into Apache Iceberg (and Delta Lake) metadata in place, so a single copy of
data on object storage is readable as any of the three formats. No data is
copied or rewritten; XTable reads the Hudi table's metadata and writes out the
equivalent Iceberg metadata alongside the same Parquet files.
+
+This matters because many teams believe they face a binary choice. Their
streaming ingestion, CDC pipelines, and update-heavy workloads point toward
Hudi's write-side strengths, but a query engine, BI tool, or managed service in
their stack only speaks Iceberg — so they conclude they must give up one to
keep the other. They don't. This post explains how XTable dissolves that
constraint, walks through exposing a Hudi table as Iceberg step by step, and
covers the limitations you should und [...]
+
+## The Format Lock-In Problem
+
+An [open table format](/blog/2026/07/14/what-is-an-open-table-format) is the
metadata layer that turns Parquet files on object storage into a transactional
table — the load-bearing component of the [data
lakehouse](/blog/2024/07/11/what-is-a-data-lakehouse) architecture. Apache
Hudi, Apache Iceberg, and Delta Lake all implement this idea, and all three
ultimately describe the same thing: a set of Parquet data files plus metadata
recording which files form the table, what schema they foll [...]
+
+The friction arises at the edges of the ecosystem. Some engines and managed
services support only one format for external tables — most commonly Iceberg,
given its broad catalog and vendor ecosystem. If your organization has
standardized its serving layer on such an engine, every table you want to query
there apparently needs to be an Iceberg table.
+
+At the same time, Hudi's design center is the write path. Teams pick Hudi for
fast upserts and deletes backed by [pluggable, multi-modal
indexing](/docs/indexes), streaming and CDC ingestion, incremental processing,
and built-in [table services](/docs/hudi_stack) — compaction, clustering,
cleaning — that keep tables optimized without separate orchestration. Giving
those up to satisfy a read-side format requirement is a real cost, paid on
every pipeline, every day.
+
+The choice is false because the formats differ in metadata, not data. If the
Parquet files can stay put and only the metadata needs a second representation,
no migration is required — just a translation. That is precisely what Apache
XTable does.
+
+## What Is Apache XTable?
+
+[Apache XTable](https://xtable.apache.org) is an open source project
incubating at the Apache Software Foundation (it was previously known as
OneTable). In the project's own words, it provides "cross-table
omni-directional interop between lakehouse table formats." Two properties
define it:
+
+- **It is not another table format.** XTable introduces no new specification
for engines to adopt. It provides abstractions and tools for translating
existing table format metadata between Apache Hudi, Apache Iceberg, and Delta
Lake.
+- **It translates metadata, not data.** XTable reads the existing metadata of
your table and writes out metadata for one or more other table formats into the
same table directory. The Parquet data files are never copied, moved, or
rewritten.
+
+Any of the three formats can act as the source, and you can target one or
several formats in a single sync. For this post's scenario, the source is Hudi
and the target is Iceberg — but the same mechanics work Iceberg-to-Hudi,
Delta-to-Iceberg, and every other direction.
+
+The Hudi community treats this as a first-class interoperability path: the
[XTable sync documentation](/docs/syncing_xtable) covers it, and Hudi 1.1's
pluggable table format work goes further by letting XTable supply format
adapters inside Hudi's own write path (more on that below).
+
+## How It Works: Metadata Translation, Not Data Movement
+
+Conceptually, every sync run does three things:
+
+1. **Read the source metadata.** XTable reads the Hudi table's timeline and
metadata to determine the current state of the table: which data files are
live, the schema, partition information, and file-level column statistics.
+2. **Translate to the target model.** That state is mapped into Iceberg's
metadata model — snapshots, manifests, and table metadata files describing the
very same Parquet files.
+3. **Write target metadata alongside.** The Iceberg metadata is written under
the table's base path (in the standard `metadata/` directory Iceberg readers
expect), next to Hudi's `.hoodie` directory. One directory on storage, two
formats' views of it.
+
+The translation carries more than the file list. Data files are synced along
with their column-level statistics and partition metadata, so Iceberg readers
keep the file-skipping benefits they expect from Iceberg's own statistics.
Schema updates in the source are reflected in the target table metadata. XTable
also performs format-appropriate metadata maintenance on the target — for
example, snapshot expiration for Iceberg targets — so translated metadata
doesn't grow without bound.
+
+XTable supports two sync modes. **Full sync** recomputes the target metadata
from the source table's current state. **Incremental sync** translates only the
changes since the last sync, which is more lightweight and performs better,
especially on large tables; if incremental sync can't proceed for some reason,
XTable automatically falls back to a full sync. In practice you run incremental
sync on a schedule (or per commit), and correctness is preserved either way.
+
+## Hands-On: Exposing a Hudi Table as Iceberg
+
+Suppose you have a Hudi table of orders at `s3://warehouse/orders`, written by
a streaming pipeline (Hudi tables created with 0.14.0 or later are supported).
Exposing it as Iceberg takes a config file and one command.
+
+First, get the XTable utilities jar — build it from
[source](https://github.com/apache/incubator-xtable) or download it from the
project's GitHub packages. Then describe the translation in a YAML file:
+
+```yaml title="my_config.yaml"
+sourceFormat: HUDI
+targetFormats:
+ - ICEBERG
+datasets:
+ - tableBasePath: s3://warehouse/orders
+ tableName: orders
+ partitionSpec: order_date:VALUE
+```
+
+`sourceFormat` names the format to read, `targetFormats` lists one or more
formats to produce (you could add `DELTA` to the list to get both at once), and
each entry under `datasets` points at a table. `partitionSpec` tells XTable how
the Hudi table is partitioned so it can map partitions into Iceberg's partition
spec.
+
+Run the sync:
+
+```shell
+java -jar path/to/xtable-utilities-bundled.jar --datasetConfig my_config.yaml
+```
+
+When the run completes, the table's base path contains Iceberg metadata files
— schema, commit history, partitions, and column statistics — describing the
same Parquet files Hudi manages. Re-running the command picks up new Hudi
commits incrementally. You can batch many tables into one config, and schedule
the job with whatever orchestrator you already use.
+
+### Continuous sync from the ingestion pipeline
+
+If the table is fed by [Hudi Streamer](/docs/hoodie_streaming_ingestion), you
can sync every commit as it lands instead of running a separate job. Add the
XTable [extensions
jar](https://github.com/apache/incubator-xtable/tree/main/hudi-support/extensions)
to the classpath, add `org.apache.xtable.hudi.sync.OneTableSyncTool` to the
pipeline's sync classes, and configure the targets:
+
+```
+hoodie.onetable.formats.to.sync=ICEBERG
+hoodie.onetable.target.metadata.retention.hr=168
+```
+
+With this in place, the Iceberg view advances in lockstep with Hudi commits,
and the freshness gap between the two views effectively disappears.
+
+## Registering with Catalogs So Iceberg Engines Find the Table
+
+Producing Iceberg metadata is half the job; Iceberg-speaking engines discover
tables through a catalog, so the synced table needs to be registered. XTable's
[catalog integration docs](https://xtable.apache.org/docs/catalogs-index) cover
registering synced target tables with Hive Metastore, AWS Glue Data Catalog,
Unity Catalog, and BigLake Metastore — registration is an explicit step after
the sync. XTable also supports keeping table metadata synchronized across
catalogs such as Hive Meta [...]
+
+Once registered, the table is, from the engine's point of view, simply an
Iceberg table. Cloud warehouses and services that can query Iceberg tables —
Snowflake, Google BigQuery, Amazon Athena and Redshift, among others — can read
it through their Iceberg support, subject to each vendor's own capabilities and
requirements. Distributed SQL engines like Trino and Presto query it through
their Iceberg connectors. None of them need to know the table is written by
Hudi.
+
+Hudi separately ships its own [catalog sync tools](/docs/syncing_metastore)
for the Hudi-native view of the table, so the same data can be discoverable
both as a Hudi table (for engines with Hudi support) and as an Iceberg table
(for everything else).
+
+## What This Architecture Unlocks
+
+Put together, the pattern looks like this: write once with Hudi, serve
everywhere.
+
+- **Ingestion** uses Hudi's strengths — record-level indexes for fast upserts
and deletes, streaming and CDC ingestion, non-blocking table services,
incremental pulls for downstream pipelines. See [table
types](/docs/table_types) for how Copy-on-Write and Merge-on-Read trade write
latency against read cost.
+- **Storage** holds exactly one copy of the data, as Parquet files on object
storage, with both Hudi and Iceberg metadata describing it.
+- **Serving** spans every engine in the organization: Hudi-native engines read
the Hudi view; Iceberg-only engines and services read the Iceberg view; nothing
is exported, duplicated, or re-ingested.
+
+There is no second pipeline to operate, no divergence between "the Hudi copy"
and "the Iceberg copy," and no per-engine storage bill. Format choice stops
being an organization-wide standardization battle and becomes a per-workload
decision about write-path capabilities.
+
+This direction is also converging with Hudi itself. Starting with Hudi 1.1, a
[pluggable table format framework](/docs/hudi_stack) decouples Hudi's storage
engine — timeline, indexes, concurrency control, table services — from the
metadata format written for data files, with XTable supplying format adapters
for Iceberg and Delta Lake. The trajectory is that maintaining Iceberg metadata
becomes part of the Hudi write path itself, rather than a companion sync.
+
+
+<p align = "center">Figure: Hudi's storage engine with format adapters for
Iceberg and Delta Lake via Apache XTable</p>
+
+## Honest Limitations
+
+XTable is a pragmatic tool, and it's worth being precise about its boundaries.
+
+- **It's read-side interoperability.** The translated Iceberg metadata gives
Iceberg engines a read view of the table. Writes should continue to go through
Hudi; XTable does not merge concurrent writes made independently in two formats
to the same files.
+- **Freshness trails the sync cadence.** The Iceberg view reflects the table
as of the last sync, so it lags the latest Hudi commit by up to the sync
interval. Per-commit sync via the Hudi Streamer extension closes this gap; a
nightly cron does not. Decide the cadence based on how fresh the Iceberg
consumers need to be.
+- **Merge-on-Read log files aren't translated.** XTable syncs the underlying
Parquet base files; Hudi log files (and, in the other directions, Delta
deletion vectors and Iceberg delete files) are not captured. For a Hudi MoR
table, the target format therefore sees the read-optimized view — records still
sitting in log files become visible to Iceberg readers after compaction.
Copy-on-Write tables don't have this caveat.
+- **Source table requirements.** XTable requires Hudi 0.14.0 or later and
depends on Hudi's metadata table; check the [XTable
docs](https://xtable.apache.org/docs/features-and-limitations) for current
prerequisites and configuration.
+- **Feature-mapping edge cases exist.** The formats are not feature-identical,
and format-specific constructs don't always have an exact counterpart on the
other side (for example, Delta generated columns as a source have limited
support). Each format's advanced features work fully only in that format's
native engines.
+
+None of these undermine the core pattern — most production uses are exactly
"Hudi writes, Iceberg engines read" — but they should inform table type, sync
cadence, and which features you rely on across the boundary.
+
+## XTable vs Actually Migrating Formats
+
+XTable also answers the "convert Hudi to Iceberg" question, but it's worth
distinguishing two intents:
+
+- **You need Iceberg read compatibility while keeping Hudi's write path.** Use
XTable in continuous incremental sync mode, as described above. This is the
common case, and nothing is migrated — both views persist indefinitely.
+- **You genuinely want to switch formats.** XTable can serve as a low-risk
migration mechanism: sync the table, repoint readers at the Iceberg view,
validate, and only then move writers to an Iceberg-native write path — all
without rewriting data files. Because the translation is omni-directional, the
door swings both ways: teams migrating an Iceberg or Delta table *to* Hudi to
gain its ingestion and table-service capabilities follow the same steps with
source and target swapped.
+
+The practical takeaway from the [open table format
landscape](/blog/2026/07/14/what-is-an-open-table-format) applies here: pick
the format whose write path fits each workload, and let interoperability handle
the rest. Migration becomes a repointing exercise, not a data-movement project.
+
+## Conclusion
+
+The premise behind "we can't use Hudi because our engine only reads Iceberg"
no longer holds. Hudi, Iceberg, and Delta Lake all describe Parquet files with
metadata, and Apache XTable translates that metadata in any direction —
incrementally, in place, without copying data. A table written with Hudi's
indexing, streaming ingestion, and table services can be registered in a
catalog and queried by any Iceberg-capable engine as an ordinary Iceberg table,
while Hudi-native engines keep the f [...]
+
+To try it, start with the [Hudi XTable sync guide](/docs/syncing_xtable) and
the [Apache XTable documentation](https://xtable.apache.org).
+
+## FAQ
+
+<PostFAQ heading={null} items={[
+ {question: 'Can Iceberg engines read Hudi tables?', answer: 'Yes. Apache
XTable translates a Hudi table\'s metadata into Apache Iceberg metadata written
alongside the same Parquet data files. Once that Iceberg metadata is registered
in a catalog, any engine with Iceberg read support can query the table as an
ordinary Iceberg table.'},
+ {question: 'Can Snowflake read Hudi tables?', answer: 'Not directly, but
Snowflake can query Iceberg tables, and Apache XTable can expose a Hudi table
as Iceberg by translating its metadata in place. After syncing with XTable and
registering the Iceberg metadata, Snowflake reads the table through its Iceberg
support, subject to Snowflake\'s own Iceberg capabilities and requirements.'},
+ {question: 'Does XTable copy my data?', answer: 'No. XTable translates only
table metadata. It reads the source format\'s metadata and writes out the
target format\'s metadata into the same table directory, while the Parquet data
files stay exactly where they are. One copy of data serves all synced
formats.'},
+ {question: 'Can I convert a Hudi table to Iceberg?', answer: 'Yes. Running
XTable with sourceFormat HUDI and targetFormats ICEBERG produces complete
Iceberg metadata for the table without rewriting any data files. You can keep
both views in sync continuously, or use the translation as a migration step by
repointing readers and writers to the Iceberg view after validating it.'},
+ {question: 'Is Apache XTable production ready?', answer: 'Apache XTable is
an incubating project at the Apache Software Foundation, which is a statement
about ASF governance maturity rather than code quality. It is used in real
deployments and its metadata-only design is low risk since source data and
metadata are never modified, but you should validate it against your own tables
and review the documented limitations, such as Merge-on-Read log files not
being captured, before relying o [...]
+ {question: 'Does XTable work with Hudi Merge-on-Read tables?', answer:
'Partially. XTable syncs the Parquet base files but not Hudi log files, so the
Iceberg view of a Merge-on-Read table corresponds to the read-optimized view.
Records still in log files become visible to Iceberg readers after compaction.
Copy-on-Write tables do not have this caveat.'},
+]} />
diff --git a/website/blog/2026-07-29-migrating-from-parquet-to-hudi.md
b/website/blog/2026-07-29-migrating-from-parquet-to-hudi.md
new file mode 100644
index 000000000000..157b16586828
--- /dev/null
+++ b/website/blog/2026-07-29-migrating-from-parquet-to-hudi.md
@@ -0,0 +1,193 @@
+---
+title: "Migrating from Parquet to Apache Hudi: A Practical Guide"
+excerpt: "A step-by-step guide to migrating existing Parquet datasets to
Apache Hudi — choosing between in-place bootstrap and full rewrite, running the
migration, and validating the result."
+description: "How to migrate Parquet tables to Apache Hudi: in-place bootstrap
vs full rewrite with bulk_insert, key and partitioning choices, code examples,
validation and rollback."
+authors: [sivabalan]
+category: how-to
+image:
/assets/images/blog/2024-01-20-Data-Engineering-Bootstrapping-Data-lake-with-Apache-Hudi.png
+tags:
+- migration
+- bootstrap
+- apache parquet
+- apache spark
+- guide
+---
+
+You can migrate existing Parquet datasets to Apache Hudi either in place —
using Hudi's bootstrap operation, which adds Hudi metadata without rewriting
data files — or by rewriting the data through a one-time bulk insert. Which
path you take comes down to how large the table is, how much rewrite cost and
downtime you can absorb, and how the table will be written to afterwards. This
guide walks through both paths end to end: choosing a strategy, making the
up-front design decisions that a [...]
+
+<!--truncate-->
+
+Why migrate at all? A directory of Parquet files is a perfectly good archive,
but it is not a table. It cannot accept updates or deletes without rewriting
whole partitions, it offers no transactional guarantees to concurrent readers
and writers, and downstream consumers must rescan everything to find what
changed. Moving that data under Hudi management gets you [upserts and
deletes](/blog/2026/07/15/what-is-upsert-on-a-data-lake) keyed on individual
records, ACID commits on the [timeline [...]
+
+## Choose Your Migration Strategy
+
+Hudi gives you three ways to bring an existing Parquet table under management,
described in detail in the [migration guide](/docs/migration_guide). Two of
them are variants of the *bootstrap* operation, which builds Hudi's timeline
and metadata over data that stays where it is; the third is a plain rewrite.
+
+- **METADATA_ONLY bootstrap (in place).** Hudi generates *skeleton* files
containing only the record-level metadata columns and key information, one
skeleton per original Parquet file, and links them to your original data files.
The original data is never copied, so a multi-terabyte table can be migrated in
a fraction of the time a rewrite would take. The trade-off: Hudi continues to
rely on the original files, so if they are deleted or modified outside Hudi,
the table is corrupted. The [...]
+- **FULL_RECORD bootstrap.** Hudi copies the full records into new Hudi files,
adding the metadata columns as it goes. This is functionally equivalent to a
bulk insert — after it completes, the table is self-contained and the original
files can eventually be retired. You pay the full rewrite cost.
+- **Full rewrite with `bulk_insert`.** Read the Parquet data with Spark and
write it out as a new Hudi table using the [`bulk_insert` write
operation](/docs/write_operations#bulk_insert). Operationally this is the
simplest path — it is just a Spark job using the same datasource API you will
use for all subsequent writes — and it gives you a chance to re-layout the data
(sorting, partitioning) as you write.
+
+The bootstrap modes can be mixed *within one table, per partition*: a common
pattern is FULL_RECORD for a small set of hot, frequently-updated partitions
and METADATA_ONLY for the long tail of cold history.
+
+| | METADATA_ONLY bootstrap | FULL_RECORD bootstrap | Full rewrite
(`bulk_insert`) |
+|---|---|---|---|
+| Data files rewritten | No — skeleton files only | Yes | Yes |
+| Migration time / cost | Low, proportional to metadata | High, proportional
to data size | High, proportional to data size |
+| Original Parquet files after migration | Still required — must not be
modified or deleted | Independent; originals can be retired | Independent;
originals can be retired |
+| Resulting table | Hudi table referencing external base files |
Self-contained Hudi table | Self-contained Hudi table |
+| Chance to re-sort / re-layout data | No | No | Yes
(`hoodie.bulkinsert.sort.mode`) |
+| Best for | Very large tables where rewrite is impractical | Hot partitions
of a large table (via regex selector) | Small-to-medium tables, or when you
want a clean re-layout |
+
+Decision rule of thumb: if the table is small enough that rewriting it is an
overnight Spark job you can afford to run once, take the full rewrite — it
leaves no strings attached to the old files. If the table is tens of terabytes
or more and a rewrite is measured in days and real money, bootstrap in place
with METADATA_ONLY, optionally upgrading hot partitions to FULL_RECORD.
+
+## Before You Start: Three Decisions That Are Hard to Change
+
+However you migrate, you are about to give this dataset a table identity.
Three choices deserve deliberate thought *before* you run anything, because
changing them later effectively means migrating again.
+
+1. **Record key.** The field (or comma-separated fields) that uniquely
identifies each record — set via `hoodie.datasource.write.recordkey.field`.
Every upsert and delete is keyed on it, and [indexes](/docs/indexes) map keys
to file groups. Pick something genuinely unique and stable: a natural business
key or an existing surrogate key. See [key generation](/docs/key_generation)
for composite and timestamp-based options.
+2. **Ordering field(s).** Set via `hoodie.table.ordering.fields`. When two
records with the same key collide — within one batch or across writes — the
record with the larger ordering value wins. An event timestamp or a
monotonically increasing version column is the usual choice.
+3. **Partitioning.** Set via `hoodie.datasource.write.partitionpath.field`. If
you bootstrap in place, your existing directory layout *is* your partitioning,
so make peace with it first. If you do a full rewrite, this is your one cheap
opportunity to fix a bad partition scheme — too many tiny partitions, or
partitioning on a column no query filters on. Also decide on
`hoodie.datasource.write.hive_style_partitioning` (`city=chennai/` style paths)
now, for catalog compatibility.
+
+You will also choose a [table type](/docs/table_types) — Copy-on-Write is the
right default for a freshly migrated table; you can adopt Merge-on-Read where
write latency demands it.
+
+## Path A: Full Rewrite with bulk_insert
+
+For small and medium tables, migration is a single Spark job: read Parquet,
write Hudi. The `bulk_insert` operation exists precisely for this — unlike
`upsert`/`insert`, it uses a disk-based, sort-oriented write path that scales
to very large initial loads without caching input in memory (see [write
operations](/docs/write_operations) for the full comparison).
+
+```scala
+// spark-shell with the Hudi bundle, per the quick start guide
+val df =
spark.read.format("parquet").load("s3://my-bucket/warehouse/trips_parquet")
+
+df.write.format("hudi").
+ option("hoodie.table.name", "trips").
+ option("hoodie.datasource.write.operation", "bulk_insert").
+ option("hoodie.datasource.write.recordkey.field", "trip_id").
+ option("hoodie.datasource.write.partitionpath.field", "city").
+ option("hoodie.table.ordering.fields", "ts").
+ option("hoodie.datasource.write.hive_style_partitioning", "true").
+ mode("overwrite").
+ save("s3://my-bucket/warehouse/trips")
+```
+
+Two knobs worth knowing:
+
+- `hoodie.bulkinsert.sort.mode` controls layout of the written files. The
default `NONE` matches plain `spark.write.parquet()` in speed and file count;
`GLOBAL_SORT` costs a sort but produces the best file sizes and, if you sort by
a commonly filtered column, better data skipping forever after.
+- Note that `bulk_insert` does a best-effort job at file sizing rather than
guaranteeing it; ongoing `upsert`/`insert` writes will progressively correct
small files.
+
+From here on, the table behaves exactly like any Hudi table in the [quick
start guide](/docs/quick-start-guide): switch subsequent writes to `upsert`
with `mode("append")`, and query it from Spark, Trino, Presto, or Hive.
+
+## Path B: In-Place Bootstrap for Large Tables
+
+When rewriting is off the table, use bootstrap. The two most convenient
front-ends are the Hudi Streamer utility and the Spark SQL `CALL` procedure;
both drive the same underlying mechanism, and both are documented in the
[migration guide](/docs/migration_guide).
+
+### Using Hudi Streamer
+
+[Hudi Streamer](/docs/hoodie_streaming_ingestion#hudi-streamer) supports
bootstrap via the `--run-bootstrap` flag. This example applies FULL_RECORD mode
to all partitions matching the regex `.*` using the regex mode selector —
change the regex to bootstrap only hot partitions as FULL_RECORD, and unmatched
partitions get the other mode:
+
+```bash
+spark-submit \
+--conf 'spark.serializer=org.apache.spark.serializer.KryoSerializer' \
+--class org.apache.hudi.utilities.streamer.HoodieStreamer
/path/to/hudi-utilities-bundle.jar \
+--run-bootstrap \
+--target-base-path s3://my-bucket/warehouse/trips \
+--target-table trips \
+--table-type COPY_ON_WRITE \
+--hoodie-conf
hoodie.bootstrap.base.path=s3://my-bucket/warehouse/trips_parquet \
+--hoodie-conf hoodie.datasource.write.recordkey.field=trip_id \
+--hoodie-conf hoodie.datasource.write.partitionpath.field=city \
+--hoodie-conf hoodie.table.ordering.fields=ts \
+--hoodie-conf
hoodie.bootstrap.mode.selector=org.apache.hudi.client.bootstrap.selector.BootstrapRegexModeSelector
\
+--hoodie-conf hoodie.bootstrap.mode.selector.regex='.*' \
+--hoodie-conf hoodie.bootstrap.mode.selector.regex.mode=FULL_RECORD \
+--hoodie-conf hoodie.datasource.write.hive_style_partitioning=true
+```
+
+The key config is `hoodie.bootstrap.base.path` — the location of the existing
Parquet dataset. If you supply *only* that config, Hudi defaults to
METADATA_ONLY mode for every partition (`hoodie.bootstrap.mode.selector`
defaults to `MetadataOnlyBootstrapModeSelector`), which is exactly the cheap
in-place migration most large tables want.
+
+### Using the Spark SQL CALL procedure
+
+If you live in Spark SQL, the [`run_bootstrap`
procedure](/docs/procedures#run_bootstrap) does the same job:
+
+```sql
+CALL run_bootstrap(
+ table => 'trips',
+ table_type => 'COPY_ON_WRITE',
+ bootstrap_path => 's3://my-bucket/warehouse/trips_parquet',
+ base_path => 's3://my-bucket/warehouse/trips',
+ rowKey_field => 'trip_id',
+ partition_path_field => 'city'
+);
+```
+
+`bootstrap_path` points at the existing Parquet data and `base_path` at the
new Hudi table location. The default `selector_class` is again
`MetadataOnlyBootstrapModeSelector`; pass `selector_class =>
'org.apache.hudi.client.bootstrap.selector.FullRecordBootstrapModeSelector'`
for a full-record bootstrap. There is also a [Hudi CLI
path](/docs/migration_guide#using-hudi-cli) (`bootstrap run`) if you prefer
driving it from the CLI, and a plain Spark datasource loop for
partition-by-partitio [...]
+
+One operational note for METADATA_ONLY tables: from this point on, the
original Parquet location is part of your table. Lock it down — revoke write
access, exclude it from retention/lifecycle policies — because any modification
or deletion there corrupts the Hudi table.
+
+## Migrating New Partitions Only
+
+There is a zero-migration option worth knowing: keep historical partitions as
plain Parquet and let Hudi manage only *new* partitions, as described in the
[migration guide](/docs/migration_guide#use-hudi-for-new-partitions-alone).
Hudi tolerates such mixed tables as long as each partition is entirely
Hudi-managed or entirely not. Start writing new partitions (or convert just the
last N) through Hudi, and leave the rest untouched.
+
+The caveat is fundamental: none of Hudi's primitives work on the unmanaged
partitions — no upserts, no deletes, no incremental pull there. This pattern
fits genuinely append-only tables (immutable event logs partitioned by date)
where old partitions will never be touched again. If there is any chance of
updates landing in history — CDC replication, GDPR deletes, late corrections —
migrate the whole table.
+
+## Validation Checklist
+
+Before pointing production at the new table, verify it. Bootstrap and bulk
insert are one-shot operations; catching a wrong key choice now costs minutes,
catching it in three months costs a re-migration.
+
+1. **Row counts match.** `spark.read.format("hudi").load(basePath).count()`
against the source Parquet count, overall and per partition.
+2. **Keys are actually unique.** Group by your record key and confirm no count
exceeds 1 — duplicate keys silently collapse into one record on the first
upsert.
+3. **Sample lookups.** Pick a handful of known records and confirm field-level
equality between source and target, including partition placement.
+4. **Exercise a write.** Upsert a few records and delete one on a staging copy
or a scratch partition; confirm the change is visible and row counts move as
expected.
+5. **Query engines see the table.** Sync the table to your catalog (Hive
Metastore, AWS Glue, etc. — see [syncing to catalogs](/docs/syncing_metastore))
and run representative queries from every engine that matters — Spark, Trino,
Presto. This matters doubly for METADATA_ONLY tables, where engines read merged
skeleton + original data.
+6. **Incremental query sanity.** Run an incremental query from the bootstrap
commit forward and confirm it returns your test writes.
+
+## Rollback Plan
+
+A comforting property of both paths: the original Parquet data is never
mutated. Bootstrap writes skeleton files and a timeline in a *new* base path; a
full rewrite writes an entirely new copy. Your rollback plan is therefore
simple:
+
+- Keep the source Parquet dataset intact and readable until validation
completes and the new table has served real workloads for an agreed soak period.
+- If something is wrong — bad record key, wrong partitioning — delete the Hudi
base path, fix the configuration, and re-run the migration. Nothing about the
source has changed.
+- For METADATA_ONLY bootstrap, remember the inverse: you can always abandon
the Hudi table, but you can never abandon the original files while keeping it.
+
+The one thing to avoid during the migration window is dual-writing to both the
old Parquet location and the new Hudi table without a plan for reconciling
them. Freeze writes to the source, migrate, validate, then cut writers over to
Hudi.
+
+## Operational Follow-Ups
+
+Migration makes the data Hudi-managed; a few follow-ups make it fast.
+
+- **Metadata table and indexes.** Hudi's [metadata table](/docs/metadata)
eliminates file listings, and [indexes](/docs/indexes) — column stats for data
skipping, record level index for point lookups — determine upsert performance.
Review which indexes fit your workload once real update traffic starts.
+- **Compaction, if you adopt Merge-on-Read.** MoR tables need
[compaction](/docs/compaction) to fold log files into base files; make sure it
is scheduled and running.
+- **Clustering.** If you bootstrapped in place (no chance to sort at write
time) or bulk-inserted with `NONE` sort mode, [clustering](/docs/clustering)
can reorganize and sort data in the background later — regaining the layout
benefits without another migration.
+- **Cleaning and file sizing.** The defaults are sane, but confirm cleaner
retention matches your longest-running queries, and let ongoing writes correct
any small files the initial load produced.
+
+## Conclusion
+
+Migrating Parquet to Hudi is not a leap; it is a choice between two well-worn
paths. Small and medium tables: one `bulk_insert` Spark job and you have a
self-contained Hudi table, with a free chance to fix layout on the way through.
Large tables: bootstrap in place with METADATA_ONLY (upgrading hot partitions
to FULL_RECORD) and skip the rewrite entirely. In both cases the original data
survives untouched, so the risk profile is a validation exercise, not a bet.
Decide your record key, o [...]
+
+## FAQ
+
+<PostFAQ heading={null} items={[
+ {
+ question: 'Do I have to rewrite my Parquet files to use Hudi?',
+ answer: 'No. Hudi\'s bootstrap operation in METADATA_ONLY mode generates
small skeleton files containing only record-level metadata and links them to
your existing Parquet files, so the original data is never copied or modified.
A FULL_RECORD bootstrap or a bulk_insert rewrite is only needed if you want the
table to be fully self-contained or want to change its layout.',
+ },
+ {
+ question: 'How long does a Parquet to Hudi migration take?',
+ answer: 'A METADATA_ONLY bootstrap only writes metadata, so it runs in a
small fraction of the time of a rewrite, even on multi-terabyte tables. A full
rewrite with bulk_insert or a FULL_RECORD bootstrap is proportional to data
size, roughly comparable to a Spark job that reads and rewrites the whole
dataset. Many teams mix the two, fully rewriting a few hot partitions and
metadata-bootstrapping the cold history.',
+ },
+ {
+ question: 'Can I roll back a migration to Hudi?',
+ answer: 'Yes. Neither bootstrap nor a bulk_insert rewrite modifies the
original Parquet files, so rolling back means deleting the new Hudi base path
and continuing to use the source data. Keep the source dataset intact until you
have validated row counts, key uniqueness, and queries from all your engines
against the new table.',
+ },
+ {
+ question: 'What is the difference between METADATA_ONLY and FULL_RECORD
bootstrap?',
+ answer: 'METADATA_ONLY writes skeleton files with just the Hudi metadata
columns and keeps relying on your original Parquet files, so it is fast but the
originals must never be modified or deleted. FULL_RECORD copies the complete
records into new Hudi files, which costs a full rewrite but produces a
self-contained table. You can apply different modes to different partitions of
the same table using a regex-based selector.',
+ },
+ {
+ question: 'Can I keep old partitions as plain Parquet and only use Hudi
for new data?',
+ answer: 'Yes, as long as each partition is entirely Hudi-managed or
entirely not. This works well for append-only tables where historical
partitions never change. The catch is that upserts, deletes, and incremental
queries only work on the Hudi-managed partitions, so if updates might ever
touch history, migrate the whole table instead.',
+ },
+ {
+ question: 'What should I decide before migrating a table to Hudi?',
+ answer: 'Three things: the record key that uniquely identifies each
record, the ordering field used to pick a winner when the same key appears
twice, and the partitioning scheme. These shape every subsequent write and are
hard to change without re-migrating. If you bootstrap in place, the existing
directory layout becomes your partitioning, so review it before you start.',
+ },
+]} />
diff --git a/website/blog/2026-07-30-migrating-from-hive-to-hudi.md
b/website/blog/2026-07-30-migrating-from-hive-to-hudi.md
new file mode 100644
index 000000000000..bbe74de6ecdf
--- /dev/null
+++ b/website/blog/2026-07-30-migrating-from-hive-to-hudi.md
@@ -0,0 +1,207 @@
+---
+title: "Migrating from Apache Hive Tables to Apache Hudi"
+excerpt: "A practical guide to converting existing Apache Hive tables into
Apache Hudi tables — in place via bootstrap or by rewriting — and
re-registering them in your metastore with Hudi's catalog sync."
+description: "Step-by-step guide to migrating Apache Hive tables to Apache
Hudi: in-place bootstrap vs full rewrite, Hive metastore sync, validation and
safe decommissioning."
+authors: [sivabalan]
+category: how-to
+image:
/assets/images/blog/2024-01-20-Data-Engineering-Bootstrapping-Data-lake-with-Apache-Hudi.png
+tags:
+- migration
+- bootstrap
+- apache hive
+- guide
+---
+
+Migrating an Apache Hive table to Apache Hudi means converting its underlying
data files into a Hudi table — either in place with Hudi's
[bootstrap](/docs/migration_guide) mechanism or by rewriting the data with
Spark — and re-registering it in your metastore through Hudi's [catalog
sync](/docs/syncing_metastore). Your existing Hive, Spark, Presto and Trino
queries keep working against the same metastore entry, and the table gains
upserts, deletes, ACID transactions and incremental reads [...]
+
+This guide walks through the whole journey: why teams outgrow plain Hive
tables, what actually changes on disk and in the metastore, how to plan key and
partition mapping, the two migration paths (in-place bootstrap and full
rewrite), re-pointing the metastore, validating with the queries you already
run, and finally decommissioning the old table safely.
+
+## Why Teams Move Off Plain Hive Tables
+
+A classic Hive external table is a directory convention plus a metastore
entry. That simplicity served the Hadoop era well, but it carries structural
costs that get worse as tables grow and freshness expectations tighten:
+
+- **No transactions on the data lake.** A plain Hive table over Parquet or ORC
has no atomic commit protocol. A failed `INSERT OVERWRITE` can leave partial
files behind, and readers racing a writer can see half-written partitions.
Every team ends up building `_SUCCESS`-file conventions or staging-directory
dances around this gap.
+- **Updates mean rewriting partitions.** There is no record-level update or
delete. Fixing one late-arriving record, applying a CDC stream, or servicing a
GDPR deletion request means rewriting every affected partition wholesale —
slow, expensive, and disruptive to readers.
+- **Slow, listing-bound query planning.** Query engines discover data in Hive
tables by listing directories. On cloud object stores, listing millions of
files across thousands of partitions dominates planning time, and historically
the eventual consistency of object-store listings caused correctness surprises
too.
+- **No incremental consumption.** Downstream pipelines cannot ask a Hive table
"what changed since yesterday?" — they re-scan whole partitions on a schedule,
multiplying compute costs.
+
+Hive ACID (transactional ORC tables) brings transactions and row-level updates
to Hive, but it is tied to ORC, to Hive's own compactor running inside the
metastore ecosystem, and its delta files are not first-class citizens in Spark,
Trino and the broader lakehouse engine ecosystem the way an open table format
is. Lakehouse formats like Hudi were built to provide transactions, mutability
and incremental processing directly on open columnar files, queryable from
every major engine. If you [...]
+
+## What Changes — and What Doesn't
+
+It helps to be precise about what a migration touches:
+
+**What stays the same.** Your data remains in open columnar files — Hudi
writes Parquet base files (and can bootstrap existing Parquet in place). The
data keeps living on the same HDFS or object storage. Analysts keep querying
through the same metastore-backed catalog, from the same engines.
+
+**What changes.** The table gains a Hudi timeline (a commit log recording
every action), metadata and indexes mapping record keys to files, and
Hudi-managed file layout with automatic file sizing. The metastore entry is
re-registered through Hudi's sync so that engines route reads through Hudi's
integrations — Hive via the Hudi input format, Spark and Trino/Presto via their
native Hudi connectors. From then on, writes go through Hudi (Spark, Flink, or
[Hudi Streamer](/docs/hoodie_streami [...]
+
+If your estate also includes raw Parquet directories that were never
registered in Hive, the companion post on [migrating from Parquet to
Hudi](/blog/2026/07/29/migrating-from-parquet-to-hudi) covers that path; this
post focuses on tables living in the Hive metastore.
+
+## Planning the Migration
+
+Before running anything, map three concepts from your Hive schema onto Hudi's
table configs:
+
+**Record key.** Hudi identifies each record by a key so it can find and update
it later. Pick the column (or comma-separated columns) that uniquely identifies
a row — an id column, or a composite of natural keys. This becomes
`hoodie.datasource.write.recordkey.field` (or `primaryKey` in Spark SQL table
properties). If the Hive table genuinely has no key and is append-only, you can
migrate without one, but you give up upserts on that table.
+
+**Ordering field.** When two versions of the same key arrive, Hudi resolves
the winner using ordering fields (`hoodie.table.ordering.fields`). An
`updated_at`-style timestamp is the usual choice; it prevents older records
from overwriting newer ones.
+
+**Partition mapping.** Hudi partitioning maps directly from Hive partitioning:
+
+- *Single-level partitions* (`ds=2026-08-01`) map to a single
`hoodie.datasource.write.partitionpath.field` with
`hoodie.datasource.write.hive_style_partitioning=true` to preserve the
`key=value` directory style your Hive table already uses.
+- *Multi-level partitions* (`year=2026/month=08/day=01`) map to a
comma-separated partition path field list (e.g. `year,month,day`); on the sync
side, the default `MultiPartKeysValueExtractor` splits partition values on `/`
correctly.
+- *Non-partitioned tables* simply omit the partition path field; during
metastore sync Hudi infers `NonPartitionedExtractor` automatically.
+
+Also decide the table type now: Copy-on-Write (CoW) is the simplest starting
point for formerly-Hive analytics tables; Merge-on-Read (MoR) suits
update-heavy or streaming write patterns. The [table types
guide](/docs/table_types) covers the trade-off; note that MoR changes what
appears in your metastore after sync (more below).
+
+## Path A: In-Place Bootstrap for Large Tables
+
+For big tables, rewriting terabytes of perfectly good Parquet just to adopt a
new format is wasteful. Hudi's [bootstrap](/docs/migration_guide) converts a
table in place, with two modes you can mix per partition:
+
+- **METADATA_ONLY** generates skeleton files containing only Hudi's metadata
columns and record keys, alongside pointers to your original data files —
avoiding the full cost of rewriting the dataset. Queries and upserts work
normally. The caveat: Hudi still relies on the original files, so they must not
be deleted or modified.
+- **FULL_RECORD** copies the full record data into Hudi-managed files,
functionally equivalent to a bulk insert. After it completes, the Hudi table is
fully self-contained.
+
+A common large-table strategy is METADATA_ONLY for cold historical partitions
and FULL_RECORD for recent, actively-updated partitions — selected with a regex
over partition paths.
+
+### With Hudi Streamer
+
+Hudi Streamer supports bootstrap via the `--run-bootstrap` flag. This example
applies FULL_RECORD to all partitions matching the regex `.*`, keeping
hive-style partitioning:
+
+```bash
+spark-submit \
+--conf 'spark.serializer=org.apache.spark.serializer.KryoSerializer' \
+--class org.apache.hudi.utilities.streamer.HoodieStreamer
/path/to/hudi-utilities-bundle.jar \
+--run-bootstrap \
+--target-base-path hdfs://ns1/warehouse/hudi/trips \
+--target-table trips \
+--table-type COPY_ON_WRITE \
+--hoodie-conf
hoodie.bootstrap.base.path=hdfs://ns1/hive/warehouse/mydb.db/trips \
+--hoodie-conf hoodie.datasource.write.recordkey.field=trip_id \
+--hoodie-conf hoodie.datasource.write.partitionpath.field=ds \
+--hoodie-conf hoodie.table.ordering.fields=updated_at \
+--hoodie-conf
hoodie.bootstrap.mode.selector=org.apache.hudi.client.bootstrap.selector.BootstrapRegexModeSelector
\
+--hoodie-conf hoodie.bootstrap.mode.selector.regex='.*' \
+--hoodie-conf hoodie.bootstrap.mode.selector.regex.mode=FULL_RECORD \
+--hoodie-conf hoodie.datasource.write.hive_style_partitioning=true
+```
+
+`hoodie.bootstrap.base.path` points at the existing Hive table's location —
the warehouse directory the metastore already knows about. To split modes by
partition, keep the `BootstrapRegexModeSelector` and set the regex to match the
partitions that should get the mode named in
`hoodie.bootstrap.mode.selector.regex.mode`; unmatched partitions get the other
mode. With only `hoodie.bootstrap.base.path` provided and no selector configs,
METADATA_ONLY is the default for everything.
+
+### With the Spark SQL CALL procedure
+
+If you prefer staying in Spark SQL, the [`run_bootstrap`
procedure](/docs/procedures#run_bootstrap) does the same job:
+
+```sql
+CALL run_bootstrap(
+ table => 'trips',
+ table_type => 'COPY_ON_WRITE',
+ bootstrap_path => 'hdfs://ns1/hive/warehouse/mydb.db/trips',
+ base_path => 'hdfs://ns1/warehouse/hudi/trips',
+ rowKey_field => 'trip_id',
+ partition_path_field => 'ds'
+);
+```
+
+By default this uses the METADATA_ONLY selector; pass `selector_class =>
'org.apache.hudi.client.bootstrap.selector.FullRecordBootstrapModeSelector'`
for a full-record bootstrap.
+
+A third option worth knowing: Hudi does not require converting the entire
table at once. Because the lowest granularity Hudi manages is a Hive partition,
an append-only table can simply start writing *new* partitions through Hudi
while old partitions stay as-is — with the caveat that upserts and incremental
pulls do not work on the unconverted partitions.
+
+## Path B: Rewrite via Spark
+
+For small and medium tables — or whenever you want a clean, fully Hudi-managed
copy with per-record ordering applied — a straight rewrite is the simplest
path. Since the source is already a metastore table, Spark SQL can read it
directly:
+
+```sql
+-- define the Hudi table with key, ordering and partitioning mapped from the
Hive schema
+CREATE TABLE hudi_trips (
+ trip_id STRING,
+ rider STRING,
+ fare DOUBLE,
+ updated_at BIGINT,
+ ds STRING
+) USING HUDI
+TBLPROPERTIES (
+ primaryKey = 'trip_id',
+ orderingFields = 'updated_at'
+)
+PARTITIONED BY (ds)
+LOCATION 'hdfs://ns1/warehouse/hudi/trips';
+
+-- use bulk_insert for the one-time load: fastest, skips upsert-path indexing
work
+SET hoodie.spark.sql.insert.into.operation = bulk_insert;
+
+INSERT INTO hudi_trips
+SELECT trip_id, rider, fare, updated_at, ds FROM mydb.trips;
+```
+
+The `bulk_insert` operation is the right choice for the initial load — it is
optimized for large one-time writes and lets Hudi apply its file-sizing configs
so the new table starts life with well-sized files rather than inheriting
whatever small-file sprawl the Hive table had. The equivalent DataFrame API
flow (read the Hive table with `spark.table(...)`, write with `format("hudi")`)
is shown in the [quick start guide](/docs/quick-start-guide). For very large
tables where a single job is [...]
+
+## Re-Pointing the Metastore
+
+The step that makes the migration invisible to downstream users is [syncing
the Hudi table to the Hive metastore](/docs/syncing_metastore). Hudi's Hive
sync registers the table (schema, partitions, location, input format) in the
metastore and keeps it current as new commits add columns or partitions.
+
+The easiest way is to enable sync on the writer itself, so every write keeps
the catalog fresh:
+
+```
+hoodie.datasource.meta.sync.enable=true
+hoodie.datasource.hive_sync.mode=hms
+hoodie.datasource.hive_sync.metastore.uris=thrift://hive-metastore:9083
+hoodie.datasource.hive_sync.database=mydb
+hoodie.datasource.hive_sync.table=trips
+```
+
+Alternatively, run the standalone `HiveSyncTool` from the command line for a
one-shot or externally scheduled sync:
+
+```bash
+cd hudi-hive
+./run_sync_tool.sh --jdbc-url jdbc:hive2://hiveserver:10000 \
+ --user hive --pass hive \
+ --partitioned-by ds --base-path hdfs://ns1/warehouse/hudi/trips \
+ --database mydb --table trips
+```
+
+Sync supports three modes — `hms` (metastore thrift APIs, recommended), `jdbc`
(via HiveServer2) and `hiveql` — detailed in the [sync
doc](/docs/syncing_metastore). A few migration-specific notes:
+
+- **Partition values** are extracted by
`hoodie.datasource.hive_sync.partition_value_extractor`. The default
`MultiPartKeysValueExtractor` handles multi-level partitions split on `/`;
hive-style partitioned and non-partitioned tables get
`HiveStylePartitionValueExtractor` and `NonPartitionedExtractor` inferred
automatically.
+- **CoW tables** appear as a single table under the same name your users
expect — one metastore entry, drop-in replacement.
+- **MoR tables** sync as two views: `<table>_ro` (read-optimized — queries
only the compacted columnar base files) and `<table>_rt` (real-time/snapshot —
merges in the latest log files for the freshest data). Point latency-sensitive
dashboards at `_ro` and freshness-sensitive consumers at `_rt`. If you migrate
a Hive table to MoR, plan for downstream queries to pick one of the two names.
+- If you sync to AWS Glue instead of a self-managed metastore, the flow is the
same with a Glue-specific sync client — see [Syncing to AWS Glue Data
Catalog](/docs/syncing_aws_glue_data_catalog).
+
+## Validating with Your Existing Queries
+
+Before flipping any traffic, verify the migrated table from every engine you
actually use:
+
+**Row counts and spot checks.** Compare `SELECT COUNT(*)` between old and new,
overall and per partition. Spot-check a sample of records by key. Note that
Hudi adds meta columns (`_hoodie_commit_time`, `_hoodie_record_key`, etc.) —
`SELECT *` outputs will include them, so audit any consumers doing positional
column access.
+
+**Hive.** Query through beeline; ensure the `hudi-hadoop-mr-bundle` jar is
available to Hive (e.g. in its `auxlib/` directory) and set the input format so
splits are computed correctly:
+
+```
+beeline -u jdbc:hive2://hiveserver:10000/mydb \
+ --hiveconf hive.input.format=org.apache.hadoop.hive.ql.io.HiveInputFormat
+```
+
+**Spark and Trino/Presto.** These engines have native Hudi support and resolve
the synced table straight from the metastore — run your representative
dashboards and pipeline queries against the new table name. For MoR, run them
against both `_ro` and `_rt` so you understand the freshness difference before
choosing defaults.
+
+**Exercise the new capabilities.** Do a small `UPDATE`/`DELETE` or an upsert
write, confirm it lands atomically, and try an incremental query — this is,
after all, why you migrated.
+
+## Decommissioning the Old Table Safely
+
+Once validation passes and writers have been cut over to Hudi, retire the old
table deliberately:
+
+1. **Freeze writes to the old Hive table first.** Run the final bootstrap or
rewrite *after* the freeze so no records land in the old location and get
missed. (For rewrites you can also do an initial bulk load, then a short
catch-up pass for late data before cutover.)
+2. **Repoint consumers, then watch.** Keep the old table readable during a
burn-in period while dashboards and pipelines run against the Hudi table.
+3. **Mind the METADATA_ONLY dependency.** If you bootstrapped with
METADATA_ONLY, the Hudi table still references the original data files —
deleting or modifying them causes data loss or corruption. Either keep the
original files permanently (drop only the old metastore *entry*, not the data),
or convert to fully Hudi-managed files before deleting anything.
+4. **Drop the metastore entry last.** For an external Hive table, `DROP TABLE`
removes only metadata; delete the underlying directory separately, and only
when rule 3 allows.
+
+## Conclusion
+
+Migrating from Hive to Hudi is less a leap than a re-registration: the files
stay open columnar, the catalog stays the metastore, the engines stay Spark,
Hive, Trino and Presto. What changes is the table layer in between — and with
it, everything a plain Hive table couldn't do. Choose in-place
[bootstrap](/docs/migration_guide) (METADATA_ONLY, FULL_RECORD, or a
regex-selected mix) for large tables, a `bulk_insert` rewrite for smaller ones,
wire up [metastore sync](/docs/syncing_metastore [...]
+
+## FAQ
+
+<PostFAQ heading={null} items={[
+ {question: 'Can Hive still query a table after it\'s migrated to Hudi?',
answer: 'Yes. Hudi\'s Hive sync registers the table in the Hive metastore with
Hudi\'s input format, and Hive queries it through the hudi-hadoop-mr bundle.
Spark, Presto and Trino also query the synced table through their native Hudi
integrations, so existing SQL keeps working.'},
+ {question: 'Do I need to stop writes during the migration?', answer: 'Only
briefly. Freeze writes to the old Hive table, run the final bootstrap or
catch-up rewrite pass, then cut writers over to the Hudi table. For rewrite
migrations you can bulk load most of the data ahead of time and keep the write
freeze down to a short catch-up window.'},
+ {question: 'What happens to my Hive partitions?', answer: 'They map
directly. Hudi preserves hive style key=value partition directories when
hive_style_partitioning is enabled, multi level partitions map to a comma
separated partition path field list, and Hive sync re-registers all partitions
in the metastore using a partition value extractor that is inferred
automatically in common cases.'},
+ {question: 'Should I use METADATA_ONLY or FULL_RECORD bootstrap?', answer:
'METADATA_ONLY writes only skeleton files with record keys and avoids rewriting
data, making it ideal for large cold partitions, but the original files must
never be deleted or modified. FULL_RECORD copies data into fully Hudi managed
files, equivalent to a bulk insert. A common pattern applies FULL_RECORD to
recent hot partitions and METADATA_ONLY to the rest using a regex mode
selector.'},
+ {question: 'What are the _ro and _rt tables that appear after migrating to
Merge-on-Read?', answer: 'For a Merge-on-Read table, Hive sync registers two
views in the metastore: a read optimized view suffixed _ro that queries only
compacted columnar base files, and a real time view suffixed _rt that merges in
the latest log files for the freshest data. Copy-on-Write tables sync as a
single table under the original name.'},
+ {question: 'Can I migrate only part of a Hive table to Hudi?', answer: 'Yes.
Because Hudi manages data at Hive partition granularity, an append only table
can start writing new partitions through Hudi while historical partitions stay
untouched. The caveat is that upserts and incremental queries only work on the
Hudi managed partitions, so update heavy tables should be converted fully.'},
+]} />
diff --git a/website/src/pages/faq/general.md b/website/src/pages/faq/general.md
index fe19916b825d..98c06fac9584 100644
--- a/website/src/pages/faq/general.md
+++ b/website/src/pages/faq/general.md
@@ -27,7 +27,7 @@ Combined with a storage format that balances write speed and
query performance,
The two projects were engineered around different workloads. Iceberg's design
centers on the traditional batch, scan-oriented workloads that Apache Hive
served — large periodic rewrites and full-table scans. Hudi was engineered for
fast-moving, mutable data: streaming ingestion, CDC, record-level upserts and
deletes, and incremental pipelines that process only what changed. Choosing
between them is a question of workload fit, not either/or on data access.
-That is because both formats store data as Parquet files, and [Apache
XTable](/docs/syncing_xtable) (incubating) translates Hudi table metadata into
Iceberg metadata in place — no data is copied or rewritten — making Hudi
copy-on-write tables fully format-compatible with Iceberg readers: a single
copy of data on cloud storage is readable as both Hudi and Iceberg.
(Merge-on-read tables expose their compacted read-optimized view to Iceberg
readers.) You can ingest and manage tables with Hu [...]
+That is because both formats store data as Parquet files, and [Apache
XTable](/docs/syncing_xtable) (incubating) translates Hudi table metadata into
Iceberg metadata in place — no data is copied or rewritten — making Hudi
copy-on-write tables fully format-compatible with Iceberg readers: a single
copy of data on cloud storage is readable as both Hudi and Iceberg.
(Merge-on-read tables expose their compacted read-optimized view to Iceberg
readers.) You can ingest and manage tables with Hu [...]
### What are some non-goals for Hudi?
diff --git a/website/static/llms.txt b/website/static/llms.txt
index 33fc4c0fc4bd..5177506057f8 100644
--- a/website/static/llms.txt
+++ b/website/static/llms.txt
@@ -47,9 +47,12 @@ It can also synchronize your data to half dozen data
catalogs to keep table cons
- [What is CDC on a Data
Lake?](https://hudi.apache.org/blog/2026/07/22/what-is-cdc-on-a-data-lake):
Change data capture from operational databases into lakehouse tables, explained
from first principles.
- [Lakehouse vs Data Warehouse vs Data
Lake](https://hudi.apache.org/blog/2026/07/23/lakehouse-vs-data-warehouse-vs-data-lake):
The three architectures compared — strengths, costs and when to use each.
- [Open Table Format vs Data
Lakehouse](https://hudi.apache.org/blog/2026/07/24/open-table-format-vs-data-lakehouse):
Untangling the table layer from the architecture, and where file formats,
catalogs and engines fit.
+- [Using Hudi with Apache Iceberg via
XTable](https://hudi.apache.org/blog/2026/07/28/using-hudi-with-apache-iceberg-via-xtable):
How Iceberg-only engines and catalogs can read Hudi tables through Apache
XTable metadata sync.
## Blog — Migration guides
+- [Migrating from Parquet to
Hudi](https://hudi.apache.org/blog/2026/07/29/migrating-from-parquet-to-hudi):
In-place bootstrap vs full rewrite for converting raw Parquet datasets into
Hudi tables.
+- [Migrating from Hive to
Hudi](https://hudi.apache.org/blog/2026/07/30/migrating-from-hive-to-hudi):
Moving Hive tables to Hudi with bootstrap and catalog sync.
## Blog — Comparisons