You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@hudi.apache.org by yi...@apache.org on 2026/05/28 16:27:58 UTC

(hudi) branch asf-site updated: docs: Update documentation for new features in Hudi 1.2.0 (#18867)

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 e3a28fbdaf94 docs: Update documentation for new features in Hudi 1.2.0 (#18867)
e3a28fbdaf94 is described below

commit e3a28fbdaf946a6a5cc0f46229dac9d687f2aa99
Author: Y Ethan Guo <et...@gmail.com>
AuthorDate: Thu May 28 09:27:50 2026 -0700

    docs: Update documentation for new features in Hudi 1.2.0 (#18867)
---
 website/docs/ai_overview.md                    |   4 +-
 website/docs/azure_hoodie.md                   |  14 +-
 website/docs/blob_unstructured_data.md         |  45 ++++-
 website/docs/cleaning.md                       |  47 +++++
 website/docs/cli.md                            |  25 ++-
 website/docs/clustering.md                     |  59 +++++-
 website/docs/compaction.md                     |  13 +-
 website/docs/concurrency_control.md            |  42 ++++-
 website/docs/deployment.md                     |   4 +-
 website/docs/flink-quick-start-guide.md        |  33 ++--
 website/docs/flink_tuning.md                   |  47 +++++
 website/docs/hoodie_streaming_ingestion.md     | 106 ++++++++++-
 website/docs/ingestion_flink.md                | 244 +++++++++++++++++++++++--
 website/docs/key_generation.md                 |  31 +++-
 website/docs/lance_file_format.md              | 129 +++++++++++--
 website/docs/metadata.md                       |  31 ++++
 website/docs/metadata_indexing.md              |  33 +++-
 website/docs/metrics.md                        |  67 ++++---
 website/docs/overview.mdx                      |   4 +-
 website/docs/precommit_validator.md            |  67 +++++++
 website/docs/procedures.md                     |  60 +++++-
 website/docs/reading_tables_batch_reads.md     |  26 +++
 website/docs/reading_tables_streaming_reads.md |  44 +++++
 website/docs/sql_ddl.md                        |  55 +++++-
 website/docs/sql_queries.md                    |  33 +++-
 website/docs/syncing_aws_glue_data_catalog.md  |   4 +
 website/docs/syncing_metastore.md              |  41 +++++
 website/docs/variant_type.md                   |  26 ++-
 website/docs/vector_search.md                  |  42 ++++-
 website/docs/writing_data.md                   |  29 ++-
 30 files changed, 1293 insertions(+), 112 deletions(-)

diff --git a/website/docs/ai_overview.md b/website/docs/ai_overview.md
index 11c695d1bf9c..c28bcae6543a 100644
--- a/website/docs/ai_overview.md
+++ b/website/docs/ai_overview.md
@@ -18,7 +18,7 @@ Apache Hudi's AI-native capabilities bring this vision to life with four foundat
 
 ### VECTOR Type and Similarity Search
 
-Store high-dimensional embedding vectors as first-class column types and run approximate nearest neighbor (ANN)
+Store high-dimensional embedding vectors as first-class column types and run vector similarity
 search directly in Spark SQL.
 
 ```sql
@@ -99,7 +99,7 @@ query performance, while keeping the flexibility for everything else.
 Hudi's pluggable file format architecture supports **Lance**, a modern columnar format purpose-built for
 AI/ML workloads. Lance provides:
 
-- Efficient vector indexing and ANN search
+- Native vector column encoding (`FixedSizeList`) — no conversion overhead at the file-format layer
 - Fast random access for training data sampling
 - Optimized storage for high-dimensional arrays and nested structures
 
diff --git a/website/docs/azure_hoodie.md b/website/docs/azure_hoodie.md
index a22d66598141..2730f6f9411a 100644
--- a/website/docs/azure_hoodie.md
+++ b/website/docs/azure_hoodie.md
@@ -2,7 +2,7 @@
 title: Microsoft Azure
 keywords: [ hudi, hive, azure, spark, presto]
 summary: In this page, we go over how to configure Hudi with Azure filesystem.
-last_modified_at: 2020-05-25T19:00:57-04:00
+last_modified_at: 2026-05-27T00:00:00-00:00
 ---
 In this page, we explain how to use Hudi on Microsoft Azure.
 
@@ -49,6 +49,18 @@ This combination works out of the box. No extra config needed.
     .load("/mountpoint/hudi-tables/customer")
   ```
 
+## Concurrency Control
+
+As of Hudi 1.2.0, the storage-based lock provider supports Azure ADLS Gen2 (`abfs://`, `abfss://`) and Azure Blob Storage (`wasb://`, `wasbs://`) base paths for concurrency control. This allows multi-writer pipelines on Azure to use storage-native conditional writes for locking — without requiring external systems like ZooKeeper, or Hive Metastore.
+
+Add `hudi-azure-bundle` to your classpath and set:
+
+```properties
+hoodie.write.lock.provider=org.apache.hudi.client.transaction.lock.StorageBasedLockProvider
+```
+
+The lock client supports multiple Azure authentication methods (connection string, SAS token, managed identity, service principal, and `DefaultAzureCredential`). See [Concurrency Control — Azure Storage-Based Lock](concurrency_control.md#azure-storage-based-lock) for the full configuration reference and authentication precedence.
+
 ## Related Resources
 
 <h3>Blogs</h3>
diff --git a/website/docs/blob_unstructured_data.md b/website/docs/blob_unstructured_data.md
index 74acc799a230..253656565084 100644
--- a/website/docs/blob_unstructured_data.md
+++ b/website/docs/blob_unstructured_data.md
@@ -3,7 +3,7 @@ title: "Unstructured Data"
 keywords: [ hudi, blob, unstructured data, images, binary, pdf, audio, video, inline, out-of-line, read_blob]
 summary: "Store and query unstructured data (images, PDFs, audio, video) in Hudi tables using the BLOB type with inline or out-of-line storage"
 toc: true
-last_modified_at: 2026-04-25T00:00:00-00:00
+last_modified_at: 2026-05-27T00:00:00-00:00
 ---
 
 import Tabs from '@theme/Tabs';
@@ -72,6 +72,7 @@ schema = pa.schema([
             pa.field("external_path", pa.string()),
             pa.field("offset",        pa.int64()),
             pa.field("length",        pa.int64()),
+            pa.field("managed",       pa.bool_()),
         ])),
     ]), metadata={b"hudi_type": b"BLOB"}),
 ])
@@ -84,7 +85,7 @@ The BLOB internal structure is a struct with three fields:
   - `external_path` — file path for out-of-line data
   - `offset` — byte offset in the file (null means read from start)
   - `length` — byte length to read (null means read to end of file)
-  - `managed` — boolean indicating whether Hudi manages the external file
+  - `managed` — boolean. Only meaningful for `OUT_OF_LINE` blobs. Marks whether Hudi owns the lifecycle of the referenced external file. **Not consumed by the cleaner yet** — set the value to record intent, and a future cleaner implementation will use it: `true` → cleaner may delete the external file when the blob row is no longer referenced; `false` → cleaner will leave the external file in place.
 
 </TabItem>
 </Tabs>
@@ -112,7 +113,7 @@ INSERT INTO media_assets VALUES (
     named_struct(
         'type',      'INLINE',
         'data',      /* binary literal or column reference */,
-        'reference', CAST(NULL AS STRUCT<external_path: STRING, offset: BIGINT, length: BIGINT>)
+        'reference', CAST(NULL AS STRUCT<external_path: STRING, offset: BIGINT, length: BIGINT, managed: BOOLEAN>)
     )
 );
 ```
@@ -158,7 +159,8 @@ INSERT INTO media_assets VALUES (
         'reference', named_struct(
             'external_path', 's3://my-bucket/media/container_001.bin',
             'offset',        8388608,       -- byte offset in the container
-            'length',        1073741824     -- number of bytes
+            'length',        1073741824,    -- number of bytes
+            'managed',       false          -- intent flag; not consumed by the cleaner yet
         )
     )
 );
@@ -290,15 +292,44 @@ Out-of-line BLOBs keep the Hudi table footprint extremely small:
 
 | Property | Default | Description |
 |:---------|:--------|:------------|
-| `hoodie.read.blob.inline.mode` | `CONTENT` | Controls how INLINE BLOBs are read. `CONTENT` materializes raw bytes in the `data` column. `DESCRIPTOR` surfaces `(position, size)` coordinates rewritten as OUT_OF_LINE references. |
+| `hoodie.read.blob.inline.mode` | `DESCRIPTOR` | Controls how INLINE BLOBs are read. `DESCRIPTOR` (default) returns an out-of-line-shaped reference pointing at the in-file coordinates of the bytes — no bytes are materialized. `CONTENT` materializes the raw inline bytes directly in the `data` field on every read. |
 | `hoodie.blob.batching.max.gap.bytes` | `4096` | Maximum gap (in bytes) between consecutive byte ranges before they are merged into a single read. Larger values reduce I/O calls at the cost of reading some unused bytes. |
 | `hoodie.blob.batching.lookahead.size` | `50` | Number of rows to buffer for batch read detection. Larger values improve batching for sorted data but increase memory usage. |
 
 :::note
-DESCRIPTOR mode is only supported on Lance-backed tables. CONTENT mode is always used for internal
-operations (compaction, merge, log replay) regardless of this setting.
+`DESCRIPTOR` mode is the default for all storage formats including Lance. `CONTENT` mode is always
+used for internal operations (compaction, merge, log replay) regardless of this setting.
 :::
 
+:::caution Calling read_blob() on INLINE columns under DESCRIPTOR mode
+Under the default `DESCRIPTOR` mode, calling `read_blob()` on an INLINE BLOB column **throws** —
+the raw bytes are not materialized in the scan, so there is nothing for `read_blob()` to return.
+To read inline bytes with `read_blob()`, switch to `CONTENT` mode first:
+
+```sql
+SET hoodie.read.blob.inline.mode=CONTENT;
+SELECT asset_id, read_blob(content) AS raw_bytes
+FROM media_assets
+WHERE asset_id = 'asset_001';
+```
+
+This setting affects only INLINE columns — OUT_OF_LINE columns always fetch from the external path
+regardless of mode.
+:::
+
+## Metastore Sync
+
+When syncing BLOB column schemas to Hive or BigQuery, Hudi maps the BLOB struct to the target
+catalog's native struct type:
+
+| Catalog | BLOB representation |
+|:--------|:-------------------|
+| Hive | `STRUCT<type:STRING, data:BINARY, reference:STRUCT<external_path:STRING, offset:BIGINT, length:BIGINT, managed:BOOLEAN>>` |
+| BigQuery | Equivalent `STRUCT` fields |
+
+The raw binary payload is preserved in the struct representation, but `read_blob()` is a Spark SQL
+function and is not available in Hive or BigQuery directly.
+
 ## Best Practices
 
 1. **Choose the right mode** — Use inline for small, frequently-accessed objects. Use out-of-line for
diff --git a/website/docs/cleaning.md b/website/docs/cleaning.md
index c3498c8aa914..829d0d57cd6d 100644
--- a/website/docs/cleaning.md
+++ b/website/docs/cleaning.md
@@ -3,6 +3,7 @@ title: Cleaning
 toc: true
 toc_min_heading_level: 2
 toc_max_heading_level: 4
+last_modified_at: 2026-05-27T00:00:00-00:00
 ---
 ## Background
 Cleaning is a table service employed by Hudi to reclaim space occupied by older versions of data and keep storage costs 
@@ -50,6 +51,41 @@ Hudi cleaner currently supports the below cleaning policies to keep a certain nu
   be retained are cleaned. Currently you can configure by parameter [`hoodie.clean.hours.retained`](https://hudi.apache.org/docs/configurations/#hoodiecleanerhoursretained).
   The corresponding Flink related config is [`clean.retain_hours`](https://hudi.apache.org/docs/configurations/#cleanretain_hours).
 
+#### Empty Clean Commits for Append-Only Tables
+
+Append-only tables never accumulate updates, so the cleaner's `earliest_commit_to_retain` pointer never advances —
+causing the cleaner to scan the full table history on every run. Hudi 1.2.0 introduced periodic _empty clean commits_
+to advance this pointer even when there is nothing to delete.
+
+| Config Name | Default | Description |
+|---|---|---|
+| `hoodie.write.empty.clean.interval.hours` | `-1` (disabled) | Interval in hours at which an empty clean commit is created. `-1` disables the feature. Must be `-1` or `>= 1`. When enabled, the cleaner advances `earliest_commit_to_retain` so that subsequent clean plans only scan partitions modified after the last empty clean's pointer. |
+
+#### Capping the Number of Commits Cleaned per Run
+
+Since 1.2.0, you can limit how many commits are cleaned in a single clean run, which is useful for controlling job
+duration on tables that have fallen significantly behind on cleaning.
+
+| Config Name | Default | Description |
+|---|---|---|
+| `hoodie.clean.max.commits.to.clean` | `Long.MAX_VALUE` (unbounded) | Maximum number of commits cleaned in a single clean commit. Applicable when the cleaning policy is `KEEP_LATEST_COMMITS` or `KEEP_LATEST_BY_HOURS`. Must be `>= 1`. |
+
+#### Full-Clean Partition Filtering
+
+When incremental cleaning is disabled (`hoodie.clean.incremental.enabled=false`), the cleaner scans every partition on
+every run. For very large tables this can cause OOM during planning. Hudi 1.2.0 added two configs to restrict which
+partitions are examined.
+
+:::note
+Both configs require `hoodie.clean.incremental.enabled=false`. If both are set, `hoodie.clean.partition.filter.selected`
+takes precedence over the regex.
+:::
+
+| Config Name | Default | Description |
+|---|---|---|
+| `hoodie.clean.partition.filter.regex` | (none) | Java regex pattern; only partitions whose path matches are cleaned. |
+| `hoodie.clean.partition.filter.selected` | (none) | Comma-separated list of partition paths to clean; takes precedence over the regex when both are set. |
+
 ### Configs
 For details about all possible configurations and their default values see the [configuration docs](https://hudi.apache.org/docs/next/configurations/#Clean-Configs).
 For Flink related configs refer [here](https://hudi.apache.org/docs/next/configurations/#FLINK_SQL).
@@ -76,6 +112,17 @@ hoodie.clean.async=true
 
 For Flink based writing, this is the default mode of cleaning. Please refer to [`clean.async.enabled`](https://hudi.apache.org/docs/configurations/#cleanasyncenabled) for details.
 
+#### Pre-Write Cleaner Policy
+
+By default the cleaner runs _after_ a write commits. Hudi 1.2.0 introduced `hoodie.prewrite.cleaner.policy`, which
+lets you force a clean (or rollback of failed writes) _before_ each write begins. This is useful in multi-writer
+deployments where you want a deterministic table state before every write — see [concurrency control](concurrency_control.md)
+for related multi-writer configuration.
+
+| Config Name | Default | Description |
+|---|---|---|
+| `hoodie.prewrite.cleaner.policy` | `NONE` | Pre-write cleaning action. `NONE`: no pre-write action (default). `CLEAN`: run a clean pass before each write — this also rolls back failed writes as part of the clean. `ROLLBACK_FAILED_WRITES`: only roll back any failed writes before each write, without running a full clean. |
+
 #### Run independently
 Hoodie Cleaner can also be run as a separate process. Following is the command for running the cleaner independently:
 ```
diff --git a/website/docs/cli.md b/website/docs/cli.md
index a29ffdfc2637..ddb8132d3cf3 100644
--- a/website/docs/cli.md
+++ b/website/docs/cli.md
@@ -1,7 +1,7 @@
 ---
 title: CLI
 keywords: [hudi, cli]
-last_modified_at: 2021-08-18T15:59:57-04:00
+last_modified_at: 2026-05-27T00:00:00-00:00
 ---
 
 ### Local set up
@@ -340,6 +340,13 @@ $ hdfs dfs -ls /app/uber/trips/.hoodie/*.inflight
 -rw-r--r--   3 vinoth supergroup     321984 2016-10-05 23:18 /app/uber/trips/.hoodie/20161005225920.inflight
 ```
 
+To list all inflight and requested instants that have been running longer than a specified number of minutes, use `commits show_inflights`:
+
+```shell
+hudi:trips->commits show_inflights --lookbackInMins 30
+```
+
+This lists every inflight or requested instant whose requested timestamp is older than 30 minutes, showing the commit time, action type, and current state. This is useful for detecting hung or stuck writes. The `--lookbackInMins` option defaults to `0` (returns all inflight/requested instants).
 
 ### Drilling Down to a specific Commit
 
@@ -675,6 +682,22 @@ corresponding to the library release version is used:
 upgrade table
 ```
 
+### Record Index Lookup
+
+To look up a record's file location via the Record Level Index (RLI) stored in the Metadata Table:
+
+```shell
+hudi:trips->metadata lookup-record-index --record_key <key>
+```
+
+For a partitioned (non-global) RLI, the partition path is required:
+
+```shell
+hudi:trips->metadata lookup-record-index --record_key <key> --partition_path <partition>
+```
+
+The `--partition_path` argument is optional for a global RLI (where record keys are unique across all partitions) and required for a partitioned RLI. If `--partition_path` is omitted for a partitioned RLI, the command will return an error. The output columns are `Record key`, `Partition path`, `File Id`, and `Instant time`.
+
 ### Change Hudi Table Type
 There are cases we want to change the hudi table type. For example, change COW table to MOR for more efficient and 
 lower latency ingestion; change MOR to COW for better read performance and compatibility with downstream engines.
diff --git a/website/docs/clustering.md b/website/docs/clustering.md
index 7442967b6505..7426bf9e2dcc 100644
--- a/website/docs/clustering.md
+++ b/website/docs/clustering.md
@@ -2,7 +2,7 @@
 title: Clustering
 summary: "In this page, we describe async compaction in Hudi."
 toc: true
-last_modified_at: 2025-11-24T02:44:48
+last_modified_at: 2026-05-27T00:00:00-00:00
 ---
 
 ## Background
@@ -134,6 +134,47 @@ dynamically expanding the buckets for bucket index datasets.
 :::note The latter two strategies are applicable only for the Spark engine.
 :::
 
+#### CommitBasedClusteringPlanStrategy
+
+Hudi 1.2.0 introduced `org.apache.hudi.table.action.cluster.strategy.CommitBasedClusteringPlanStrategy`, a plan
+strategy that schedules clustering based on commit patterns rather than just file size. It groups file slices by the
+commits that produced them, making it easier to cluster data written in specific time windows or under specific commit
+criteria.
+
+| Config Name | Default | Description |
+|---|---|---|
+| `hoodie.clustering.plan.strategy.class` | `SparkSizeBasedClusteringPlanStrategy` | Set to `org.apache.hudi.table.action.cluster.strategy.CommitBasedClusteringPlanStrategy` to use commit-based planning. |
+| `hoodie.clustering.plan.strategy.earliest.commit.to.cluster` | (none) | Earliest commit time (exclusive) to start clustering from. Only commits after this instant are considered. Useful for incrementally clustering new data while skipping already-clustered history. |
+
+#### SparkStreamCopyClusteringPlanStrategy
+
+Available since Hudi 1.2.0, `org.apache.hudi.client.clustering.plan.strategy.SparkStreamCopyClusteringPlanStrategy`
+is a Spark-only plan strategy that performs binary file stitching (byte-level copy) rather than re-reading and
+re-writing records. This can be significantly faster when the goal is simply to coalesce small files and sort order is
+not required. It is paired with
+`org.apache.hudi.client.clustering.run.strategy.SparkStreamCopyClusteringExecutionStrategy`.
+
+#### Single-Group Clustering Control
+
+| Config Name | Default | Description |
+|---|---|---|
+| `hoodie.clustering.plan.strategy.single.group.clustering.enabled` | `true` | Whether to generate a clustering plan when only one file group is eligible. Set to `false` to skip clustering when there is nothing meaningful to consolidate (i.e., the partition already has a single file group). |
+
+#### File-Slice Sort Order in Clustering Plan Generation
+
+Since 1.2.0, the order in which file slices are packed into clustering groups is configurable, giving more control over
+which files are colocated and how groups are filled.
+
+| Config Name | Default | Description |
+|---|---|---|
+| `hoodie.clustering.plan.strategy.file.slices.sort.by` | `SIZE` | Comma-separated list of fields used to sort file slices when packing them into clustering groups within a partition. `SIZE`: sort by file size descending (largest first). `INSTANT_TIME`: sort by commit time ascending (oldest files first). Example: `INSTANT_TIME,SIZE` sorts by commit time then by size. |
+
+#### Driver-Side Plan Generation
+
+| Config Name | Default | Description |
+|---|---|---|
+| `hoodie.clustering.plan.generation.use.local.engine.context` | `false` | When enabled, clustering group computation runs on the driver (local engine context) instead of being distributed across executors. Enable when there are only a few partitions with many files, where driver-local computation is more resource-efficient than allocating executor slots. |
+
 ### Execution Strategy
 
 After building the clustering groups in the planning phase, Hudi applies execution strategy, for each group, primarily
@@ -251,6 +292,22 @@ In addition to the basic mode options, HoodieClusteringJob supports the followin
 These retry options are only effective when using `--mode scheduleAndExecute`. The `--retry-last-failed-job` option requires `--job-max-processing-time-ms` to be set to a positive value to detect stale inflight instants.
 :::
 
+#### Automatic Expiration of Stale Clustering Instants
+
+When a clustering job is scheduled but never successfully executed (e.g., due to a driver failure), the inflight
+`replacecommit` instant blocks future clustering runs. Hudi 1.2.0 adds automatic expiration of such stale clustering
+instants, complementing the manual retry options above.
+
+:::note
+Expired clustering plan cleanup requires `hoodie.clean.failed.writes.policy=LAZY`. With LAZY cleaning, the rollback of
+failed writes (triggered on the next write) also rolls back expired clustering instants.
+:::
+
+| Config Name | Default | Description |
+|---|---|---|
+| `hoodie.clustering.enable.expirations` | `false` | When enabled, rollback of failed writes (under LAZY cleaning) also rolls back clustering `replacecommit` instants whose heartbeat has expired. Clustering jobs record a heartbeat before scheduling so other writers can detect stale attempts. |
+| `hoodie.clustering.expiration.threshold.mins` | `60` | A clustering instant is not considered expired unless its creation time is at least this many minutes old. Acts as a guardrail to avoid rolling back clustering attempts that are still in progress. |
+
 Note that to run this job while the original writer is still running, please enable multi-writing:
 
 ```properties
diff --git a/website/docs/compaction.md b/website/docs/compaction.md
index 89b9214f0bd9..bf1dee7c12d6 100644
--- a/website/docs/compaction.md
+++ b/website/docs/compaction.md
@@ -4,7 +4,7 @@ summary: "In this page, we describe async compaction in Hudi."
 toc: true
 toc_min_heading_level: 2
 toc_max_heading_level: 4
-last_modified_at: 2025-11-24T02:44:48
+last_modified_at: 2026-05-27T00:00:00-00:00
 ---
 ## Background
 
@@ -104,6 +104,17 @@ BoundedPartitionAwareCompactionStrategy</li></ul>
 Please refer to [advanced configs](https://hudi.apache.org/docs/next/configurations#Compaction-Configs) for more details.
 :::
 
+#### Metadata Table Compaction Trigger Strategy
+
+Available since Hudi 1.2.0, the metadata table (MDT) supports the same set of compaction trigger strategies as the
+data table, plus a time-based option.
+
+| Config Name | Default | Description |
+|---|---|---|
+| `hoodie.metadata.compact.trigger.strategy` | `NUM_COMMITS` | Trigger strategy for MDT compaction. Accepts the same values as `hoodie.compact.inline.trigger.strategy`: `NUM_COMMITS`, `NUM_COMMITS_AFTER_LAST_REQUEST`, `TIME_ELAPSED`, `NUM_AND_TIME`, `NUM_OR_TIME`. |
+| `hoodie.metadata.compact.max.delta.commits` | `10` | Number of delta commits after the last MDT compaction before a new one is scheduled (for `NUM_COMMITS`-based strategies). |
+| `hoodie.metadata.compact.max.delta.seconds` | `7200` | Elapsed seconds after the last MDT compaction before scheduling a new one. Takes effect only for `TIME_ELAPSED`, `NUM_AND_TIME`, and `NUM_OR_TIME` strategies. |
+
 ## Ways to trigger Compaction
 
 ### Inline
diff --git a/website/docs/concurrency_control.md b/website/docs/concurrency_control.md
index d6231aca79b7..2056acfca64c 100644
--- a/website/docs/concurrency_control.md
+++ b/website/docs/concurrency_control.md
@@ -4,7 +4,7 @@ summary: On this page, we discuss how to perform concurrent writes to Hudi table
 toc: true
 toc_min_heading_level: 2
 toc_max_heading_level: 4
-last_modified_at: 2025-11-23T14:20:00
+last_modified_at: 2026-05-27T00:00:00-00:00
 ---
 
 Concurrency control defines how different writers, readers, and table services coordinate access to a Hudi table. Hudi ensures atomic writes by publishing commits atomically to the timeline, stamped with an instant time that denotes when the action is deemed to have occurred. Unlike general-purpose file version control, Hudi draws a clear distinction between writer processes that issue [write operations](write_operations.md), table services that (re)write data/metadata to optimize or per [...]
@@ -47,6 +47,7 @@ Add the corresponding cloud bundle to your classpath:
 
 * For S3: `hudi-aws-bundle`
 * For GCS: `hudi-gcp-bundle`
+* For Azure (`abfs://`, `abfss://`, `wasb://`, `wasbs://`): `hudi-azure-bundle`
 
 Set this configuration:
 
@@ -54,7 +55,7 @@ Set this configuration:
 hoodie.write.lock.provider=org.apache.hudi.client.transaction.lock.StorageBasedLockProvider
 ```
 
-Supported for S3 and GCS (additional systems planned). This cloud-native design works directly with storage features, simplifying large-scale cloud operations.
+Supported for S3, GCS, and Azure ADLS Gen2 / Azure Blob Storage. This cloud-native design works directly with storage features, simplifying large-scale cloud operations.
 
 Optional tuning configurations:
 
@@ -63,6 +64,27 @@ Optional tuning configurations:
 | hoodie.write.lock.storage.validity.timeout.secs | 300 (Optional) | Validity period (seconds) for each new lock. The provider renews its lock until the lease extends or timeout occurs.<br /><br />`Config Param: STORAGE_BASED_LOCK_VALIDITY_TIMEOUT_SECS`<br />`Since Version: 1.0.2` |
 | hoodie.write.lock.storage.renew.interval.secs   | 30 (Optional)  | Interval (seconds) between renewal attempts.<br /><br />`Config Param: STORAGE_BASED_LOCK_RENEW_INTERVAL_SECS`<br />`Since Version: 1.0.2`                                                                           |
 
+#### Azure Storage-Based Lock
+
+Authentication is resolved in the following precedence order:
+
+| Priority | Config Key | Description |
+|----------|------------|-------------|
+| 1 (highest) | `hoodie.write.lock.azure.connection.string` | Azure Storage connection string |
+| 2 | `hoodie.write.lock.azure.sas.token` | SAS token (not recommended for production by Azure) |
+| 3 | `hoodie.write.lock.azure.managed.identity.client.id` | Client ID of a user-assigned managed identity (`ManagedIdentityCredential`) |
+| 4 | `hoodie.write.lock.azure.client.tenant.id` + `.client.id` + `.client.secret` | Service principal via `ClientSecretCredential` — all three must be set |
+| 5 (lowest) | _(none)_ | `DefaultAzureCredential` chain (system-assigned managed identity, environment variables, etc.) |
+
+Example configuration for service-principal authentication:
+
+```properties
+hoodie.write.lock.provider=org.apache.hudi.client.transaction.lock.StorageBasedLockProvider
+hoodie.write.lock.azure.client.tenant.id=<your-tenant-id>
+hoodie.write.lock.azure.client.id=<your-app-client-id>
+hoodie.write.lock.azure.client.secret=<your-client-secret>
+```
+
 ### Zookeeper-Based Lock Provider
 
 ```properties
@@ -359,6 +381,22 @@ hoodie.write.lock.client.num_retries
 
 *Setting the right values for these depends on a case by case basis; some defaults have been provided for general cases.*
 
+## Pre-Write Cleaner Policy
+
+When running multi-writer pipelines, failed writes can accumulate on storage if a writer crashes before a clean cycle runs. Hudi 1.2.0 introduces `hoodie.prewrite.cleaner.policy` to proactively handle this at write startup:
+
+| Config Key | Default | Description |
+|---|---|---|
+| `hoodie.prewrite.cleaner.policy` | `NONE` | Policy applied before starting a new ingestion write commit. `NONE`: no pre-write action (default). `CLEAN`: force a clean table service call (also rolls back failed writes). `ROLLBACK_FAILED_WRITES`: only roll back failed writes without running a full clean. |
+
+This is useful when a writer is perpetually crashing before completing a `CLEAN`. See [Cleaning](cleaning.md) for the full list of cleaning configurations.
+
+## Lock Audit Logging and Diagnostics
+
+The storage-based lock provider supports optional audit logging of lock operations. When enabled, a `.hoodie/lock/audit_enabled.json` marker is written to the table base path and lock acquisition/release events are recorded for post-hoc debugging.
+
+For ZooKeeper-based locking, the ZK lock node now stores the Spark application ID of the writer holding the lock, making it easier to correlate lock holders with running Spark jobs in cluster UIs.
+
 ## Caveats
 
 If you are using the `WriteClient` API, please note that multiple writes to the table need to be initiated from 2 different instances of the write client.
diff --git a/website/docs/deployment.md b/website/docs/deployment.md
index 51f92e40d407..62497285a333 100644
--- a/website/docs/deployment.md
+++ b/website/docs/deployment.md
@@ -3,7 +3,7 @@ title: Deployment
 keywords: [ hudi, administration, operation, devops, deployment]
 summary: This section offers an overview of tools available to operate an ecosystem of Hudi
 toc: true
-last_modified_at: 2019-12-30T15:59:57-04:00
+last_modified_at: 2026-05-27T00:00:00-00:00
 ---
 
 This section provides all the help you need to deploy and operate Hudi tables at scale.
@@ -32,6 +32,8 @@ from varied sources such as DFS, Kafka and DB Changelogs and ingest them to hudi
 To use Hudi Streamer in Spark, the `hudi-utilities-slim-bundle` and Hudi Spark bundle are required, by adding
 `--packages org.apache.hudi:hudi-utilities-slim-bundle_2.12:1.0.1,org.apache.hudi:hudi-spark3.5-bundle_2.12:1.0.1` to the `spark-submit` command.
 
+Pick the Spark bundle that matches your Spark runtime — for example, `hudi-spark3.3-bundle_2.12`, `hudi-spark3.4-bundle_2.12`, `hudi-spark3.5-bundle_2.12` (Scala 2.12 or 2.13), `hudi-spark3.5-bundle_2.13`, `hudi-spark4.0-bundle_2.13`, or `hudi-spark4.1-bundle_2.13`. Spark 4.0 and 4.1 require Java 17 or later at runtime; Spark 3.x runs on Java 8 or later.
+
 - **Run Once Mode** : In this mode, Hudi Streamer performs one ingestion round which includes incrementally pulling events from upstream sources and ingesting them to hudi table. Background operations like cleaning old file versions and archiving hoodie timeline are automatically executed as part of the run. For Merge-On-Read tables, Compaction is also run inline as part of ingestion unless disabled by passing the flag "--disable-compaction". By default, Compaction is run inline for ever [...]
 
 Here is an example invocation for reading from kafka topic in a single-run mode and writing to Merge On Read table type in a yarn cluster.
diff --git a/website/docs/flink-quick-start-guide.md b/website/docs/flink-quick-start-guide.md
index 2a04dc85abb2..23ef5efbe3cc 100644
--- a/website/docs/flink-quick-start-guide.md
+++ b/website/docs/flink-quick-start-guide.md
@@ -1,7 +1,7 @@
 ---
 title: "Flink Quick Start"
 toc: true
-last_modified_at: 2025-11-22T14:30:00+08:00
+last_modified_at: 2026-05-27T00:00:00-00:00
 ---
 import Tabs from '@theme/Tabs';
 import TabItem from '@theme/TabItem';
@@ -12,12 +12,13 @@ This page introduces Flink–Hudi integration and demonstrates how Flink brings
 
 ### Flink Support Matrix
 
-| Hudi   | Supported Flink versions                                               |
-| :----- | :--------------------------------------------------------------------- |
-| 1.1.x  | 1.17.x, 1.18.x, 1.19.x, 1.20.x (default build), 2.0.x                  |
-| 1.0.x  | 1.14.x, 1.15.x, 1.16.x, 1.17.x, 1.18.x, 1.19.x, 1.20.x (default build) |
-| 0.15.x | 1.14.x, 1.15.x, 1.16.x, 1.17.x, 1.18.x                                 |
-| 0.14.x | 1.13.x, 1.14.x, 1.15.x, 1.16.x, 1.17.x                                 |
+| Hudi   | Supported Flink versions                                                      |
+| :----- | :---------------------------------------------------------------------------- |
+| 1.2.x  | 1.17.x, 1.18.x, 1.19.x, 1.20.x (default build), 2.0.x, 2.1.x                |
+| 1.1.x  | 1.17.x, 1.18.x, 1.19.x, 1.20.x (default build), 2.0.x                        |
+| 1.0.x  | 1.14.x, 1.15.x, 1.16.x, 1.17.x, 1.18.x, 1.19.x, 1.20.x (default build)      |
+| 0.15.x | 1.14.x, 1.15.x, 1.16.x, 1.17.x, 1.18.x                                       |
+| 0.14.x | 1.13.x, 1.14.x, 1.15.x, 1.16.x, 1.17.x                                       |
 
 ### Download Flink and Start Flink cluster
 
@@ -62,9 +63,9 @@ You can build the jar manually under path `hudi-source-dir/packaging/hudi-flink-
 Now start the SQL CLI:
 
 ```bash
-# For Flink versions: 1.17-1.20, 2.0
-export FLINK_VERSION=1.20 
-export HUDI_VERSION=1.1.1
+# Supported Flink versions for Hudi 1.2.x: 1.17, 1.18, 1.19, 1.20 (default build), 2.0, 2.1
+export FLINK_VERSION=1.20
+export HUDI_VERSION=1.2.0
 wget https://repo1.maven.org/maven2/org/apache/hudi/hudi-flink${FLINK_VERSION}-bundle/${HUDI_VERSION}/hudi-flink${FLINK_VERSION}-bundle-${HUDI_VERSION}.jar -P /tmp/
 ./bin/sql-client.sh embedded -j /tmp/hudi-flink${FLINK_VERSION}-bundle-${HUDI_VERSION}.jar shell
 ```
@@ -77,11 +78,11 @@ The SQL CLI only executes the SQL line by line.
 
 Please add the desired dependency to your project:
 ```xml
-<!-- For Flink versions 1.17-1.20, 2.0-->
+<!-- Supported Flink versions for Hudi 1.2.x: 1.17, 1.18, 1.19, 1.20 (default build), 2.0, 2.1 -->
 <properties>
-    <flink.version>1.20.0</flink.version>
+    <flink.version>1.20.1</flink.version>
     <flink.binary.version>1.20</flink.binary.version>
-    <hudi.version>1.1.1</hudi.version>
+    <hudi.version>1.2.0</hudi.version>
 </properties>
 <dependency>
     <groupId>org.apache.hudi</groupId>
@@ -446,9 +447,9 @@ feature is that it lets you author streaming pipelines on streaming or batch dat
 
 - **Quick Start**: Read the Quick Start section above to get started quickly with the Flink SQL client to write to (and read from) Hudi.
 - **Configuration**: For [Global Configuration](flink_tuning.md#global-configurations), set up through `$FLINK_HOME/conf/flink-conf.yaml`. For per-job configuration, set up through [Table Option](flink_tuning.md#table-options).
-- **Writing Data** : Flink supports different modes for writing, such as [CDC Ingestion](ingestion_flink.md#cdc-ingestion), [Bulk Insert](ingestion_flink.md#bulk-insert), [Index Bootstrap](ingestion_flink.md#index-bootstrap), [Changelog Mode](ingestion_flink.md#changelog-mode) and [Append Mode](ingestion_flink.md#append-mode). Flink also supports multiple streaming writers with [non-blocking concurrency control](sql_dml.md#non-blocking-concurrency-control-experimental).
-- **Reading Data** : Flink supports different modes for reading, such as [Streaming Query](sql_queries.md#streaming-query) and [Incremental Query](sql_queries.md#incremental-query).
-- **Tuning**: For write/read tasks, this guide provides some tuning suggestions, such as [Memory Optimization](flink_tuning.md#memory-optimization) and [Write Rate Limit](flink_tuning.md#write-rate-limit).
+- **Writing Data** : Flink supports different modes for writing, such as [CDC Ingestion](ingestion_flink.md#cdc-ingestion), [Bulk Insert](ingestion_flink.md#bulk-insert), [Index Bootstrap](ingestion_flink.md#index-bootstrap), [Changelog Mode](ingestion_flink.md#changelog-mode) and [Append Mode](ingestion_flink.md#append-mode). For high-throughput append pipelines, choose an [append write buffer mode](ingestion_flink.md#append-write-buffer). For upsert workloads at scale, use [Record-Leve [...]
+- **Reading Data** : Flink supports different modes for reading, such as [Streaming Query](sql_queries.md#streaming-query) and [Incremental Query](sql_queries.md#incremental-query). For improved push-down and resumable reads, see [Flink Source V2](ingestion_flink.md#flink-source-v2). For dimension-table joins, use [lookup join](ingestion_flink.md#lookup-join) with an optional off-heap RocksDB cache.
+- **Tuning**: For write/read tasks, this guide provides some tuning suggestions, such as [Memory Optimization](flink_tuning.md#memory-optimization), the [Managed-Memory Write Buffer](flink_tuning.md#managed-memory-write-buffer), and [Write Rate Limit](flink_tuning.md#write-rate-limit).
 - **Optimization**: Offline compaction is supported: [Offline Compaction](compaction.md#flink-offline-compaction).
 - **Query Engines**: Besides Flink, many other engines are integrated: [Hive Query](syncing_metastore.md#flink-setup), [Presto Query](sql_queries.md#presto).
 - **Catalog**: A Hudi‑specific catalog is supported: [Hudi Catalog](sql_ddl/#create-catalog).
diff --git a/website/docs/flink_tuning.md b/website/docs/flink_tuning.md
index 28e70e48b1f2..bd08b4be0fba 100644
--- a/website/docs/flink_tuning.md
+++ b/website/docs/flink_tuning.md
@@ -115,3 +115,50 @@ the `write.rate.limit` option can be turned on to ensure smooth writing.
 |  Option Name  | Required | Default | Remarks |
 |  -----------  | -------  | ------- | ------- |
 | `write.rate.limit` | `false` | `0` | Turn off by default |
+
+## Managed-Memory Write Buffer
+
+By default, the Flink write buffer uses JVM heap memory (`ON_HEAP`). In containerized environments where heap memory is tightly budgeted, you can switch to Flink's managed (off-heap) memory pool to reduce GC pressure and avoid OOM errors.
+
+:::note
+When using `MANAGED` memory type, ensure `taskmanager.memory.managed.size` is configured sufficiently in `flink-conf.yaml`.
+:::
+
+|  Option Name  | Description | Default | Remarks |
+|  -----------  | -------  | ------- | ------- |
+| `write.buffer.memory.type` | Memory type for the write buffer: `ON_HEAP` (default, uses JVM heap) or `MANAGED` (uses Flink managed off-heap memory) | `ON_HEAP` | Switch to `MANAGED` to avoid OOM in memory-constrained deployments |
+| `write.memory.segment.page.size` | Page size in bytes for memory segments used in the write buffer | `32768` (32 KB) | Tune for workload characteristics; larger pages reduce overhead for large records |
+
+## Disruptor Buffer Tuning
+
+When `write.buffer.type=DISRUPTOR` is set in the table options (see [Append Write Buffer](ingestion_flink.md#append-write-buffer)), the following tuning options control the Disruptor ring buffer:
+
+|  Option Name  | Description | Default | Remarks |
+|  -----------  | -------  | ------- | ------- |
+| `write.buffer.disruptor.ring.size` | Size of the Disruptor ring buffer (must be a power of 2) | `16384` | Larger values absorb write bursts but consume more heap memory |
+| `write.buffer.disruptor.wait.strategy` | Wait strategy for the Disruptor consumer: `BLOCKING_WAIT` (default), `SLEEPING_WAIT`, `YIELDING_WAIT`, `BUSY_SPIN_WAIT` | `BLOCKING_WAIT` | `BLOCKING_WAIT` is safest for containerized environments; `BUSY_SPIN_WAIT` offers lowest latency at the cost of a dedicated CPU core |
+
+## Timeline-Server-Based Markers
+
+As of Hudi 1.2.0, Flink writers support `TIMELINE_SERVER_BASED` marker type (`hoodie.write.markers.type=TIMELINE_SERVER_BASED`). This is recommended over `DIRECT` markers on object stores (S3, GCS, ADLS) where the high cost of directory listings makes `DIRECT` markers slow.
+
+```sql
+CREATE TABLE my_table (...)
+WITH (
+  'connector' = 'hudi',
+  'path' = 's3a://my-bucket/my-table',
+  'hoodie.write.markers.type' = 'TIMELINE_SERVER_BASED'
+  -- other options
+);
+```
+
+## Source V2 Read-Lag Metrics
+
+When [Source V2](ingestion_flink.md#flink-source-v2) is enabled (`read.source-v2.enabled=true`), the following read-lag metrics are emitted to help monitor streaming pipeline health:
+
+| Metric | Description |
+|--------|-------------|
+| `issuedInstantDelay` | Time elapsed (ms) between when a new instant was written and when the source issued it for reading |
+| `sourceReaderIdleTime` | Time (ms) the source reader has been idle (no new splits assigned) |
+
+These metrics are exposed through Flink's standard metrics system and can be forwarded to Prometheus, JMX, or other reporters.
diff --git a/website/docs/hoodie_streaming_ingestion.md b/website/docs/hoodie_streaming_ingestion.md
index 286d5765c751..1fb282a5d718 100644
--- a/website/docs/hoodie_streaming_ingestion.md
+++ b/website/docs/hoodie_streaming_ingestion.md
@@ -146,7 +146,9 @@ Usage: <main class> [options]
       Default: 0
     --op
       Takes one of these values : UPSERT (default), INSERT, BULK_INSERT,
-      INSERT_OVERWRITE, INSERT_OVERWRITE_TABLE, DELETE_PARTITION
+      INSERT_OVERWRITE, INSERT_OVERWRITE_TABLE, DELETE_PARTITION, DELETE
+      (DELETE extracts HoodieKeys from source records and deletes the
+      corresponding records from the table.)
       Default: UPSERT
       Possible Values: [INSERT, INSERT_PREPPED, UPSERT, UPSERT_PREPPED, BULK_INSERT, BULK_INSERT_PREPPED, DELETE, DELETE_PREPPED, BOOTSTRAP, INSERT_OVERWRITE, CLUSTER, DELETE_PARTITION, INSERT_OVERWRITE_TABLE, COMPACT, INDEX, ALTER_SCHEMA, LOG_COMPACT, UNKNOWN]
     --payload-class
@@ -503,6 +505,77 @@ Check out [Kafka source config](https://hudi.apache.org/docs/configurations#Kafk
 Hudi Streamer also supports ingesting from Apache Pulsar via `org.apache.hudi.utilities.sources.PulsarSource`.
 Check out [Pulsar source config](https://hudi.apache.org/docs/configurations#Pulsar-Source-Configs) for more details.
 
+#### Amazon Kinesis
+
+Use the `JsonKinesisSource` (`org.apache.hudi.utilities.sources.JsonKinesisSource`) to ingest JSON records from an AWS Kinesis Data Stream into a Hudi table. It reads from every shard in parallel, tracks per-shard progress in the Hudi Streamer checkpoint, automatically handles shard splits and merges, and de-aggregates records produced by the Kinesis Producer Library (KPL).
+
+##### Common configuration
+
+All keys use the prefix `hoodie.streamer.source.kinesis.`. The settings most users need:
+
+| Config key | Default | Description |
+|---|---|---|
+| `hoodie.streamer.source.kinesis.stream.name` | (required) | Kinesis Data Streams stream name. |
+| `hoodie.streamer.source.kinesis.region` | (required) | AWS region for the stream (e.g., `us-east-1`). |
+| `hoodie.streamer.source.kinesis.starting.position` | `LATEST` | Where to start when no checkpoint exists yet. `LATEST` starts at the tip of each shard; `EARLIEST` replays from `TRIM_HORIZON`. |
+| `hoodie.streamer.source.kinesis.max.events` | `5000000` | Maximum number of records read per batch across all shards. Tune to control batch size. |
+| `hoodie.streamer.source.kinesis.partitions` | `0` | Spark partitions to use when reading. `0` means one Spark partition per Kinesis shard. Set a positive value to repartition for downstream parallelism. |
+
+For credentials, the source uses the default AWS credential chain (instance profile, environment variables, etc.). Authentication for custom endpoints (e.g., LocalStack), API-level rate limiting, and retry tuning are also available — see the [configurations reference](configurations.md) for the full list of `hoodie.streamer.source.kinesis.*` keys.
+
+##### Checkpoint format
+
+Hudi Streamer persists Kinesis progress as a single checkpoint string on the timeline. Each batch advances the checkpoint to the last record successfully read from every shard, so a failed batch can be retried without skipping or duplicating records.
+
+The checkpoint encodes per-shard state in plain text:
+
+```
+streamName,shardId:value,shardId:value,...
+```
+
+Each `value` is one of:
+
+- `lastSeq` — last sequence number consumed from an open shard.
+- `lastSeq@arrivalTime` — same, with the record's approximate arrival time (epoch millis) for lag/observability.
+- `lastSeq|endSeq` — closed shard. `endSeq` is the shard's final sequence number, used to detect data loss if the shard expires before being fully consumed.
+- `lastSeq@arrivalTime|endSeq` — closed shard with arrival time.
+
+Example (sequence numbers abbreviated; Kinesis assigns each shard a 56-digit decimal sequence number):
+
+```
+my-stream,shardId-000000000000:49590…88898,shardId-000000000001:49590…96306
+```
+
+You don't need to construct or parse this string yourself — it is read and updated automatically by the source — but it's useful for debugging, manual checkpoint resets, or comparing progress across shards.
+
+##### Minimal spark-submit example
+
+```properties
+# kinesis-source.properties
+hoodie.streamer.source.kinesis.stream.name=my-stream
+hoodie.streamer.source.kinesis.region=us-east-1
+hoodie.streamer.source.kinesis.starting.position=LATEST
+
+# Standard Hudi write / key-gen configs
+hoodie.datasource.write.recordkey.field=id
+hoodie.datasource.write.partitionpath.field=event_date
+hoodie.table.ordering.fields=ts
+```
+
+```bash
+spark-submit \
+  --packages org.apache.hudi:hudi-utilities-slim-bundle_2.12:1.2.0,org.apache.hudi:hudi-spark3.5-bundle_2.12:1.2.0 \
+  --class org.apache.hudi.utilities.streamer.HoodieStreamer \
+  hudi-utilities-slim-bundle-*.jar \
+  --props kinesis-source.properties \
+  --source-class org.apache.hudi.utilities.sources.JsonKinesisSource \
+  --table-type COPY_ON_WRITE \
+  --target-base-path s3://my-bucket/hudi/my-table \
+  --target-table my_db.my_table \
+  --op UPSERT \
+  --continuous
+```
+
 #### Cloud storage event sources
 AWS S3 storage provides an event notification service which will post notifications when certain events happen in your S3 bucket: 
 https://docs.aws.amazon.com/AmazonS3/latest/userguide/NotificationHowTo.html
@@ -636,3 +709,34 @@ to how you run Hudi Streamer.
 ```
 
 For detailed information on how to configure and use `HoodieMultiTableStreamer`, please refer [blog section](/blog/2020/08/22/ingest-multiple-tables-using-hudi).
+
+## On-Demand Hive Sync (HudiHiveSyncJob)
+
+`org.apache.hudi.utilities.HudiHiveSyncJob` is a standalone Spark job that syncs a Hudi table's metadata to Hive metastore independently of any ingestion workflow. It is useful for backfills, manual data corrections, or reconciling metastore metadata after direct writes.
+
+### Arguments
+
+| Argument | Required | Description |
+|---|---|---|
+| `--base-path` / `-sp` | Yes | Base path of the Hudi table. |
+| `--base-file-format` / `-bff` | No | Base file format. Default: `PARQUET`. |
+| `--props-file-path` | No | Path to a properties file with Hudi / Hive sync configs. |
+| `--hoodie-conf` | No | Inline config override (repeatable). |
+| `--spark-master` | No | Spark master URL. Inherits from environment if unset. |
+
+### Example
+
+```bash
+spark-submit \
+  --packages org.apache.hudi:hudi-utilities-slim-bundle_2.12:1.2.0,org.apache.hudi:hudi-spark3.5-bundle_2.12:1.2.0 \
+  --class org.apache.hudi.utilities.HudiHiveSyncJob \
+  hudi-utilities-slim-bundle-*.jar \
+  --base-path s3://my-bucket/hudi/my-table \
+  --base-file-format PARQUET \
+  --hoodie-conf hoodie.datasource.hive_sync.mode=hms \
+  --hoodie-conf hoodie.datasource.hive_sync.metastore.uris=thrift://hive-metastore:9083 \
+  --hoodie-conf hoodie.datasource.hive_sync.database=my_db \
+  --hoodie-conf hoodie.datasource.hive_sync.table=my_table
+```
+
+All `hoodie.datasource.hive_sync.*` options accepted by the DataSource writer are also accepted here. See [Syncing to Hive Metastore](syncing_metastore.md) for the full list.
diff --git a/website/docs/ingestion_flink.md b/website/docs/ingestion_flink.md
index e720a748c1c8..2e534e286d67 100644
--- a/website/docs/ingestion_flink.md
+++ b/website/docs/ingestion_flink.md
@@ -1,7 +1,7 @@
 ---
 title: Using Flink
 keywords: [hudi, flink, streamer, ingestion]
-last_modified_at: 2025-11-22T12:53:57+08:00
+last_modified_at: 2026-05-27T00:00:00-00:00
 ---
 
 ## CDC Ingestion
@@ -112,15 +112,24 @@ the compaction options `compaction.delta_commits` and `compaction.delta_seconds`
 
 For `INSERT` mode write operations, new Parquet files are written directly, and the [auto‑file sizing](file_sizing.md) is not enabled.
 
-### In-Memory Buffer Sort
+### Append Write Buffer
 
-For append-only workloads, Hudi supports in-memory buffer sorting to improve Parquet compression ratio. When enabled, data is sorted within the write buffer before being flushed to disk. This improves columnar file compression efficiency by grouping similar values together.
+For append-only workloads, Hudi supports several write-buffer strategies that improve Parquet compression ratio and write throughput. Data is sorted or batched within the write buffer before being flushed to disk, grouping similar values together for better columnar compression.
 
-| Option Name                 | Required | Default | Remarks                                                                                                                       |
-|-----------------------------|----------|---------|-------------------------------------------------------------------------------------------------------------------------------|
-| `write.buffer.sort.enabled` | `false`  | `false` | Whether to enable buffer sort within append write function. Improves Parquet compression ratio by sorting data before writing |
-| `write.buffer.sort.keys`    | `false`  | `N/A`   | Sort keys concatenated by comma (e.g., `col1,col2`). Required when `write.buffer.sort.enabled` is `true`                      |
-| `write.buffer.size`         | `false`  | `1000`  | Buffer size in number of records. When buffer reaches this size, data is sorted and flushed to disk                           |
+The buffer strategy is selected with `write.buffer.type`. In Hudi 1.2.0 this replaces the deprecated `write.buffer.sort.enabled` flag.
+
+| Option Name                              | Required | Default    | Remarks                                                                                                                                                                                                                          |
+|------------------------------------------|----------|------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
+| `write.buffer.type`                      | `false`  | `NONE`     | Buffer type for append write. Values: `NONE` (no buffering), `BOUNDED_IN_MEMORY` (double buffer with async write), `DISRUPTOR` (ring-buffer with async write, recommended for higher throughput), `CONTINUOUS_SORT` (TreeMap-based continuous sort with incremental draining) |
+| `write.buffer.size`                      | `false`  | `1000`     | Record count threshold at which the buffer is flushed. Applies to all non-`NONE` buffer types                                                                                                                                    |
+| `write.buffer.sort.keys`                 | `false`  | `N/A`      | Comma-separated sort key columns (e.g., `col1,col2`). Required for `DISRUPTOR` and `CONTINUOUS_SORT` modes                                                                                                                       |
+| `write.buffer.sort.continuous.drain.size`| `false`  | `1`        | Number of records drained per flush cycle in `CONTINUOUS_SORT` mode. Default 1 provides smooth incremental draining; increase for batching (e.g., 10–100)                                                                       |
+
+:::note
+`write.buffer.sort.enabled` is deprecated as of 1.2.0. Use `write.buffer.type=DISRUPTOR` instead for equivalent behavior. The `DISRUPTOR` and `CONTINUOUS_SORT` modes require `write.buffer.sort.keys` to be set.
+:::
+
+For Disruptor-specific tuning options, see [flink_tuning.md](flink_tuning.md#disruptor-buffer-tuning).
 
 ### Disable Meta Fields
 
@@ -156,7 +165,7 @@ Only Copy‑on‑Write tables are supported.
 
 ### Clustering Plan Strategy
 
-Custom clustering strategy is supported.
+Custom clustering strategy is supported. Hudi 1.2.0 adds `FlinkSkipSingleFileClusteringPlanStrategy` (`org.apache.hudi.client.clustering.plan.strategy.FlinkSkipSingleFileClusteringPlanStrategy`), which skips file groups that already consist of a single file, reducing unnecessary rewrites.
 
 | Option Name                                             | Required | Default | Remarks                                                                                                                                          |
 |---------------------------------------------------------|----------|---------|--------------------------------------------------------------------------------------------------------------------------------------------------|
@@ -185,7 +194,7 @@ Hudi Flink writer supports two types of writer indexes:
 | Cross‑Partition Changes | Cannot handle changes among partitions (unless input is a CDC stream)                                                                                                                                                                                                       | No limit on handling cross‑partition changes                                                                           |
 
 :::note
-Bucket index supports only the `UPSERT` write operation and cannot be used with the [append mode](#append-mode) in Flink.
+Bucket index supports `UPSERT` write operations on both COW and MOR tables. As of Hudi 1.2.0, MOR + bucket index + upsert is fully supported. Bucket index cannot be used with the [append mode](#append-mode) in Flink.
 :::
 
 ### Bucket Index Examples
@@ -349,10 +358,215 @@ For Flink streaming reads, rate limiting helps avoid backpressure when processin
 
 The average read rate can be calculated as: **`read.splits.limit` / `read.streaming.check-interval`** splits per second.
 
+Hudi 1.2.0 adds `read.commits.limit`, which complements `read.splits.limit` by capping the number of commits (instants) consumed per check interval. This is useful when tables have many small commits — limiting commits bounds the number of splits regardless of their individual size.
+
+### Options
+
+| Option Name                     | Required | Default             | Remarks                                                                                                                                                                                    |
+|---------------------------------|----------|---------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
+| `write.rate.limit`              | `false`  | `0`                 | Write record rate limit per second to prevent traffic jitter and improve stability. Default is 0 (no limit)                                                                                |
+| `read.splits.limit`             | `false`  | `Integer.MAX_VALUE` | Maximum number of splits allowed to read in each instant check for streaming reads. Average read rate = `read.splits.limit`/`read.streaming.check-interval`. Default is no limit           |
+| `read.commits.limit`            | `false`  | `(none)`            | Maximum number of commits (instants) allowed to read in each check interval. Complements `read.splits.limit`. Average rate = `read.commits.limit`/`read.streaming.check-interval`. Default is no limit |
+| `read.streaming.check-interval` | `false`  | `60`                | Check interval in seconds for streaming reads. Default is 60 seconds (1 minute)                                                                                                            |
+
+## Flink Source V2
+
+Hudi 1.2.0 introduces a new Flink source implementation ([RFC-95](https://github.com/apache/hudi/blob/master/rfc/rfc-95/rfc-95.md)) based on [FLIP-27](https://cwiki.apache.org/confluence/display/FLINK/FLIP-27%3A+Refactor+Source+Interface), available as an opt-in feature via the `read.source-v2.enabled` flag.
+
+### Why Source V2?
+
+The legacy Hudi Flink source was built on Flink's `SourceFunction` API. The FLIP-27 rewrite brings:
+
+- **Resumable split assignment** — splits can be checkpointed independently, enabling finer-grained recovery
+- **Checkpoint alignment** — the new API participates in Flink's coordinated checkpoint protocol, improving end-to-end consistency
+- **Push-down support** — predicate push-down, partition pruning, and `LIMIT` push-down are supported through the new source interface, reducing data scanned at the source level
+
+### Enabling Source V2
+
+```sql
+CREATE TABLE t1 (
+  uuid VARCHAR(20) PRIMARY KEY NOT ENFORCED,
+  name VARCHAR(10),
+  age INT,
+  ts TIMESTAMP(3),
+  `partition` VARCHAR(20)
+)
+PARTITIONED BY (`partition`)
+WITH (
+  'connector' = 'hudi',
+  'path' = '${path}',
+  'table.type' = 'MERGE_ON_READ',
+  'read.source-v2.enabled' = 'true'  -- enable the FLIP-27 source
+);
+```
+
+### Options
+
+| Option Name               | Required | Default | Remarks                                                                                                 |
+|---------------------------|----------|---------|---------------------------------------------------------------------------------------------------------|
+| `read.source-v2.enabled`  | `false`  | `false` | Whether to use the FLIP-27 new source (Source V2) to consume data files. Default is the legacy source  |
+
+### Savepoint Incompatibility
+
+:::warning
+Savepoints taken with the **legacy source** (`read.source-v2.enabled=false`) are **not compatible** with the Source V2 source, and vice versa. When switching from the legacy source to Source V2, start a fresh job without restoring from a legacy savepoint. If you need to preserve read progress, record the last committed instant time and use `read.start-commit` to resume from that point.
+:::
+
+## Record-Level Index (RLI) Bucket Indexing for Flink
+
+As of Hudi 1.2.0, the Flink writer supports the Record-Level Index (RLI) backed by the metadata table, in addition to the existing `FLINK_STATE` and `BUCKET` index types. RLI is stored in the metadata table and avoids the state-backend overhead of `FLINK_STATE`, while supporting full global or partition-scoped uniqueness guarantees.
+
+Two RLI variants are available via `index.type`:
+
+- `RECORD_LEVEL_INDEX` — partitioned RLI; enforces uniqueness per (partition path, record key) pair
+- `GLOBAL_RECORD_LEVEL_INDEX` — global RLI; enforces uniqueness across all partitions
+
+### Bootstrap
+
+When enabling RLI on an existing table, the bootstrap process loads existing record locations into RocksDB before the first write. Bootstrap is triggered by setting `index.bootstrap.enabled=true`.
+
+```sql
+CREATE TABLE my_hudi_table (
+  id BIGINT,
+  name STRING,
+  ts BIGINT,
+  dt STRING,
+  PRIMARY KEY (id) NOT ENFORCED
+)
+PARTITIONED BY (dt)
+WITH (
+  'connector' = 'hudi',
+  'path' = 'hdfs:///warehouse/my_hudi_table',
+  'table.type' = 'MERGE_ON_READ',
+  'index.type' = 'RECORD_LEVEL_INDEX',
+  'metadata.enabled' = 'true',
+  'index.bootstrap.enabled' = 'true',  -- enable bootstrap on first run
+  'index.bootstrap.rocksdb.path' = '/tmp/hudi-rli-rocksdb'
+);
+```
+
+Once bootstrap completes (after the first successful checkpoint), you can optionally restart the job with `index.bootstrap.enabled=false` to skip the bootstrap operators. Leaving them enabled is harmless — they become no-ops on subsequent runs and do not affect write performance.
+
+### In-Pipeline MDT Compaction
+
+For RLI workloads, the metadata table (MDT) accumulates log files that need periodic compaction. The option `metadata.compaction.async.enabled` (default `true`) runs MDT compaction inside the Flink pipeline after every `metadata.compaction.delta_commits` (default `10`) delta commits.
+
+### Options
+
+| Option Name                         | Required | Default  | Remarks                                                                                                                                                 |
+|-------------------------------------|----------|----------|---------------------------------------------------------------------------------------------------------------------------------------------------------|
+| `index.type`                        | `false`  | `FLINK_STATE` | Set to `RECORD_LEVEL_INDEX` or `GLOBAL_RECORD_LEVEL_INDEX` to use the metadata-table-backed RLI                                                    |
+| `index.bootstrap.enabled`           | `false`  | `false`  | Bootstrap the index from the existing table on first run. Blocks checkpoints during bootstrap                                                           |
+| `index.bootstrap.rocksdb.path`      | `false`  | system temp dir | Local path for RocksDB storage during RLI bootstrap. Each task manager creates a unique subdirectory under this path                             |
+| `index.rli.cache.size`              | `false`  | `256`    | Maximum memory in MB for the RLI cache per bucket-assign task. Dynamically adjusted based on historical usage                                           |
+| `index.rli.lookup.minibatch.size`   | `false`  | `1000`   | Maximum records buffered per mini-batch during RLI lookup. Mini-batching reduces individual index lookups. Minimum effective value is 1000              |
+| `metadata.compaction.async.enabled` | `false`  | `true`   | Whether to run MDT compaction asynchronously within the Flink pipeline. Recommended to keep enabled for RLI workloads                                  |
+| `metadata.compaction.delta_commits` | `false`  | `10`     | Number of MDT delta commits that trigger in-pipeline compaction                                                                                         |
+
+:::note
+`GLOBAL_RECORD_LEVEL_INDEX` requires `metadata.enabled=true` and `index.global.enabled=true`. The Flink table factory validates these constraints automatically.
+:::
+
+## Lookup Join
+
+Hudi 1.2.0 adds a RocksDB-backed cache option for Flink lookup joins against Hudi dimension tables. This avoids JVM heap pressure when the dimension table is large.
+
 ### Options
 
-| Option Name                     | Required | Default             | Remarks                                                                                                                                                                          |
-|---------------------------------|----------|---------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
-| `write.rate.limit`              | `false`  | `0`                 | Write record rate limit per second to prevent traffic jitter and improve stability. Default is 0 (no limit)                                                                      |
-| `read.splits.limit`             | `false`  | `Integer.MAX_VALUE` | Maximum number of splits allowed to read in each instant check for streaming reads. Average read rate = `read.splits.limit`/`read.streaming.check-interval`. Default is no limit |
-| `read.streaming.check-interval` | `false`  | `60`                | Check interval in seconds for streaming reads. Default is 60 seconds (1 minute)                                                                                                  |
+| Option Name                    | Required | Default                         | Remarks                                                                                                                                         |
+|--------------------------------|----------|---------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------|
+| `lookup.join.cache.type`       | `false`  | `heap`                          | Storage backend for the lookup join cache. `heap` (default) stores rows in JVM heap; `rocksdb` stores rows off-heap in an embedded RocksDB instance |
+| `lookup.join.rocksdb.path`     | `false`  | `${java.io.tmpdir}/hudi-lookup-rocksdb` | Local directory for RocksDB data when `lookup.join.cache.type=rocksdb`. Cleaned up when the lookup function closes                    |
+| `lookup.async`                 | `false`  | `false`                         | Whether to enable async lookup join. Async join can improve throughput when the lookup function has high latency                                  |
+| `lookup.async-thread-number`   | `false`  | `16`                            | Number of threads for async lookup join                                                                                                         |
+
+### Example
+
+```sql
+-- Streaming fact table with a processing-time attribute
+CREATE TABLE orders (
+  order_id BIGINT,
+  customer_id BIGINT,
+  amount DOUBLE,
+  proc_time AS PROCTIME(),
+  PRIMARY KEY (order_id) NOT ENFORCED
+) WITH (
+  'connector' = 'hudi',
+  'path' = 'hdfs:///warehouse/orders',
+  'table.type' = 'MERGE_ON_READ',
+  'read.streaming.enabled' = 'true'
+);
+
+-- Hudi dimension table with RocksDB-backed lookup cache
+CREATE TABLE customers (
+  customer_id BIGINT,
+  name STRING,
+  city STRING,
+  PRIMARY KEY (customer_id) NOT ENFORCED
+) WITH (
+  'connector' = 'hudi',
+  'path' = 'hdfs:///warehouse/customers',
+  'lookup.join.cache.type' = 'rocksdb',
+  'lookup.join.rocksdb.path' = '/tmp/hudi-lookup-rocksdb'
+);
+
+-- Lookup join keyed by the fact table's processing-time attribute
+SELECT o.order_id, c.name, o.amount
+FROM orders AS o
+JOIN customers FOR SYSTEM_TIME AS OF o.proc_time AS c
+  ON o.customer_id = c.customer_id;
+```
+
+## Virtual Metadata Columns
+
+Hudi metadata fields can be declared as `METADATA VIRTUAL` columns in the Flink DDL. This allows accessing system metadata (e.g., commit time, record key) without storing them as regular data columns.
+
+```sql
+CREATE TABLE events (
+  event_id BIGINT,
+  payload STRING,
+  -- virtual metadata columns (read-only, not persisted as data)
+  _hoodie_commit_time     STRING METADATA VIRTUAL,
+  _hoodie_record_key      STRING METADATA VIRTUAL,
+  _hoodie_partition_path  STRING METADATA VIRTUAL,
+  PRIMARY KEY (event_id) NOT ENFORCED
+)
+WITH (
+  'connector' = 'hudi',
+  'path' = 'hdfs:///warehouse/events'
+);
+
+-- Query metadata alongside data
+SELECT event_id, _hoodie_commit_time, payload FROM events;
+```
+
+:::note
+Only `VIRTUAL` metadata columns are supported. All valid virtual columns correspond to Hudi's built-in meta fields (`_hoodie_commit_time`, `_hoodie_commit_seqno`, `_hoodie_record_key`, `_hoodie_partition_path`, `_hoodie_file_name`, `_hoodie_operation`).
+:::
+
+## Advanced Options
+
+### Hadoop Configuration Pass-through
+
+Hadoop filesystem configuration properties can be passed to the Flink writer using the `properties.hadoop.*` prefix (or directly as `hadoop.*`):
+
+```sql
+WITH (
+  'connector' = 'hudi',
+  'path' = 's3a://my-bucket/my-table',
+  'properties.hadoop.fs.s3a.access.key' = 'AKID...',
+  'properties.hadoop.fs.s3a.secret.key' = '...'
+)
+```
+
+### Kafka Offset Tracing
+
+For advanced Kafka offset tracing (internal/optional), the following `kafka.offset.trace.*` options configure the checkpoint-service-based offset lookup used in some deployment environments. These are advanced options with no functional impact on standard Hudi writes:
+
+| Option Name                              | Default           | Remarks                                                    |
+|------------------------------------------|-------------------|------------------------------------------------------------|
+| `kafka.offset.trace.caller.service.name` | `ingestion-rt`    | Caller service name for checkpoint-service RPC headers     |
+| `kafka.offset.trace.checkpoint.service`  | `athena-job-manager` | Checkpoint service name                                 |
+| `kafka.offset.trace.dc`                  | `(none)`          | Data center for checkpoint offset lookup                   |
+| `kafka.offset.trace.env`                 | `(none)`          | Environment for checkpoint offset lookup                   |
+| `kafka.offset.trace.job.name`            | `(none)`          | Flink job name for checkpoint offset lookup                |
diff --git a/website/docs/key_generation.md b/website/docs/key_generation.md
index 3a7b109c3363..5f00956cefdb 100644
--- a/website/docs/key_generation.md
+++ b/website/docs/key_generation.md
@@ -2,7 +2,7 @@
 title: Key Generation
 summary: "In this page, we describe key generation in Hudi."
 toc: true
-last_modified_at:
+last_modified_at: 2026-05-27T00:00:00-00:00
 ---
 
 Hudi needs some way to point to records in the table, so that base/log files can be merged efficiently for updates/deletes, 
@@ -210,6 +210,35 @@ Partition path generated from key generator: "2020040118"
 Input field value: "20200401" <br/>
 Partition path generated from key generator: "04/01/2020"
 
+## Slash-Separated Date Partitioning
+
+By default, Hudi writes date-valued partition paths as a flat string (e.g. `2024-03-15`).
+When `hoodie.datasource.write.slash.separated.date.partitioning` is set to `true`, partition field
+values in `yyyy-MM-dd` format are stored as `yyyy/MM/dd` directory hierarchies (e.g. `2024/03/15`).
+
+| Config Name                                                          | Default   | Description                                                                                                                              |
+|----------------------------------------------------------------------|-----------|------------------------------------------------------------------------------------------------------------------------------------------|
+| `hoodie.datasource.write.slash.separated.date.partitioning`          | `false`   | When `true`, transforms date partition values from `yyyy-MM-dd` into `yyyy/MM/dd` directory paths. Cannot be used together with hive-style partitioning (`hoodie.datasource.write.hive_style_partitioning=true`). |
+
+Example:
+
+```java
+df.write.format("hudi")
+  .option("hoodie.datasource.write.partitionpath.field", "event_date")
+  .option("hoodie.datasource.write.slash.separated.date.partitioning", "true")
+  .option("hoodie.table.name", tableName)
+  .mode("append")
+  .save(basePath)
+```
+
+A record with `event_date = "2024-03-15"` will be stored under `basePath/2024/03/15/` instead of
+`basePath/2024-03-15/`.
+
+:::note
+`SHOW PARTITIONS` in Spark SQL correctly handles slash-separated date partition paths: it displays
+the value in `yyyy-MM-dd` form (normalizing the `/` separators back to `-`) for readability.
+:::
+
 ## Related Resources
 
 <h3>Blogs</h3>
diff --git a/website/docs/lance_file_format.md b/website/docs/lance_file_format.md
index 51269107f188..9754a4e31797 100644
--- a/website/docs/lance_file_format.md
+++ b/website/docs/lance_file_format.md
@@ -1,18 +1,28 @@
 ---
 title: "Lance File Format"
 keywords: [ hudi, lance, file format, vector, AI, ML, columnar, ANN, indexing]
-summary: "Use the Lance columnar file format with Hudi for vector-optimized storage, ANN indexing, and efficient ML workloads"
+summary: "Use the Lance columnar file format with Hudi for vector-friendly storage and efficient ML workloads"
 toc: true
-last_modified_at: 2026-04-25T00:00:00-00:00
+last_modified_at: 2026-05-27T00:00:00-00:00
 ---
 
 [Lance](https://lancedb.github.io/lance/) is a modern columnar data format designed for AI and machine learning
 workloads. Hudi's pluggable storage architecture lets you use Lance as the base file format alongside Parquet
 and ORC, unlocking vector indexing, fast random access, and optimized high-dimensional array storage.
 
+:::caution Engine Support
+Lance file format support is **Spark-only**. Attempting to read a Lance-backed table from Flink or Hive throws a
+`HoodieValidationException`:
+> Lance base file format is currently only supported with the Spark engine. Please use Parquet, ORC, or HFile
+> for non-Spark engines (Flink, Hive, Presto, Trino).
+
+The Lance JAR is **not bundled** in the Hudi distribution — you must add it to your Spark classpath
+(see [Required Dependencies](#required-dependencies)).
+:::
+
 ## Enabling Lance in Hudi
 
-### Table Creation
+### Table Creation (COW)
 
 Set the base file format to `lance` in table properties:
 
@@ -26,7 +36,26 @@ TBLPROPERTIES (
     primaryKey = 'id',
     type = 'cow',
     hoodie.record.merger.impls = 'org.apache.hudi.DefaultSparkRecordMerger',
-    hoodie.datasource.write.base.file.format = 'lance'
+    hoodie.table.base.file.format = 'lance'
+);
+```
+
+### Table Creation (MOR)
+
+Lance base files work with MOR tables — Lance files act as base files while Avro log files capture
+incremental changes. Log compaction merges the delta log back into Lance base files.
+
+```sql
+CREATE TABLE my_ai_table_mor (
+    id        STRING,
+    embedding VECTOR(768),
+    metadata  STRING
+) USING hudi
+TBLPROPERTIES (
+    primaryKey = 'id',
+    type = 'mor',
+    hoodie.record.merger.impls = 'org.apache.hudi.DefaultSparkRecordMerger',
+    hoodie.table.base.file.format = 'lance'
 );
 ```
 
@@ -39,18 +68,21 @@ TBLPROPERTIES (
    .option("hoodie.datasource.write.recordkey.field", "id")
    .option("hoodie.record.merger.impls",
            "org.apache.hudi.DefaultSparkRecordMerger")
-   .option("hoodie.datasource.write.base.file.format", "lance")
+   .option("hoodie.table.base.file.format", "lance")
    .mode("overwrite")
    .save("/path/to/my_ai_table"))
 ```
 
 ### Required Dependencies
 
-Add the Lance Spark bundle to your Spark classpath:
+The Lance JAR is not bundled in Hudi. Add the appropriate Lance Spark bundle JAR to your Spark classpath:
 
-| Component | Maven Coordinates |
-|:----------|:-----------------|
-| Lance Spark Bundle (Spark 3.5) | `org.lance:lance-spark-bundle-3.5_2.12:0.4.0` |
+| Spark Version | Bundle JAR (Maven Central) |
+|:--------------|:---------------------------|
+| Spark 3.4 | `org.lance:lance-spark-bundle-3.4_2.12:0.4.0` |
+| Spark 3.5 | `org.lance:lance-spark-bundle-3.5_2.12:0.4.0` |
+| Spark 4.0 | `org.lance:lance-spark-bundle-4.0_2.13:0.4.0` |
+| Spark 4.1 | `org.lance:lance-spark-bundle-4.1_2.13:0.4.0` |
 
 ```bash
 export LANCE_BUNDLE_JAR=/path/to/lance-spark-bundle-3.5_2.12-0.4.0.jar
@@ -74,7 +106,6 @@ file-level storage:
 │  (same Hudi concepts as Parquet)  │
 ├───────────────────────────────────┤
 │     Lance Data Files (.lance)     │
-│  IVF-PQ vector index              │
 │  Columnar storage                 │
 │  Fragment-based layout            │
 ├───────────────────────────────────┤
@@ -87,11 +118,49 @@ All Hudi table services work with Lance-backed tables:
 - **Compaction** — merges log files into Lance base files
 - **Clustering** — reorganizes Lance files for better data locality
 - **Cleaning** — removes old Lance file versions
-- **Metadata indexing** — column stats and bloom filters work across Lance files
+- **Metadata indexing** — bloom filters work across Lance files; column stats and partition stats are
+  **automatically disabled** for Lance tables
+
+## VECTOR Storage on Lance
+
+VECTOR columns are stored natively in Lance as `FixedSizeList<Float32/Float64, dim>` — Lance's own
+vector column encoding, so embeddings are written without conversion overhead at the file-format
+layer.
+
+Only **FLOAT** and **DOUBLE** element types are supported as VECTOR columns on Lance. INT8 vectors
+are not yet supported and will fail fast at write time.
+
+See [Vector Search](vector_search.md) for the `hudi_vector_search` TVF that queries VECTOR columns.
+
+## BLOB Columns on Lance
+
+INLINE BLOB columns on Lance default to `DESCRIPTOR` read mode — standard queries return an
+out-of-line-shaped reference descriptor rather than materializing the raw bytes. To read inline
+byte content via `read_blob()`, set `hoodie.read.blob.inline.mode=CONTENT`. See
+[Unstructured Data](blob_unstructured_data.md) for full documentation.
+
+## Schema Evolution
+
+Lance supports the following schema changes at the Hudi layer:
+
+| Operation | Supported? |
+|:----------|:-----------|
+| Add column | Yes |
+| Rename column | Yes (via Hudi schema evolution) |
+| Promote `FLOAT` → `DOUBLE` | **No** — not supported on Lance |
+| Promote `FLOAT` → `STRING` | **No** — not supported on Lance |
+| Drop column | Yes |
+
+:::caution
+`FLOAT → DOUBLE` and `FLOAT → STRING` type promotions are supported for Parquet tables but **not**
+for Lance. Attempting these on a Lance table will fail. Use `DOUBLE` from the start if you anticipate
+needing higher precision.
+:::
 
 ## Vector Search with Lance
 
-The `hudi_vector_search` TVF leverages Lance's built-in IVF-PQ index for approximate nearest neighbor search:
+Use the `hudi_vector_search` TVF to run vector similarity queries against VECTOR columns on a
+Lance-backed table:
 
 ```sql
 SELECT id, metadata, _hudi_distance
@@ -107,10 +176,38 @@ See [Vector Search](vector_search.md) for full documentation on the TVF and dist
 
 ## Configuration Reference
 
-| Property | Description | Default |
-|:---------|:------------|:--------|
-| `hoodie.datasource.write.base.file.format` | Set to `lance` to use Lance as the base file format | `parquet` |
-| `hoodie.record.merger.impls` | Must be `org.apache.hudi.DefaultSparkRecordMerger` for Lance | — |
+| Property | Default | Description |
+|:---------|:--------|:------------|
+| `hoodie.table.base.file.format` | `parquet` | Set to `lance` to use Lance as the base file format. |
+| `hoodie.record.merger.impls` | — | Must be `org.apache.hudi.DefaultSparkRecordMerger` for Lance. |
+| `hoodie.lance.max.file.size` | `125829120` (120 MiB) | Target file size in bytes for Lance base files. |
+| `hoodie.lance.write.allocator.size.bytes` | `268435456` (256 MiB) | Maximum size of the Arrow child allocator used for buffering in-flight batch data. Increase for tables with very large BLOB columns. |
+| `hoodie.lance.write.flush.byte.watermark` | `100663296` (96 MiB) | Byte-size threshold at which the current write batch is flushed. Must be less than `hoodie.lance.write.allocator.size.bytes`. |
+
+### File Sizing and Memory
+
+The three sizing configs work together:
+
+- **`hoodie.lance.max.file.size`** controls when Hudi rolls over to a new Lance file, similar to
+  `hoodie.parquet.max.file.size` for Parquet tables.
+- **`hoodie.lance.write.allocator.size.bytes`** caps the Arrow allocator's in-flight memory. Arrow
+  uses power-of-2 buffer doubling; the default 256 MiB accommodates the 128 MiB doubling step with
+  headroom.
+- **`hoodie.lance.write.flush.byte.watermark`** triggers an early batch flush when Arrow buffers
+  approach the cap. The default 96 MiB (≈ 3/8 of the allocator cap) leaves room for offset and
+  validity buffers to double without exceeding the allocator limit.
+
+For tables with large BLOB columns, increase both `hoodie.lance.write.allocator.size.bytes` and
+`hoodie.lance.write.flush.byte.watermark` proportionally (keep watermark at roughly 3/8 of allocator
+size).
+
+## Additional Notes
+
+- **`populateMetaFields=false`** is supported. User-defined key generators work normally with Lance
+  tables.
+- **Complex types** (struct, array, map) are supported as Lance columns.
+- **VARIANT columns** are **not supported** on Lance. Attempting to write a table with VARIANT columns
+  to Lance throws a `HoodieNotSupportedException`. Use Parquet for tables with VARIANT columns.
 
 ## Mixed-Format Tables
 
diff --git a/website/docs/metadata.md b/website/docs/metadata.md
index c5a01b42dfa9..9c108c815cdd 100644
--- a/website/docs/metadata.md
+++ b/website/docs/metadata.md
@@ -89,6 +89,15 @@ If you turn off the metadata table after enabling, be sure to wait for a few com
 cleaned up, before re-enabling the metadata table again.
 :::
 
+### Auto-Delete of Disabled MDT Partitions
+
+When an index is disabled in the write config, Hudi automatically deletes the corresponding metadata table partition.
+Available since Hudi 1.2.0, this behavior is configurable.
+
+| Config Name | Default | Description |
+|---|---|---|
+| `hoodie.metadata.auto.delete.partitions` | `true` | When enabled (default), metadata table partitions (indexes) that are disabled in the write config are automatically deleted. Set to `false` to prevent accidental deletion in multi-writer environments where not all writers may have the same config — users must then drop indexes explicitly via Hudi CLI or `DROP INDEX`. |
+
 ## Leveraging metadata during queries
 
 ### files index
@@ -129,6 +138,28 @@ can bring up the writers sequentially after stopping the writers for enabling me
 configurations to only a subset of writers or table services is unsafe and can lead to loss of data. So, please ensure you enable 
 metadata table across all writers.
 
+## MDT Cleaner and Compaction
+
+Hudi 1.2.0 introduced a config that lets the metadata table's cleaner derive its retention policy directly from the
+data table, rather than requiring a separate configuration.
+
+| Config Name | Default | Description |
+|---|---|---|
+| `hoodie.metadata.derive.from.datatable.clean.policy` | `true` | When enabled, the metadata table's cleaner uses the same cleaning policy (retention count, hours, etc.) as the data table. See also [cleaning](cleaning.md#mdt-cleaner-inherits-data-table-policy). |
+
+The metadata table's compaction and log compaction can also be delegated to an external table service platform. See
+[compaction](compaction.md#delegating-mdt-compaction-to-an-external-platform) for the full config reference.
+
+## Timeline Archival Controls
+
+Hudi 1.2.0 added two configs in `HoodieArchivalConfig` to fine-tune how the timeline manifest and archival interact
+with the most recent clean.
+
+| Config Name | Default | Description |
+|---|---|---|
+| `hoodie.timeline.manifest.retained.versions` | `3` | Number of timeline manifest file versions to retain. Older manifest versions are pruned during archival. |
+| `hoodie.archive.block.on.latest.clean.ectr` | `false` | When enabled, archival stops at the Earliest Commit To Retain (ECTR) from the last completed clean. This prevents archiving commits whose data files still exist on storage, avoiding inconsistencies between the timeline and actual data. |
+
 ## Related Resources
 <h3>Blogs</h3>
 * [Table service deployment models in Apache Hudi](https://medium.com/@simpsons/table-service-deployment-models-in-apache-hudi-9cfa5a44addf)
diff --git a/website/docs/metadata_indexing.md b/website/docs/metadata_indexing.md
index 7056a1e02671..dbdd523df60d 100644
--- a/website/docs/metadata_indexing.md
+++ b/website/docs/metadata_indexing.md
@@ -2,7 +2,7 @@
 title: Indexing
 summary: "In this page, we describe how to run metadata indexing asynchronously."
 toc: true
-last_modified_at:
+last_modified_at: 2026-05-27T00:00:00-00:00
 ---
 
 Hudi maintains a scalable [metadata](metadata.md) that has some auxiliary data about the table.
@@ -36,7 +36,7 @@ For more information on these indexes please refer [metadata section](metadata/#
 :::note
 Please note in order to create secondary index:
 1. The table must have a primary key and merge mode should be [COMMIT_TIME_ORDERING](record_merger.md#commit_time_ordering).
-2. Record index must be enabled. This can be done by setting `hoodie.metadata.record.index.enable=true` and then creating `record_index`. Please note the example below.
+2. Record index must be enabled. This can be done by setting `hoodie.metadata.global.record.level.index.enable=true` and then creating `record_index`. Please note the example below.
 :::
 
 **Examples**
@@ -73,8 +73,8 @@ hoodie.metadata.index.column.stats.enable=true
 -- [Optional Configs] - list of columns to index on. By default all columns are indexed
 hoodie.metadata.index.column.stats.column.list=col1,col2,...
 
--- [Required Configs] Record Level Index
-hoodie.metadata.record.index.enable=true
+-- [Required Configs] Record Level Index (Global RLI — single record key unique across all partitions)
+hoodie.metadata.global.record.level.index.enable=true
 
 -- [Required Configs] Bloom filter Index
 hoodie.metadata.index.bloom.filter.enable=true
@@ -116,7 +116,7 @@ inserts.write.format("hudi").
   
 // Create record index and secondary index for the table
 spark.sql(s"CREATE TABLE test_table_external USING hudi LOCATION '$basePath'")
-spark.sql(s"SET hoodie.metadata.record.index.enable=true")
+spark.sql(s"SET hoodie.metadata.global.record.level.index.enable=true")
 spark.sql(s"CREATE INDEX record_index ON test_table_external (uuid)")
 spark.sql(s"CREATE INDEX idx_rider ON test_table_external (rider)")
 spark.sql(s"SHOW INDEXES FROM hudi_indexed_table").show(false)
@@ -191,6 +191,24 @@ Enabling the metadata table and configuring a lock provider are the prerequisite
 configuration below.
 :::
 
+#### Record-Level Index Configuration Keys
+
+Hudi supports two flavors of the Record Level Index, each with its own enable flag and sizing configs:
+
+- **Global RLI** — record key is unique across the entire table (across partitions).
+- **Partitioned RLI** — `partition_path + record_key` is unique within each partition.
+
+| Config Name | Default | Notes |
+|---|---|---|
+| `hoodie.metadata.global.record.level.index.enable` | `false` | Enables the global RLI. |
+| `hoodie.metadata.global.record.level.index.min.filegroup.count` | `10` | Min file groups for the global RLI. |
+| `hoodie.metadata.global.record.level.index.max.filegroup.count` | `10000` | Max file groups for the global RLI. |
+| `hoodie.metadata.record.level.index.enable` | `false` | Enables the partitioned RLI. Independent toggle from the global RLI above. |
+| `hoodie.metadata.record.level.index.min.filegroup.count` | `1` | Min file groups for the partitioned RLI. |
+| `hoodie.metadata.record.level.index.max.filegroup.count` | `10` | Max file groups for the partitioned RLI. |
+| `hoodie.metadata.record.level.index.defer.init` | `false` | When enabled, defers RLI initialization to the second commit on a fresh table so Hudi can size file groups based on actual record volume. Applies to both global and partitioned RLI. |
+| `hoodie.metadata.record.index.max.filegroup.size` | `1073741824` (1 GB) | Maximum size in bytes of a single RLI file group. Larger file groups take longer to compact. |
+
 ```
 # ensure that async indexing is enabled
 hoodie.metadata.index.async=true
@@ -284,7 +302,10 @@ indexer logs, we would find that it indeed caught up with instant `2022041419542
 
 ### Drop Index
 
-To drop an index, just run the index in `dropindex` mode.
+To drop an index, just run the index in `dropindex` mode. Note that as of Hudi 1.2.0, when an index is disabled in
+the write config, Hudi automatically drops its metadata table partition by default; see
+[`hoodie.metadata.auto.delete.partitions`](metadata.md#auto-delete-of-disabled-mdt-partitions) to control this
+behavior.
 
 ```
 spark-submit \
diff --git a/website/docs/metrics.md b/website/docs/metrics.md
index ec94d21aa45a..151d27f5d319 100644
--- a/website/docs/metrics.md
+++ b/website/docs/metrics.md
@@ -3,7 +3,7 @@ title: Metrics
 keywords: [ hudi, administration, operation, devops, metrics]
 summary: This section offers an overview of metrics in Hudi
 toc: true
-last_modified_at: 2020-06-20T15:59:57-04:00
+last_modified_at: 2026-05-27T00:00:00-00:00
 ---
 
 In this section, we will introduce the `MetricsReporter` and `HoodieMetrics` in Hudi. You can view the metrics-related configurations [here](configurations.md#METRICS).
@@ -204,29 +204,46 @@ These `HoodieMetrics` can then be plotted on a standard tool like grafana. Below
 
 ## List of metrics:
 
-The below metrics are available in all timeline operations that involves a commit such as deltacommit, compaction, clustering and rollback.
+The metrics below are emitted across timeline operations (deltacommit, compaction, clustering, rollback, clean, archival) and post-commit callbacks. When `hoodie.metrics.reporter.metricsname.prefix` is set, every name is prefixed with `<prefix>.<name>`.
 
-Name  |  Description
+Name | Description
 --- | ---
-commitFreshnessInMs | Milliseconds from the commit end time and the maximum event time of the incoming records
-commitLatencyInMs | Milliseconds from the commit end time and the minimum event time of incoming records
-commitTime  | Time of commit in epoch milliseconds
-duration  | Total time taken for the commit/rollback in milliseconds
-numFilesDeleted | Number of files deleted during a clean/rollback
-numFilesFinalized | Number of files finalized in a write
-totalBytesWritten | Bytes written in a HoodieCommit
-totalCompactedRecordsUpdated  | Number of records updated in a compaction operation
-totalCreateTime | Time taken for file creation during a Hoodie Insert operation
-totalFilesInsert  | Number of newly written files in a HoodieCommit
-totalFilesUpdate  | Number of files updated in a HoodieCommit
-totalInsertRecordsWritten | Number of records inserted or converted to updates(for small file handling) in a HoodieCommit
-totalLogFilesCompacted  | Number of log files under a base file in a file group compacted
-totalLogFilesSize | Total size in bytes of all log files under a base file in a file group
-totalPartitionsWritten  | Number of partitions that took writes in a HoodieCommit
-totalRecordsWritten | Number of records written in a HoodieCommit. For inserts, it is the total numbers of records inserted. And for updates, it the total number of records in the file.
-totalScanTime | Time taken for reading and merging logblocks in a log file
-totalUpdateRecordsWritten | Number of records that got changed in a HoodieCommit
-totalUpsertTime | Time taken for Hoodie Merge
-
-These metrics can be found at org.apache.hudi.metrics.HoodieMetrics and referenced from 
-org.apache.hudi.common.model.HoodieCommitMetadata and org.apache.hudi.common.model.HoodieWriteStat
+commitFreshnessInMs | Milliseconds from the commit end time and the maximum event time of the incoming records.
+commitLatencyInMs | Milliseconds from the commit end time and the minimum event time of incoming records.
+commitTime | Time of commit in epoch milliseconds.
+duration | Total time taken for the commit/rollback in milliseconds.
+numFilesDeleted | Number of files deleted during a clean/rollback.
+numFilesFinalized | Number of files finalized in a write.
+totalBytesWritten | Bytes written in a HoodieCommit.
+totalCompactedRecordsUpdated | Number of records updated in a compaction operation.
+totalCreateTime | Time taken for file creation during a Hoodie Insert operation.
+totalFilesInsert | Number of newly written files in a HoodieCommit.
+totalFilesUpdate | Number of files updated in a HoodieCommit.
+totalInsertRecordsWritten | Number of records inserted or converted to updates (for small file handling) in a HoodieCommit.
+totalLogFilesCompacted | Number of log files under a base file in a file group compacted.
+totalLogFilesSize | Total size in bytes of all log files under a base file in a file group.
+totalPartitionsWritten | Number of partitions that took writes in a HoodieCommit.
+totalRecordsWritten | Number of records written in a HoodieCommit. For inserts, the total records inserted; for updates, the total records in the file.
+totalScanTime | Time taken for reading and merging log blocks in a log file.
+totalUpdateRecordsWritten | Number of records that got changed in a HoodieCommit.
+totalUpsertTime | Time taken for Hoodie Merge.
+clean.duration | Wall-clock time in milliseconds for a clean operation.
+archive.duration | Wall-clock time in milliseconds for an archive operation.
+rollback.failure.counter | Incremented each time a rollback operation fails.
+postCommit.success.counter | Incremented each time all post-commit callbacks succeed.
+postCommit.failure.counter | Incremented each time a post-commit callback fails (post-commit failures are non-fatal).
+postCommit.duration | Wall-clock time in milliseconds for post-commit callback execution.
+archival.archivalNumAllCommits | Total number of instants archived in this archival run.
+archival.archivalNumWriteCommits | Number of write instants (commit, deltacommit, replacecommit) archived.
+archival.archivalNumCleanCommits | Number of clean instants archived.
+archival.archivalNumRollbackCommits | Number of rollback instants archived.
+archival.archivalStatus | `1` if archival succeeded, `-1` if it failed.
+archival.archivalFailure.\<ExceptionClassName\> | Incremented on archival failure; the suffix is the simple class name of the exception thrown.
+archival.archivalOutOfMemory | Incremented when archival fails due to an `OutOfMemoryError`.
+\<action\>.totalCorruptedLogBlocks | Number of corrupted log blocks encountered during compaction. Reported only when `hoodie.metricscompaction.log.blocks.on=true`. `<action>` is the commit action type (e.g., `commit`).
+\<action\>.totalRollbackLogBlocks | Number of rollback log blocks encountered during compaction. Reported only when `hoodie.metricscompaction.log.blocks.on=true`.
+\<action\>.totalLogBlocksCompacted | Total number of log blocks compacted. Reported only when `hoodie.metricscompaction.log.blocks.on=true`.
+
+These metrics live in `org.apache.hudi.metrics.HoodieMetrics` (with archival-specific names sourced from `org.apache.hudi.client.utils.ArchivalMetrics`) and are referenced from `org.apache.hudi.common.model.HoodieCommitMetadata` and `org.apache.hudi.common.model.HoodieWriteStat`.
+
+In multi-tenant deployments where a single Spark job writes to multiple Hudi tables, each table gets its own isolated `MetricRegistry`, scoped as `<tableName>.<registryName>` so metrics from different tables do not collide. No configuration is required.
diff --git a/website/docs/overview.mdx b/website/docs/overview.mdx
index 46c2d5b4be1c..ed439cef38a1 100644
--- a/website/docs/overview.mdx
+++ b/website/docs/overview.mdx
@@ -59,10 +59,10 @@ If you want to experience Apache Hudi integrated into an end to end demo with Ka
 
 Hudi brings first-class support for AI and unstructured data workloads to the data lakehouse:
 
-- **[VECTOR type & Similarity Search](vector_search.md)** — Store embeddings and run approximate nearest neighbor search directly in Spark SQL
+- **[VECTOR type & Similarity Search](vector_search.md)** — Store embeddings and run vector similarity search directly in Spark SQL
 - **[BLOB type for Unstructured Data](blob_unstructured_data.md)** — Store images, PDFs, audio, and other binary data with inline or out-of-line storage
 - **[VARIANT type for Semi-Structured Data](variant_type.md)** — Store flexible JSON-like data (LLM outputs, model metadata, feature maps) without rigid schemas
-- **[Lance File Format](lance_file_format.md)** — Vector-optimized columnar format with built-in ANN indexing
+- **[Lance File Format](lance_file_format.md)** — Vector-friendly columnar format for AI/ML workloads
 
 See the full [AI-Native Lakehouse Overview](ai_overview.md) for use cases and architecture.
 
diff --git a/website/docs/precommit_validator.md b/website/docs/precommit_validator.md
index fe0a0dd77605..d67d9ec3a054 100644
--- a/website/docs/precommit_validator.md
+++ b/website/docs/precommit_validator.md
@@ -109,6 +109,73 @@ Hudi offers a [commit notification service](platform_services_post_commit_callba
 
 The commit notification service can be combined with pre-commit validators to send a notification when a commit fails a validation. This is possible by passing details about the validation as a custom value to the HTTP endpoint.
 
+## Notes on Validator Behavior
+
+Hudi 1.2.0 introduced the following behavioral refinements:
+
+**Metadata fields in SQL queries**: Validator SQL can now reference Hudi metadata fields (`_hoodie_record_key`, `_hoodie_partition_path`, `_hoodie_file_name`, `_hoodie_commit_time`, `_hoodie_commit_seqno`) directly in query expressions.
+
+**Empty writes**: Empty write commits no longer cause pre-commit validators to error. Validators are skipped gracefully when no records are present in the write.
+
+## Failure Policy
+
+Hudi 1.2.0 introduces a configurable failure policy for pre-commit validators:
+
+| Config Key | Default | Description |
+|---|---|---|
+| `hoodie.precommit.validators.failure.policy` | `FAIL` | How to handle validator failures. `FAIL`: block the commit with an exception. `WARN_LOG`: emit a warning log but allow the commit to proceed (useful for soft monitoring). |
+
+## Flink and Streaming-Offset Validators
+
+Available since Hudi 1.2.0. Flink writers now honor `hoodie.precommit.validators` using the same configuration key as Spark. Validators intended for use with Flink must extend the engine-agnostic `org.apache.hudi.client.validator.BasePreCommitValidator` (in `hudi-common`), which provides access to commit metadata and timeline information independently of Spark.
+
+Two built-in streaming-offset validators are now available for Kafka-sourced pipelines:
+
+| Validator Class | Engine | Description |
+|---|---|---|
+| `org.apache.hudi.sink.validator.FlinkKafkaOffsetValidator` | Flink | Validates that the number of records written matches the Kafka offset difference for the batch |
+| `org.apache.hudi.utilities.streamer.validator.SparkKafkaOffsetValidator` | Spark / HoodieStreamer | Same semantics for Spark-based Kafka ingestion pipelines |
+
+Both validators use the following configuration:
+
+| Config Key | Default | Description |
+|---|---|---|
+| `hoodie.precommit.validators.streaming.offset.tolerance.percentage` | `0.0` | Tolerance percentage for offset-based record-count validation. A value of `0.0` requires an exact match between expected records (from Kafka offset delta) and actual records written. For upsert workloads with deduplication, set a higher tolerance (e.g., `10.0` for 10%). |
+| `hoodie.precommit.validators.failure.policy` | `FAIL` | See [Failure Policy](#failure-policy) above. |
+
+Example (Flink):
+```properties
+hoodie.precommit.validators=org.apache.hudi.sink.validator.FlinkKafkaOffsetValidator
+hoodie.precommit.validators.streaming.offset.tolerance.percentage=5.0
+hoodie.precommit.validators.failure.policy=WARN_LOG
+```
+
+## Pre-Write Validators
+
+Introduced in Hudi 1.2.0, pre-write validators run **before** data is written to storage, in contrast to pre-commit validators which run **after** data is written but before the commit is published to the timeline. This enables earlier rejection of invalid operations, avoiding unnecessary I/O.
+
+Configuration:
+
+| Config Key | Default | Description |
+|---|---|---|
+| `hoodie.prewrite.validators` | `""` | Comma-separated list of fully-qualified class names implementing `org.apache.hudi.client.validator.PreWriteValidator`. |
+
+To implement a custom pre-write validator, implement the `org.apache.hudi.client.validator.PreWriteValidator` interface:
+
+```java
+public interface PreWriteValidator {
+  <T> void validate(
+      String instantTime,
+      WriteOperationType writeOperationType,
+      HoodieTableMetaClient metaClient,
+      HoodieWriteConfig writeConfig,
+      HoodieEngineContext engineContext,
+      Option<HoodieData<HoodieRecord<T>>> recordsOpt) throws HoodieValidationException;
+}
+```
+
+No built-in pre-write validator implementations are provided yet; this framework is designed for custom user extensions. Unlike pre-commit validators, pre-write validators have access to the incoming records before any write I/O occurs.
+
 ## Related Resources
 
 <h3>Blogs</h3>
diff --git a/website/docs/procedures.md b/website/docs/procedures.md
index 71edf434ed10..94dd41991598 100644
--- a/website/docs/procedures.md
+++ b/website/docs/procedures.md
@@ -2,7 +2,7 @@
 title: SQL Procedures
 summary: "In this page, we introduce how to use SQL procedures with Hudi."
 toc: true
-last_modified_at: 2025-11-24T00:00:00
+last_modified_at: 2026-05-27T00:00:00-00:00
 ---
 import Tabs from '@theme/Tabs';
 import TabItem from '@theme/TabItem';
@@ -298,6 +298,64 @@ call show_archived_commits(table => 'test_hudi_table');
 | 20220216171027021 | 435346              | 1                 | 0                   | 1                        | 1                     | 0                            | 0            |
 | 20220216171019361 | 435349              | 1                 | 0                   | 1                        | 1                     | 0                            | 0            |
 
+### show_timeline
+
+Show timeline entries for a Hudi table. Returns instant-level information for all timeline operations (commits, compactions, clustering, clean, rollback, etc.) from the active and optionally archived timeline. Results are sorted by timestamp descending.
+
+**Input**
+
+| Parameter Name | Type    | Required | Default Value | Description                                                                      |
+|----------------|---------|----------|---------------|----------------------------------------------------------------------------------|
+| table          | String  | N*       | None          | Hudi table name (mutually exclusive with `path`)                                 |
+| path           | String  | N*       | None          | Base path of the Hudi table (mutually exclusive with `table`)                    |
+| limit          | Int     | N        | 20            | Max number of timeline entries to return (ignored when both `startTime` and `endTime` are set) |
+| showArchived   | Boolean | N        | false         | Whether to include archived timeline entries                                     |
+| filter         | String  | N        | ""            | SQL expression to filter results on any output column                            |
+| startTime      | String  | N        | ""            | Start timestamp for filtering (format: `yyyyMMddHHmmss`, inclusive)              |
+| endTime        | String  | N        | ""            | End timestamp for filtering (format: `yyyyMMddHHmmss`, inclusive)                |
+
+\* Either `table` or `path` must be provided.
+
+**Output**
+
+| Output Name    | Type   | Description                                                                           |
+|----------------|--------|---------------------------------------------------------------------------------------|
+| instant_time   | String | Requested timestamp of the instant                                                    |
+| action         | String | Action type: `commit`, `deltacommit`, `compaction`, `clustering`, `clean`, `rollback`, etc. |
+| state          | String | State of the instant: `REQUESTED`, `INFLIGHT`, or `COMPLETED`                        |
+| requested_time | String | Wall-clock time when the instant was requested (format: `MM-dd HH:mm:ss`)            |
+| inflight_time  | String | Wall-clock time when the instant became inflight (format: `MM-dd HH:mm:ss`)          |
+| completed_time | String | Wall-clock time when the instant completed (format: `MM-dd HH:mm:ss`), or `null`     |
+| timeline_type  | String | `ACTIVE` or `ARCHIVED`                                                                |
+| rollback_info  | String | For rollback instants: what was rolled back; for rolled-back instants: which rollback instant rolled them back; otherwise `null` |
+
+**Example**
+
+```sql
+-- Show the 20 most recent timeline entries
+call show_timeline(table => 'test_hudi_table');
+
+-- Show up to 50 entries including archived timeline
+call show_timeline(table => 'test_hudi_table', limit => 50, showArchived => true);
+
+-- Filter to completed commits in a time range
+call show_timeline(
+  table => 'test_hudi_table',
+  startTime => '20251201000000',
+  endTime => '20251231235959',
+  filter => "action = 'commit' AND state = 'COMPLETED'"
+);
+
+-- Look up by base path instead of table name
+call show_timeline(path => 'hdfs:///user/hive/warehouse/test_hudi_table');
+```
+
+| instant_time      | action | state     | requested_time      | inflight_time       | completed_time      | timeline_type | rollback_info |
+|-------------------|--------|-----------|---------------------|---------------------|---------------------|---------------|---------------|
+| 20251205143022001 | commit | COMPLETED | 12-05 14:30:20      | 12-05 14:30:21      | 12-05 14:30:22      | ACTIVE        | null          |
+| 20251205141510003 | clean  | COMPLETED | 12-05 14:15:09      | 12-05 14:15:10      | 12-05 14:15:10      | ACTIVE        | null          |
+| 20251205140030002 | commit | COMPLETED | 12-05 14:00:28      | 12-05 14:00:29      | 12-05 14:00:30      | ACTIVE        | null          |
+
 ### show_commit_files
 
 Show files of a commit.
diff --git a/website/docs/reading_tables_batch_reads.md b/website/docs/reading_tables_batch_reads.md
index 0a62f34d55e3..3b1e845e2145 100644
--- a/website/docs/reading_tables_batch_reads.md
+++ b/website/docs/reading_tables_batch_reads.md
@@ -19,6 +19,32 @@ val tripsDF = spark.read.
 tripsDF.where(tripsDF.fare > 20.0).show()
 ```
 
+## Flink Batch (Snapshot) Read
+
+Flink can read a Hudi table as a snapshot (batch) query by leaving `read.streaming.enabled` at its default value of `false`.
+
+```sql
+CREATE TABLE hudi_table (
+  uuid VARCHAR(20) PRIMARY KEY NOT ENFORCED,
+  name VARCHAR(10),
+  age INT,
+  ts TIMESTAMP(3),
+  `partition` VARCHAR(20)
+)
+PARTITIONED BY (`partition`)
+WITH (
+  'connector' = 'hudi',
+  'path' = '${path}',
+  'table.type' = 'MERGE_ON_READ'
+  -- read.streaming.enabled defaults to false → batch/snapshot read
+);
+
+-- Snapshot query
+SELECT * FROM hudi_table WHERE age > 25;
+```
+
+For more Flink read options, see [Using Flink](ingestion_flink.md).
+
 ## Daft
 
 [Daft](https://www.daft.ai/) supports reading Hudi tables using `daft.read_hudi()` function.
diff --git a/website/docs/reading_tables_streaming_reads.md b/website/docs/reading_tables_streaming_reads.md
index 5055f42c0449..7191cedf0115 100644
--- a/website/docs/reading_tables_streaming_reads.md
+++ b/website/docs/reading_tables_streaming_reads.md
@@ -97,3 +97,47 @@ spark.readStream \
 Spark SQL can be used within ForeachBatch sink to do INSERT, UPDATE, DELETE and MERGE INTO.
 Target table must exist before write.
 :::
+
+## Flink Streaming Read
+
+Flink can continuously consume new commits from a Hudi table as a streaming source. Enable this by setting `read.streaming.enabled=true` and optionally a `read.start-commit`.
+
+```sql
+CREATE TABLE hudi_table (
+  uuid VARCHAR(20) PRIMARY KEY NOT ENFORCED,
+  name VARCHAR(10),
+  age INT,
+  ts TIMESTAMP(3),
+  `partition` VARCHAR(20)
+)
+PARTITIONED BY (`partition`)
+WITH (
+  'connector' = 'hudi',
+  'path' = '${path}',
+  'table.type' = 'MERGE_ON_READ',
+  'read.streaming.enabled' = 'true',          -- enable streaming read
+  'read.start-commit' = '20210316134557',      -- start from this instant (omit for latest)
+  'read.streaming.check-interval' = '60'       -- poll interval in seconds
+);
+
+SELECT * FROM hudi_table;
+```
+
+### Source V2 for Streaming
+
+As of Hudi 1.2.0, the [FLIP-27-based Source V2](ingestion_flink.md#flink-source-v2) is available as an opt-in for streaming reads. Source V2 participates in Flink's checkpoint protocol for finer-grained recovery and supports partition pruning:
+
+```sql
+WITH (
+  'connector' = 'hudi',
+  'path' = '${path}',
+  'read.streaming.enabled' = 'true',
+  'read.source-v2.enabled' = 'true'   -- enable FLIP-27 source (Hudi 1.2.0+)
+)
+```
+
+:::warning
+Savepoints taken with the legacy source are not compatible with Source V2. Start a fresh job when switching. See [Flink Source V2](ingestion_flink.md#flink-source-v2) for migration details.
+:::
+
+For a full list of Flink streaming read options (rate limiting, commits limit, CDC mode, etc.), see [Using Flink](ingestion_flink.md).
diff --git a/website/docs/sql_ddl.md b/website/docs/sql_ddl.md
index d1c5ba865bdb..2fc0d421dab2 100644
--- a/website/docs/sql_ddl.md
+++ b/website/docs/sql_ddl.md
@@ -2,7 +2,7 @@
 title: SQL DDL
 summary: "In this page, we discuss using SQL DDL commands with Hudi"
 toc: true
-last_modified_at: 
+last_modified_at: 2026-05-27T00:00:00-00:00
 ---
 import Tabs from '@theme/Tabs';
 import TabItem from '@theme/TabItem';
@@ -696,6 +696,14 @@ SHOW PARTITIONS hudi_table;
 ALTER TABLE hudi_table DROP PARTITION (dt='2021-12-09', hh='10');
 ```
 
+:::note Slash-separated date partitioning and SHOW PARTITIONS
+When a table is written with `hoodie.datasource.write.slash.separated.date.partitioning=true`, the
+physical directory layout uses `yyyy/MM/dd` paths. `SHOW PARTITIONS` correctly handles this: it
+returns partition values in the standard `col=yyyy-MM-dd` display format, normalizing the `/`
+separators back to `-` for readability. See [Key Generation](key_generation.md#slash-separated-date-partitioning)
+for details on configuring slash-separated partitioning.
+:::
+
 ### Show and drop index
 
 **Syntax**
@@ -837,6 +845,39 @@ WITH (
 );
 ```
 
+### Create Append-Only Table Without Primary Key
+
+Hudi 1.2.0 supports creating a Flink table **without a `PRIMARY KEY`** for pure append workloads.
+In this mode, set `write.operation` to `insert`; Hudi will not enforce record-level uniqueness and
+the record-key and ordering fields are optional.
+
+```sql
+-- Append-only table: no PRIMARY KEY required
+CREATE TABLE hudi_append_table (
+  id      BIGINT,
+  name    STRING,
+  ts      BIGINT,
+  city    STRING
+)
+PARTITIONED BY (`city`)
+WITH (
+  'connector'       = 'hudi',
+  'path'            = 'file:///tmp/hudi_append_table',
+  'table.type'      = 'COPY_ON_WRITE',
+  'write.operation' = 'insert'
+);
+
+INSERT INTO hudi_append_table VALUES (1, 'Alice', 1695159649, 'sf'), (2, 'Bob', 1695091554, 'ny');
+```
+
+:::note
+Without a primary key, Hudi uses auto-generated record keys and does **not** perform deduplication
+or upsert merging. This is equivalent to `bulk_insert` semantics and is well suited for log/event
+ingestion pipelines where every incoming row should be appended as-is.
+If `write.operation` is any value other than `insert` and no `PRIMARY KEY` is defined, Hudi will
+throw `"Primary key definition is missing"` at table creation time.
+:::
+
 ### Create Table in Non-Blocking Concurrency Control Mode
 
 The following is an example of creating a Flink table in [Non-Blocking Concurrency Control mode](concurrency_control.md#non-blocking-concurrency-control).
@@ -967,3 +1008,15 @@ WITH (
 | numeric       |              | not supported |
 | null          |              | not supported |
 | object        |              | not supported |
+
+### AI and Unstructured Data Types
+
+Hudi 1.2.0 introduces two additional column types for AI and unstructured data workloads:
+
+- **`VECTOR(dim[, elementType])`** — stores fixed-dimension embedding vectors (e.g. `VECTOR(768)`,
+  `VECTOR(768, FLOAT)`, `VECTOR(768, DOUBLE)`). Enables approximate nearest-neighbor search via
+  the `hudi_vector_search` TVF. See [Vector Search](vector_search.md) for full details.
+
+- **`BLOB`** — stores arbitrary binary objects (images, audio, documents) either inline within the
+  base file or as external references. See [BLOB / Unstructured Data](blob_unstructured_data.md)
+  for the storage modes, DDL syntax, and read APIs.
diff --git a/website/docs/sql_queries.md b/website/docs/sql_queries.md
index 9310c72fbc62..4b9d6c2dd695 100644
--- a/website/docs/sql_queries.md
+++ b/website/docs/sql_queries.md
@@ -2,7 +2,7 @@
 title: SQL Queries
 summary: "In this page, we go over querying Hudi tables using SQL"
 toc: true
-last_modified_at: 
+last_modified_at: 2026-05-27T00:00:00-00:00
 ---
 import Tabs from '@theme/Tabs';
 import TabItem from '@theme/TabItem';
@@ -14,6 +14,24 @@ This page will show how to issue different queries and discuss any specific inst
 ## Spark SQL
 The Spark [quickstart](quick-start-guide.md) provides a good overview of how to use Spark SQL to query Hudi tables. This section will go into more advanced configurations and functionalities.
 
+:::tip Setting Hudi read options at the session level
+Hudi 1.2.0 supports setting read options at the **Spark session level** using the `spark.hoodie.*` prefix.
+Any `spark.hoodie.X` config set via `spark.conf.set` or `--conf` is treated equivalently to `hoodie.X`.
+
+Config precedence (low → high):
+1. Global DFS properties
+2. `spark.hoodie.*` session-level configs (normalized to `hoodie.*`)
+3. Explicit `hoodie.*` data source options or per-table `SET` commands
+
+```sql
+-- Apply a Hudi read option for the entire session
+SET spark.hoodie.metadata.column.stats.enable = true;
+SELECT * FROM hudi_table WHERE price BETWEEN 10.0 AND 50.0;
+```
+
+If both `spark.hoodie.X` and `hoodie.X` are set, the explicit `hoodie.X` value takes precedence.
+:::
+
 ### Snapshot Query
 Snapshot queries are the most common query type for Hudi tables. Spark SQL supports snapshot queries on both COPY_ON_WRITE and MERGE_ON_READ tables.
 Using session properties, you can specify options around indexing to optimize query performance, as shown below.
@@ -332,6 +350,19 @@ also changed to use completion time. To support compatiblity, Hudi does a checkp
 time to completion time depending on the source table version.
 :::
 
+### Vector Similarity Search
+
+Hudi 1.2.0 introduces a `hudi_vector_search` table-valued function (TVF) for approximate
+nearest-neighbor (ANN) search over `VECTOR` columns. This is an extension of the
+`hudi_table_changes` TVF pattern.
+
+```sql
+-- Find the 10 nearest neighbors to a query vector in the 'embedding' column
+SELECT * FROM hudi_vector_search('db.embeddings_table', 'embedding', ARRAY(0.1, 0.2, ...), 10);
+```
+
+See [Vector Search](vector_search.md) for the full API, supported metrics, and setup instructions.
+
 ### Query Indexes and Timeline
 
 Hudi also allows users to directly query the metadata partitions and check the metadata corresponding to the table
diff --git a/website/docs/syncing_aws_glue_data_catalog.md b/website/docs/syncing_aws_glue_data_catalog.md
index 35f43a8af472..4b1e295dc831 100644
--- a/website/docs/syncing_aws_glue_data_catalog.md
+++ b/website/docs/syncing_aws_glue_data_catalog.md
@@ -54,6 +54,10 @@ hoodie.datasource.meta.sync.glue.partition_index_fields.enable
 hoodie.datasource.meta.sync.glue.partition_index_fields
 ```
 
+## Writer Version Table Property
+
+Hudi 1.2.0 Glue sync writes the table property `hudi_writer_version` (set to the Hudi version that last synced the table) to the Glue Data Catalog entry on every sync, consistent with HMS sync behavior.
+
 ## Other references
 
 ### Running AWS Glue Catalog Sync for Spark DataSource
diff --git a/website/docs/syncing_metastore.md b/website/docs/syncing_metastore.md
index f260a585c1b6..059f172de003 100644
--- a/website/docs/syncing_metastore.md
+++ b/website/docs/syncing_metastore.md
@@ -297,3 +297,44 @@ While using hive beeline query, you need to enter settings:
 ```bash
 set hive.input.format = org.apache.hudi.hadoop.hive.HoodieCombineHiveInputFormat;
 ```
+
+## Spark Catalog Metastore Client
+
+When running Hudi inside a Spark environment that already has Hive support enabled (e.g., SparkSQL with `spark.sql.catalogImplementation=hive`), the standard `IMetaStoreClient` initialization can conflict with Spark's own Hive classloader. Setting
+
+```properties
+hoodie.datasource.hive_sync.use_spark_catalog=true
+```
+
+(default: `false`) makes Hudi use `SparkCatalogMetaStoreClient` — a Spark-native `IMetaStoreClient` implementation — instead of creating its own. This avoids classloader conflicts in Hive-on-Spark setups. Requires a `SparkSession` with Hive support active.
+
+## HMS 4.x Support via JDBC Fallback
+
+HMS 4.x changed several Thrift API method signatures (e.g., `get_table` → `get_table_req`), which makes the standard Thrift-based HMS client incompatible. Hudi 1.2.0 adds automatic fallback: when a Thrift metadata call surfaces a `TApplicationException` anywhere in its cause chain, Hudi flips an internal `thriftIncompatible` flag and reroutes the rest of that sync run through the JDBC path.
+
+**Requirement:** sync mode must be `jdbc` with a valid JDBC URL so the fallback client is available:
+
+```properties
+hoodie.datasource.hive_sync.mode=jdbc
+hoodie.datasource.hive_sync.jdbcurl=jdbc:hive2://hiveserver:10000
+hoodie.datasource.hive_sync.username=<username>
+hoodie.datasource.hive_sync.password=<password>
+```
+
+If the table is synced with `mode=hms` or `mode=hiveql` against HMS 4.x, Hudi logs `"Thrift API incompatible with HMS but no JDBC fallback available. Consider using mode=jdbc with a valid jdbcUrl."` and surfaces the original exception — no automatic recovery happens.
+
+**Detection scope.** The flag is per `HoodieHiveSyncClient` instance, not global, and only transitions from `false` to `true` (it never resets). In practice this means the first Thrift call of each sync run probes once, and the rest of that run uses the JDBC fallback. The next sync run starts with a fresh probe.
+
+**JDBC connection failures surface separately.** With `mode=jdbc`, Hudi opens the JDBC connection eagerly when the sync client is constructed — before any Thrift call is attempted. A bad JDBC URL, missing driver, or wrong credentials therefore fails at startup with `HoodieHiveSyncException: Failed to create HiveMetaStoreClient` and the underlying JDBC exception as the cause in the stack trace. This is a configuration-error path, not an HMS API mismatch, and is the same behavior as `mode= [...]
+
+## Writer Version Table Property
+
+Hudi 1.2.0 sync writes the table property `hudi_writer_version` (set to the Hudi version that last synced the table) to the Hive metastore entry on every sync. This allows tooling and metastore administrators to identify which Hudi version wrote a given table.
+
+To emit `TOUCH` events to the metastore for partition-level change tracking (e.g., for downstream catalog notifications), set:
+
+```properties
+hoodie.meta.sync.touch.partitions.enabled=true
+```
+
+Default is `false`. When enabled, a TOUCH event is issued for each partition that was modified in the sync operation.
diff --git a/website/docs/variant_type.md b/website/docs/variant_type.md
index bf42390afe2f..fdcf6f54b54b 100644
--- a/website/docs/variant_type.md
+++ b/website/docs/variant_type.md
@@ -3,7 +3,7 @@ title: "Semi-Structured Data (VARIANT)"
 keywords: [ hudi, variant, semi-structured, json, schemaless, shredding, parse_json, flexible schema]
 summary: "Store and query semi-structured JSON-like data in Hudi tables using the VARIANT type, with optional shredding for query performance"
 toc: true
-last_modified_at: 2026-04-25T00:00:00-00:00
+last_modified_at: 2026-05-27T00:00:00-00:00
 ---
 
 import Tabs from '@theme/Tabs';
@@ -253,12 +253,25 @@ binary `value` field.
 
 | Engine | VARIANT Support |
 |:-------|:---------------|
-| **Spark 4.0+** | Native `VariantType` — full read/write/query |
+| **Spark 4.0** | Native `VariantType` — full read/write/query for COW and MOR; native `df.write` with `VariantType` on the V1 datasource |
+| **Spark 4.1** | Native `VariantType` — full read/write/query for COW and MOR |
 | **Spark 3.x** | Reads as `STRUCT<value: BINARY, metadata: BINARY>` — backward compatible |
-| **Flink** | Reads as `ROW<metadata BYTES, value BYTES>` — cross-engine compatible |
+| **Flink** | Native `VARIANT` operations are not supported. Tables written by Spark with VARIANT columns can be read in Flink only as the underlying `ROW<metadata BYTES, value BYTES>` struct. |
 
-A VARIANT table written by Spark 4.0 can be read by Spark 3.x or Flink, and vice versa. The
-binary encoding is engine-independent.
+A VARIANT table written by Spark 4.0/4.1 can be read by Spark 3.x using the underlying binary struct, or by Flink as `ROW<metadata BYTES, value BYTES>`. The binary encoding is engine-independent.
+
+## Metastore Sync
+
+When syncing VARIANT column schemas to external catalogs, Hudi maps the binary encoding to the
+target catalog's native struct type:
+
+| Catalog | VARIANT representation |
+|:--------|:----------------------|
+| Hive | `STRUCT<metadata:BINARY, value:BINARY>` |
+| BigQuery | `STRUCT` with `metadata` and `value` fields (`BYTES` type) |
+
+Query engines that support VARIANT (Spark 4.0+, Flink 2.1+) read the table directly using the
+Parquet VARIANT annotation and do not go through the Hive/BigQuery metastore representation.
 
 ## Use Cases for AI Workloads
 
@@ -344,3 +357,6 @@ CREATE TABLE api_responses (
 - Native `VARIANT` keyword in DDL requires Spark 4.0+. On Spark 3.x, use the struct representation.
 - VARIANT shredding configuration is determined at write time based on the schema definition.
 - Complex path expressions within VARIANT may require casting to STRING and then using JSON functions.
+- Native VARIANT operations are not supported on Flink. VARIANT columns surface as `ROW<metadata BYTES, value BYTES>` and can be read but not natively decoded or queried as a variant.
+- VARIANT columns are **not supported** on Lance-backed tables. Use Parquet as the base file format
+  for tables containing VARIANT columns.
diff --git a/website/docs/vector_search.md b/website/docs/vector_search.md
index 9c53443161a7..4e6687057a6c 100644
--- a/website/docs/vector_search.md
+++ b/website/docs/vector_search.md
@@ -1,9 +1,9 @@
 ---
 title: "Vector Search"
-keywords: [ hudi, vector, search, embeddings, similarity, cosine, ANN, nearest neighbor, VECTOR type]
-summary: "Store embedding vectors in Hudi tables and run approximate nearest neighbor search using the VECTOR type and hudi_vector_search TVF"
+keywords: [ hudi, vector, search, embeddings, similarity, cosine, nearest neighbor, VECTOR type]
+summary: "Store embedding vectors in Hudi tables and run vector similarity search using the VECTOR type and hudi_vector_search TVF"
 toc: true
-last_modified_at: 2026-04-25T00:00:00-00:00
+last_modified_at: 2026-05-27T00:00:00-00:00
 ---
 
 import Tabs from '@theme/Tabs';
@@ -13,6 +13,20 @@ Hudi's `VECTOR` type and `hudi_vector_search` table-valued function (TVF) bring
 to the data lakehouse. Store embeddings alongside your structured data and query them with familiar Spark SQL —
 no external vector database required.
 
+## Storage Format
+
+VECTOR columns are stored in Parquet as `FIXED_LEN_BYTE_ARRAY` — a fixed-length binary encoding of the
+float array. Hudi stamps `hudi_type` metadata on the column so the Spark reader knows to decode the
+bytes back into a typed array.
+
+On **Lance** tables, VECTOR columns are stored natively as Lance `FixedSizeList<Float32/Float64, dim>`,
+so embeddings are written without conversion overhead at the file-format layer. See
+[Lance File Format](lance_file_format.md) for details.
+
+The `VECTOR(dim[, elementType])` DDL syntax works across Spark 3.4, 3.5, 4.0, and 4.1. Hudi's SQL
+parser normalizes `VECTOR(128, FLOAT)` to `VECTOR(128)` (FLOAT is the default element type).
+Nesting VECTOR inside STRUCT, ARRAY, or MAP is not supported.
+
 ## VECTOR Type
 
 The `VECTOR(dim[, elementType])` type declares a column that stores fixed-dimensional embedding vectors.
@@ -98,8 +112,8 @@ INSERT INTO products VALUES (
 
 ## hudi_vector_search TVF
 
-The `hudi_vector_search` table-valued function performs approximate nearest neighbor (ANN) search
-over a VECTOR column.
+The `hudi_vector_search` table-valued function returns the `top_k` rows from a Hudi table whose
+VECTOR column is closest to a given query vector under a chosen distance metric.
 
 ### Syntax
 
@@ -240,3 +254,21 @@ FROM hudi_vector_search(
 - VECTOR columns must be **top-level fields** — nesting inside STRUCT, ARRAY, or MAP is not supported.
 - The query vector's element type must **exactly match** the corpus embedding's element type (no implicit casting).
 - VECTOR dimension and element type **cannot be changed** after table creation via schema evolution.
+- **Flink cannot read VECTOR columns.** VECTOR data is stored as Parquet `FIXED_LEN_BYTE_ARRAY`, which
+  Flink's Parquet reader does not decode back into a typed array. Flink can still read all **other**
+  columns in a table that contains a VECTOR column — only the VECTOR column itself is inaccessible.
+  Use Spark to query VECTOR columns.
+
+## Metastore Sync
+
+When syncing VECTOR column schemas to external catalogs, Hudi maps the binary encoding to the
+target catalog's native binary type, preserving the original VECTOR metadata in table properties:
+
+| Catalog | VECTOR representation |
+|:--------|:---------------------|
+| Hive | `BINARY` |
+| BigQuery | `BYTES` |
+
+The `VECTOR(dim, elementType)` dimension and element-type metadata is preserved in
+`TBLPROPERTIES`/table descriptions so the table can be correctly reconstructed by Spark even after
+a metastore round-trip.
diff --git a/website/docs/writing_data.md b/website/docs/writing_data.md
index 5e4570182692..dcdc5a33bd81 100644
--- a/website/docs/writing_data.md
+++ b/website/docs/writing_data.md
@@ -1,7 +1,7 @@
 ---
 title: Batch Writes
 keywords: [hudi, incremental, batch, processing]
-last_modified_at: 2024-03-13T15:59:57-04:00
+last_modified_at: 2026-05-27T00:00:00-00:00
 ---
 import Tabs from '@theme/Tabs';
 import TabItem from '@theme/TabItem';
@@ -441,5 +441,32 @@ inputDF.write.format("hudi")
        .save(basePath)
 ```
 
+### Rolling Extra Metadata
+
+Rolling extra metadata allows you to automatically carry forward selected commit metadata keys to every subsequent commit and clean instant without having to walk the full timeline. This is particularly useful for persisting checkpoint information such as Kafka offsets or Flink checkpoints across commits.
+
+| Config | Default | Description |
+|---|---|---|
+| `hoodie.write.rolling.metadata.keys` | `""` (disabled) | Comma-separated list of extra metadata keys to carry forward to each new commit and clean instant. Values are read from recent completed instants and written into the new commit metadata, so they remain accessible without walking the timeline. New values override old ones. Only applies to data table commits and clean instants. |
+| `hoodie.write.rolling.metadata.timeline.lookback.commits` | `10` | Maximum number of completed instants to walk back when searching for the configured rolling metadata keys. Higher values improve resilience at a small performance cost. |
+
+**Example:**
+
+```java
+inputDF.write.format("hudi")
+       .option("hoodie.write.rolling.metadata.keys", "kafka.offset.partition.0,kafka.offset.partition.1")
+       .option("hoodie.write.rolling.metadata.timeline.lookback.commits", "10")
+       // ... other options
+       .save(basePath)
+```
+
+### Advanced Storage Options
+
+The following advanced storage configuration options were added in Hudi 1.2.0:
+
+| Config | Default | Description |
+|---|---|---|
+| `hoodie.parquet.write.config.injector.class` | (none) | Fully-qualified class name of a custom `HoodieParquetConfigInjector` implementation. Use this to inject custom Parquet writer properties (e.g., disable dictionary encoding, set bloom filter sizes) without modifying the Hudi source. The implementing class must implement `org.apache.hudi.io.HoodieParquetConfigInjector`. |
+
 ## Java Client
 We can use plain java to write to hudi tables. To use Java client we can refere [here](https://github.com/apache/hudi/blob/master/hudi-examples/hudi-examples-java/src/main/java/org/apache/hudi/examples/java/HoodieJavaWriteClientExample.java)