This is an automated email from the ASF dual-hosted git repository.
voonhous 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 8f3432e16a13 docs(performance): document Parquet bloom filters and
separate them from the bloom index (#19590)
8f3432e16a13 is described below
commit 8f3432e16a13e1587272843ce617973e6d44bbb4
Author: Ranga Reddy <[email protected]>
AuthorDate: Sun Aug 30 20:04:56 2026 +0530
docs(performance): document Parquet bloom filters and separate them from
the bloom index (#19590)
Parquet's per-column bloom filters have been passthrough-configurable in
Hudi since 0.14.0, but the
only trace of them in the docs was an auto-generated row in
configurations.md - and that row is for
a different feature (see below). Nothing told a user the keys exist, that
they are per column, or
that they are read from the Hadoop config.
Adds a "Parquet Bloom Filters" subsection to performance.md under Read
Path, next to Data Skipping,
since that is the gap it fills: column stats prune on ranges and are least
useful for an equality
predicate on a high-cardinality column, which is exactly what a bloom
filter covers.
parquet.bloom.filter.enabled#<column> write a bloom filter for that
column
parquet.bloom.filter.expected.ndv#<column> expected distinct values,
sizes the filter
HoodieBaseParquetWriter.handleParquetBloomFilters scans the Hadoop
configuration for those prefixes
and forwards each to the Parquet writer builder, so from Spark they are set
with the spark.hadoop.
prefix. The spelling matches TestHoodieParquetBloom, which sets exactly
these keys on
jsc.hadoopConfiguration.
Two things are called out because both are easy to get wrong:
- hoodie.parquet.bloom.filter.enabled is NOT this feature, despite the
name. It controls whether
Hudi writes a bloom filter of RECORD KEYS into the footer for the bloom
index during upserts; it
defaults to true, applies only when meta fields are populated, and is
implied when
hoodie.index.type names a BLOOM index
(HoodieFileWriterFactory.enableBloomFilter). Setting it
does not enable per-column Parquet filters.
- the settings are applied by reflection and the NoSuchMethodException is
swallowed, so on a
Parquet without withBloomFilterEnabled / withBloomFilterNDV they are
silently ignored.
Applied to the current docs and to every supported versioned copy - 1.2.0,
1.1.1, 1.0.2, 1.0.1 and
1.0.0 - because the feature ships in all of them: handleParquetBloomFilters
is absent at
release-0.13.1 and present from release-0.14.0 onwards, and the
hoodie.parquet.bloom.filter.enabled
config it is contrasted against reads
defaultValue(true)/sinceVersion(0.15.0) identically at
release-1.0.0, 1.0.2, 1.1.1 and 1.2.0, so the text is accurate for each.
That five-version fan-out
matches how other cross-version docs fixes have been applied (#19555,
#19459). version-1.0.0 has no
"Related Resources" heading, so there the section is appended at the end of
the page instead of
before it; the inserted text is byte-identical in all six files.
The 0.15.x and 0.14.x copies are left alone: they have had no or nearly no
doc commits in the last
six months and are effectively frozen.
Review follow-up: says what the read side needs, since the section sits
under Read Path while the
keys it documents are write-side. On Spark 3.x nothing extra is configured
- reading back through the
Hudi datasource consults the filters given a pushed-down equality
predicate, and
TestHoodieParquetBloomFilter pins that by disabling the statistics and
column-index filter levels so
only a bloom can cause a skip, then asserting zero row groups scanned for
an absent value against one
for a present value. parquet.filter.bloom.enabled is left at its parquet-mr
default and Hudi never
sets it. Also renamed the example column from uuid, which read like a
record key, and said outright
that <column> is any data column filtered by equality rather than the
record key.
Closes #16063
---
website/docs/performance.md | 50 ++++++++++++++++++++++
.../versioned_docs/version-1.0.0/performance.md | 50 ++++++++++++++++++++++
.../versioned_docs/version-1.0.1/performance.md | 50 ++++++++++++++++++++++
.../versioned_docs/version-1.0.2/performance.md | 50 ++++++++++++++++++++++
.../versioned_docs/version-1.1.1/performance.md | 50 ++++++++++++++++++++++
.../versioned_docs/version-1.2.0/performance.md | 50 ++++++++++++++++++++++
6 files changed, 300 insertions(+)
diff --git a/website/docs/performance.md b/website/docs/performance.md
index 7c6d489c3732..e540e770fb75 100644
--- a/website/docs/performance.md
+++ b/website/docs/performance.md
@@ -132,6 +132,56 @@ To enable Data Skipping in your queries make sure to set
following properties to
- `hoodie.metadata.enable` (to enable metadata table use on the read path,
enabled by default)
- `hoodie.metadata.index.column.stats.enable` (to enable column stats index
use on the read path)
+#### Parquet Bloom Filters
+
+Column stats prune on ranges, so they help least where they are needed most:
an equality predicate on a
+high-cardinality column whose min-max range covers almost every file.
Parquet's own bloom filters cover that
+case. They are written into the Parquet file itself, and a reader consults
them to skip row groups that
+cannot contain the value being searched for.
+
+Hudi passes these through to the Parquet writer, per column, from the
**Hadoop** configuration:
+
+| key | meaning |
+| --- | --- |
+| `parquet.bloom.filter.enabled#<column>` | write a bloom filter for
`<column>` |
+| `parquet.bloom.filter.expected.ndv#<column>` | expected number of distinct
values, which sizes the filter |
+
+`<column>` is any data column you filter on by equality — not the record key,
and unrelated to the record-key
+bloom index discussed in the note below. Set the keys on the Hadoop
configuration your writer uses; from Spark
+the `spark.hadoop.` prefix forwards them:
+
+```
+--conf spark.hadoop.parquet.bloom.filter.enabled#session_id=true
+--conf spark.hadoop.parquet.bloom.filter.expected.ndv#session_id=100000
+```
+
+Give `expected.ndv` a realistic estimate for the column. Too low and the
filter saturates and stops
+eliminating anything; too high and you pay in file size for nothing.
+
+This is a write-time decision: only files written after you set it carry the
filters, so an existing table
+picks them up as it is rewritten by ongoing writes, compaction or clustering.
+
+On the read side nothing extra needs configuring for Spark 3.x. Reading the
table back through the Hudi
+datasource consults the filters, provided the query carries an equality
predicate that can be pushed down to
+the Parquet reader — a query filtering on a value no row group contains skips
those row groups entirely.
+Parquet's own read-side switch, `parquet.filter.bloom.enabled`, is left at its
default and Hudi never
+overrides it, so there is no reader-side flag to turn on.
+
+:::note
+Do not confuse the keys above with Hudi's own
`hoodie.parquet.bloom.filter.enabled`, which is a different
+feature despite the near-identical name. That config controls whether Hudi
writes a bloom filter **of record
+keys** into the file footer for use by the [bloom index](indexes.md) during
upserts; it defaults to `true`,
+applies only when meta fields are populated, and is implied anyway when
`hoodie.index.type` names a `BLOOM`
+index. It has nothing to do with per-column skipping on the read path, and
setting it does not enable the
+Parquet column filters described here.
+:::
+
+:::caution
+Hudi applies these settings reflectively, so if the Parquet version on your
classpath predates the
+`withBloomFilterEnabled` / `withBloomFilterNDV` builder methods, the keys are
**silently ignored** rather
+than rejected. If you see no change in file size or query behaviour, check
your Parquet version first.
+:::
+
## Related Resources
<h3>Blogs</h3>
diff --git a/website/versioned_docs/version-1.0.0/performance.md
b/website/versioned_docs/version-1.0.0/performance.md
index fece8c260b18..1840d0f65ab9 100644
--- a/website/versioned_docs/version-1.0.0/performance.md
+++ b/website/versioned_docs/version-1.0.0/performance.md
@@ -131,3 +131,53 @@ To enable Data Skipping in your queries make sure to set
following properties to
- `hoodie.enable.data.skipping` (to control data skipping, enabled by
default)
- `hoodie.metadata.enable` (to enable metadata table use on the read path,
enabled by default)
- `hoodie.metadata.index.column.stats.enable` (to enable column stats index
use on the read path)
+
+#### Parquet Bloom Filters
+
+Column stats prune on ranges, so they help least where they are needed most:
an equality predicate on a
+high-cardinality column whose min-max range covers almost every file.
Parquet's own bloom filters cover that
+case. They are written into the Parquet file itself, and a reader consults
them to skip row groups that
+cannot contain the value being searched for.
+
+Hudi passes these through to the Parquet writer, per column, from the
**Hadoop** configuration:
+
+| key | meaning |
+| --- | --- |
+| `parquet.bloom.filter.enabled#<column>` | write a bloom filter for
`<column>` |
+| `parquet.bloom.filter.expected.ndv#<column>` | expected number of distinct
values, which sizes the filter |
+
+`<column>` is any data column you filter on by equality — not the record key,
and unrelated to the record-key
+bloom index discussed in the note below. Set the keys on the Hadoop
configuration your writer uses; from Spark
+the `spark.hadoop.` prefix forwards them:
+
+```
+--conf spark.hadoop.parquet.bloom.filter.enabled#session_id=true
+--conf spark.hadoop.parquet.bloom.filter.expected.ndv#session_id=100000
+```
+
+Give `expected.ndv` a realistic estimate for the column. Too low and the
filter saturates and stops
+eliminating anything; too high and you pay in file size for nothing.
+
+This is a write-time decision: only files written after you set it carry the
filters, so an existing table
+picks them up as it is rewritten by ongoing writes, compaction or clustering.
+
+On the read side nothing extra needs configuring for Spark 3.x. Reading the
table back through the Hudi
+datasource consults the filters, provided the query carries an equality
predicate that can be pushed down to
+the Parquet reader — a query filtering on a value no row group contains skips
those row groups entirely.
+Parquet's own read-side switch, `parquet.filter.bloom.enabled`, is left at its
default and Hudi never
+overrides it, so there is no reader-side flag to turn on.
+
+:::note
+Do not confuse the keys above with Hudi's own
`hoodie.parquet.bloom.filter.enabled`, which is a different
+feature despite the near-identical name. That config controls whether Hudi
writes a bloom filter **of record
+keys** into the file footer for use by the [bloom index](indexes.md) during
upserts; it defaults to `true`,
+applies only when meta fields are populated, and is implied anyway when
`hoodie.index.type` names a `BLOOM`
+index. It has nothing to do with per-column skipping on the read path, and
setting it does not enable the
+Parquet column filters described here.
+:::
+
+:::caution
+Hudi applies these settings reflectively, so if the Parquet version on your
classpath predates the
+`withBloomFilterEnabled` / `withBloomFilterNDV` builder methods, the keys are
**silently ignored** rather
+than rejected. If you see no change in file size or query behaviour, check
your Parquet version first.
+:::
diff --git a/website/versioned_docs/version-1.0.1/performance.md
b/website/versioned_docs/version-1.0.1/performance.md
index 7c294e57886a..384d31c16fd8 100644
--- a/website/versioned_docs/version-1.0.1/performance.md
+++ b/website/versioned_docs/version-1.0.1/performance.md
@@ -132,6 +132,56 @@ To enable Data Skipping in your queries make sure to set
following properties to
- `hoodie.metadata.enable` (to enable metadata table use on the read path,
enabled by default)
- `hoodie.metadata.index.column.stats.enable` (to enable column stats index
use on the read path)
+#### Parquet Bloom Filters
+
+Column stats prune on ranges, so they help least where they are needed most:
an equality predicate on a
+high-cardinality column whose min-max range covers almost every file.
Parquet's own bloom filters cover that
+case. They are written into the Parquet file itself, and a reader consults
them to skip row groups that
+cannot contain the value being searched for.
+
+Hudi passes these through to the Parquet writer, per column, from the
**Hadoop** configuration:
+
+| key | meaning |
+| --- | --- |
+| `parquet.bloom.filter.enabled#<column>` | write a bloom filter for
`<column>` |
+| `parquet.bloom.filter.expected.ndv#<column>` | expected number of distinct
values, which sizes the filter |
+
+`<column>` is any data column you filter on by equality — not the record key,
and unrelated to the record-key
+bloom index discussed in the note below. Set the keys on the Hadoop
configuration your writer uses; from Spark
+the `spark.hadoop.` prefix forwards them:
+
+```
+--conf spark.hadoop.parquet.bloom.filter.enabled#session_id=true
+--conf spark.hadoop.parquet.bloom.filter.expected.ndv#session_id=100000
+```
+
+Give `expected.ndv` a realistic estimate for the column. Too low and the
filter saturates and stops
+eliminating anything; too high and you pay in file size for nothing.
+
+This is a write-time decision: only files written after you set it carry the
filters, so an existing table
+picks them up as it is rewritten by ongoing writes, compaction or clustering.
+
+On the read side nothing extra needs configuring for Spark 3.x. Reading the
table back through the Hudi
+datasource consults the filters, provided the query carries an equality
predicate that can be pushed down to
+the Parquet reader — a query filtering on a value no row group contains skips
those row groups entirely.
+Parquet's own read-side switch, `parquet.filter.bloom.enabled`, is left at its
default and Hudi never
+overrides it, so there is no reader-side flag to turn on.
+
+:::note
+Do not confuse the keys above with Hudi's own
`hoodie.parquet.bloom.filter.enabled`, which is a different
+feature despite the near-identical name. That config controls whether Hudi
writes a bloom filter **of record
+keys** into the file footer for use by the [bloom index](indexes.md) during
upserts; it defaults to `true`,
+applies only when meta fields are populated, and is implied anyway when
`hoodie.index.type` names a `BLOOM`
+index. It has nothing to do with per-column skipping on the read path, and
setting it does not enable the
+Parquet column filters described here.
+:::
+
+:::caution
+Hudi applies these settings reflectively, so if the Parquet version on your
classpath predates the
+`withBloomFilterEnabled` / `withBloomFilterNDV` builder methods, the keys are
**silently ignored** rather
+than rejected. If you see no change in file size or query behaviour, check
your Parquet version first.
+:::
+
## Related Resources
<h3>Blogs</h3>
diff --git a/website/versioned_docs/version-1.0.2/performance.md
b/website/versioned_docs/version-1.0.2/performance.md
index 7c294e57886a..384d31c16fd8 100644
--- a/website/versioned_docs/version-1.0.2/performance.md
+++ b/website/versioned_docs/version-1.0.2/performance.md
@@ -132,6 +132,56 @@ To enable Data Skipping in your queries make sure to set
following properties to
- `hoodie.metadata.enable` (to enable metadata table use on the read path,
enabled by default)
- `hoodie.metadata.index.column.stats.enable` (to enable column stats index
use on the read path)
+#### Parquet Bloom Filters
+
+Column stats prune on ranges, so they help least where they are needed most:
an equality predicate on a
+high-cardinality column whose min-max range covers almost every file.
Parquet's own bloom filters cover that
+case. They are written into the Parquet file itself, and a reader consults
them to skip row groups that
+cannot contain the value being searched for.
+
+Hudi passes these through to the Parquet writer, per column, from the
**Hadoop** configuration:
+
+| key | meaning |
+| --- | --- |
+| `parquet.bloom.filter.enabled#<column>` | write a bloom filter for
`<column>` |
+| `parquet.bloom.filter.expected.ndv#<column>` | expected number of distinct
values, which sizes the filter |
+
+`<column>` is any data column you filter on by equality — not the record key,
and unrelated to the record-key
+bloom index discussed in the note below. Set the keys on the Hadoop
configuration your writer uses; from Spark
+the `spark.hadoop.` prefix forwards them:
+
+```
+--conf spark.hadoop.parquet.bloom.filter.enabled#session_id=true
+--conf spark.hadoop.parquet.bloom.filter.expected.ndv#session_id=100000
+```
+
+Give `expected.ndv` a realistic estimate for the column. Too low and the
filter saturates and stops
+eliminating anything; too high and you pay in file size for nothing.
+
+This is a write-time decision: only files written after you set it carry the
filters, so an existing table
+picks them up as it is rewritten by ongoing writes, compaction or clustering.
+
+On the read side nothing extra needs configuring for Spark 3.x. Reading the
table back through the Hudi
+datasource consults the filters, provided the query carries an equality
predicate that can be pushed down to
+the Parquet reader — a query filtering on a value no row group contains skips
those row groups entirely.
+Parquet's own read-side switch, `parquet.filter.bloom.enabled`, is left at its
default and Hudi never
+overrides it, so there is no reader-side flag to turn on.
+
+:::note
+Do not confuse the keys above with Hudi's own
`hoodie.parquet.bloom.filter.enabled`, which is a different
+feature despite the near-identical name. That config controls whether Hudi
writes a bloom filter **of record
+keys** into the file footer for use by the [bloom index](indexes.md) during
upserts; it defaults to `true`,
+applies only when meta fields are populated, and is implied anyway when
`hoodie.index.type` names a `BLOOM`
+index. It has nothing to do with per-column skipping on the read path, and
setting it does not enable the
+Parquet column filters described here.
+:::
+
+:::caution
+Hudi applies these settings reflectively, so if the Parquet version on your
classpath predates the
+`withBloomFilterEnabled` / `withBloomFilterNDV` builder methods, the keys are
**silently ignored** rather
+than rejected. If you see no change in file size or query behaviour, check
your Parquet version first.
+:::
+
## Related Resources
<h3>Blogs</h3>
diff --git a/website/versioned_docs/version-1.1.1/performance.md
b/website/versioned_docs/version-1.1.1/performance.md
index 1e587e563681..63228a0309f8 100644
--- a/website/versioned_docs/version-1.1.1/performance.md
+++ b/website/versioned_docs/version-1.1.1/performance.md
@@ -132,6 +132,56 @@ To enable Data Skipping in your queries make sure to set
following properties to
- `hoodie.metadata.enable` (to enable metadata table use on the read path,
enabled by default)
- `hoodie.metadata.index.column.stats.enable` (to enable column stats index
use on the read path)
+#### Parquet Bloom Filters
+
+Column stats prune on ranges, so they help least where they are needed most:
an equality predicate on a
+high-cardinality column whose min-max range covers almost every file.
Parquet's own bloom filters cover that
+case. They are written into the Parquet file itself, and a reader consults
them to skip row groups that
+cannot contain the value being searched for.
+
+Hudi passes these through to the Parquet writer, per column, from the
**Hadoop** configuration:
+
+| key | meaning |
+| --- | --- |
+| `parquet.bloom.filter.enabled#<column>` | write a bloom filter for
`<column>` |
+| `parquet.bloom.filter.expected.ndv#<column>` | expected number of distinct
values, which sizes the filter |
+
+`<column>` is any data column you filter on by equality — not the record key,
and unrelated to the record-key
+bloom index discussed in the note below. Set the keys on the Hadoop
configuration your writer uses; from Spark
+the `spark.hadoop.` prefix forwards them:
+
+```
+--conf spark.hadoop.parquet.bloom.filter.enabled#session_id=true
+--conf spark.hadoop.parquet.bloom.filter.expected.ndv#session_id=100000
+```
+
+Give `expected.ndv` a realistic estimate for the column. Too low and the
filter saturates and stops
+eliminating anything; too high and you pay in file size for nothing.
+
+This is a write-time decision: only files written after you set it carry the
filters, so an existing table
+picks them up as it is rewritten by ongoing writes, compaction or clustering.
+
+On the read side nothing extra needs configuring for Spark 3.x. Reading the
table back through the Hudi
+datasource consults the filters, provided the query carries an equality
predicate that can be pushed down to
+the Parquet reader — a query filtering on a value no row group contains skips
those row groups entirely.
+Parquet's own read-side switch, `parquet.filter.bloom.enabled`, is left at its
default and Hudi never
+overrides it, so there is no reader-side flag to turn on.
+
+:::note
+Do not confuse the keys above with Hudi's own
`hoodie.parquet.bloom.filter.enabled`, which is a different
+feature despite the near-identical name. That config controls whether Hudi
writes a bloom filter **of record
+keys** into the file footer for use by the [bloom index](indexes.md) during
upserts; it defaults to `true`,
+applies only when meta fields are populated, and is implied anyway when
`hoodie.index.type` names a `BLOOM`
+index. It has nothing to do with per-column skipping on the read path, and
setting it does not enable the
+Parquet column filters described here.
+:::
+
+:::caution
+Hudi applies these settings reflectively, so if the Parquet version on your
classpath predates the
+`withBloomFilterEnabled` / `withBloomFilterNDV` builder methods, the keys are
**silently ignored** rather
+than rejected. If you see no change in file size or query behaviour, check
your Parquet version first.
+:::
+
## Related Resources
<h3>Blogs</h3>
diff --git a/website/versioned_docs/version-1.2.0/performance.md
b/website/versioned_docs/version-1.2.0/performance.md
index 7c6d489c3732..e540e770fb75 100644
--- a/website/versioned_docs/version-1.2.0/performance.md
+++ b/website/versioned_docs/version-1.2.0/performance.md
@@ -132,6 +132,56 @@ To enable Data Skipping in your queries make sure to set
following properties to
- `hoodie.metadata.enable` (to enable metadata table use on the read path,
enabled by default)
- `hoodie.metadata.index.column.stats.enable` (to enable column stats index
use on the read path)
+#### Parquet Bloom Filters
+
+Column stats prune on ranges, so they help least where they are needed most:
an equality predicate on a
+high-cardinality column whose min-max range covers almost every file.
Parquet's own bloom filters cover that
+case. They are written into the Parquet file itself, and a reader consults
them to skip row groups that
+cannot contain the value being searched for.
+
+Hudi passes these through to the Parquet writer, per column, from the
**Hadoop** configuration:
+
+| key | meaning |
+| --- | --- |
+| `parquet.bloom.filter.enabled#<column>` | write a bloom filter for
`<column>` |
+| `parquet.bloom.filter.expected.ndv#<column>` | expected number of distinct
values, which sizes the filter |
+
+`<column>` is any data column you filter on by equality — not the record key,
and unrelated to the record-key
+bloom index discussed in the note below. Set the keys on the Hadoop
configuration your writer uses; from Spark
+the `spark.hadoop.` prefix forwards them:
+
+```
+--conf spark.hadoop.parquet.bloom.filter.enabled#session_id=true
+--conf spark.hadoop.parquet.bloom.filter.expected.ndv#session_id=100000
+```
+
+Give `expected.ndv` a realistic estimate for the column. Too low and the
filter saturates and stops
+eliminating anything; too high and you pay in file size for nothing.
+
+This is a write-time decision: only files written after you set it carry the
filters, so an existing table
+picks them up as it is rewritten by ongoing writes, compaction or clustering.
+
+On the read side nothing extra needs configuring for Spark 3.x. Reading the
table back through the Hudi
+datasource consults the filters, provided the query carries an equality
predicate that can be pushed down to
+the Parquet reader — a query filtering on a value no row group contains skips
those row groups entirely.
+Parquet's own read-side switch, `parquet.filter.bloom.enabled`, is left at its
default and Hudi never
+overrides it, so there is no reader-side flag to turn on.
+
+:::note
+Do not confuse the keys above with Hudi's own
`hoodie.parquet.bloom.filter.enabled`, which is a different
+feature despite the near-identical name. That config controls whether Hudi
writes a bloom filter **of record
+keys** into the file footer for use by the [bloom index](indexes.md) during
upserts; it defaults to `true`,
+applies only when meta fields are populated, and is implied anyway when
`hoodie.index.type` names a `BLOOM`
+index. It has nothing to do with per-column skipping on the read path, and
setting it does not enable the
+Parquet column filters described here.
+:::
+
+:::caution
+Hudi applies these settings reflectively, so if the Parquet version on your
classpath predates the
+`withBloomFilterEnabled` / `withBloomFilterNDV` builder methods, the keys are
**silently ignored** rather
+than rejected. If you see no change in file size or query behaviour, check
your Parquet version first.
+:::
+
## Related Resources
<h3>Blogs</h3>