Skip to content

Add ArrowSbbf to check Arrow arrays against Parquet bloom filters - #11549

Open
bhaskargurram-ai wants to merge 11 commits into
apache:mainfrom
bhaskargurram-ai:fix/bloom-filter-arrow-check
Open

bhaskargurram-ai wants to merge 11 commits into
apache:mainfrom
bhaskargurram-ai:fix/bloom-filter-arrow-check

Conversation

@bhaskargurram-ai

@bhaskargurram-ai bhaskargurram-ai commented Oct 14, 2026 •

Copy link
Copy Markdown
Contributor

Which issue does this PR close?

Rationale for this change

ArrowWriter converts some Arrow types before writing them (for example Int8/Int16/UInt8/UInt16 to INT32, Date64 to INT32 days, decimals to INT32, INT64 or FIXED_LEN_BYTE_ARRAY), and the bloom filter is built from the converted values. Calling Sbbf::check with the original Arrow value hashes different bytes, so values that are in the file are reported as absent.

What changes are included in this PR?

  • A new parquet::arrow::bloom_filter::ArrowSbbf, created with ArrowSbbf::try_new(sbbf, column, arrow_type). arrow_type is the Arrow type of the arrays to check; if None it is derived from the column with parquet_to_arrow_field, as the reader does.
  • try_new uses the writer's arrow_to_parquet_type to check once that values of that type are stored in the column the same way. It returns an error for a different physical type or length, a different decimal scale, or a different time unit.
  • check takes an array of exactly that type (or a dictionary or run-end encoded array of it, or a NullArray) and passes the values through the writer's own conversions (visit_i32, visit_i64, visit_fixed_len_byte_array), returning a BooleanArray with nulls kept.
  • Decimals are resized to the column's byte length, so columns from writers that store decimals in wider fixed-length arrays can be checked. For this the writer's FLBA decimal conversion now sign-extends when the target is longer, and visit_i64 accepts Decimal32.
  • The Sbbf::check docs point Arrow users to ArrowSbbf.

Are these changes tested?

Yes. Round-trip tests write each type with a bloom filter, read it back, and check that written values are found and others are not: integer widths, unsigned values above the signed max, Date64 with and without coerce_types, decimals of several precisions, FixedSizeBinary, Float16, intervals, strings, nulls, dictionary and run-end encoded arrays. every_writer_type checks that try_new(.., None) accepts every column the writer produces. Error tests cover mismatched types, decimal scales, lengths and time units.

Are there any user-facing changes?

A new public API, ArrowSbbf. No existing APIs change.

…rowWriter

ArrowWriter converts some Arrow types before writing them (Int8, Int16,
UInt8 and UInt16 to INT32, Date64 to INT32 days, decimals to INT32,
INT64 or FIXED_LEN_BYTE_ARRAY, ...) and builds the bloom filter from the
converted values, so Sbbf::check with the original Arrow value reports
values that are present as absent.

Move the writer's INT32, INT64 and FIXED_LEN_BYTE_ARRAY conversions into
shared helpers and add parquet::arrow::bloom_filter::ArrowSbbf, whose
check(&dyn Array) applies the same conversion and returns a BooleanArray.

Closes apache#5550
@github-actions github-actions Bot added parquet Changes to the parquet crate bug labels Oct 14, 2026

@mbutrovich mbutrovich left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @bhaskargurram-ai, took a first pass.

Comment thread parquet/src/arrow/arrow_writer/mod.rs Resolved
Comment thread parquet/src/arrow/bloom_filter.rs Resolved
…ArrowSbbf

ArrowSbbf::check converted decimals for a FIXED_LEN_BYTE_ARRAY column to
the byte width of the checked array's precision, but the writer uses the
column's width, so a value written at precision 30 (13 bytes) was not
found when checked with a precision 20 or 38 array. Convert decimals to
the column's type_length instead: sign-extend narrower values and drop
leading sign bytes from wider ones, reporting a value that does not fit
as absent.

Decimal values with a different scale than the column's are different
unscaled integers, so checking them now returns an error, as does
checking a FixedSizeBinary array whose length differs from the column's.
Null arrays now return all nulls for any column type.

Replace coerce_to_i32, coerce_to_i64 and coerce_to_fixed_len_byte_array,
which collected the converted values into buffers, with visit_i32,
visit_i64 and visit_fixed_len_byte_array. They keep the single copy of
the conversion from each Arrow type and pass either the Arrow values as
they are or a per-value conversion to a visitor. The writer's visitors
borrow the Arrow buffer when no conversion is needed and collect
converted values as before, while ArrowSbbf hashes each value without
collecting them. Decimal32 values written to an INT32 column are no
longer copied.

@adriangb adriangb left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, this is a good fix!

Some requests:

  • We need a arrow_writer benchmark run (before and after). This PR changes the writer hot path for INT32, INT64 and FIXED_LEN_BYTE_ARRAY. Ill kick it off.
  • Option 1 in #5550 asks for a per-value conversion that callers can use before Sbbf::check. The visit_* functions are pub(crate), so this PR gives only option 2. Please update the PR description, or make a per-value conversion public.
  • #5550 also asks to document which Arrow types change their physical representation. A short table in the ArrowSbbf docs would be sufficient.

I ran randomized round-trip tests over all writer types (about 15k configurations and 114k cross-precision decimal checks) and cargo-mutants on the changed code. I found no false negatives for in-range values and no panics. The writer output is byte-identical to the base commit. The inline comments show the gaps that I found.

Comment thread parquet/src/arrow/arrow_writer/mod.rs Outdated
let indices = self.levels.non_null_indices().iter().copied();
// A value that does not fit is truncated, as the writer has always done
let values = chunk_array_slice(length, indices, |i, value| {
fill(i, value);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The writer ignores the bool, but it still pays for the overflow check on each value (dropped.iter().all(..)). Before this PR the width check was done once, outside the loop. Please confirm with a benchmark that this has no cost for Decimal128 to FIXED_LEN_BYTE_ARRAY.

Also, the old assert!(chunk_size <= T::BYTE_LENGTH) is gone. Was that intentional?

Comment thread parquet/src/arrow/arrow_writer/mod.rs Outdated
Comment on lines +1912 to +1916
out.copy_from_slice(kept);
let sign = if kept.first().is_some_and(|b| (*b as i8) < 0) {
0xFF
} else {
0

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This sign-byte expression is also used above. A small helper removes the duplicate:

Suggested change
out.copy_from_slice(kept);
let sign = if kept.first().is_some_and(|b| (*b as i8) < 0) {
0xFF
} else {
0
let sign = sign_byte(kept);
fn sign_byte(bytes: &[u8]) -> u8 {
    if bytes.first().is_some_and(|b| (*b as i8) < 0) { 0xFF } else { 0 }
}

Comment thread parquet/src/arrow/bloom_filter.rs Outdated
sbbf: &'a Sbbf,
physical_type: PhysicalType,
/// The length of the column's values if it is a `FIXED_LEN_BYTE_ARRAY` column
type_length: i32,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is cast as usize later. Do the conversion once in new and store a usize:

Suggested change
type_length: i32,
type_length: usize,

Comment thread parquet/src/arrow/bloom_filter.rs Outdated
// Never negative for a FIXED_LEN_BYTE_ARRAY column, which the schema checks
type_length: self.type_length as usize,
};
visit_fixed_len_byte_array(array, visitor)??

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: the double ?? is hard to read. Maybe make the visitor Output the inner type, or add a short comment that explains the two layers of Result.

Comment thread parquet/src/arrow/bloom_filter.rs Outdated
DataType::Decimal32(_, scale)
| DataType::Decimal64(_, scale)
| DataType::Decimal128(_, scale)
| DataType::Decimal256(_, scale) => self.validate_decimal_scale(*scale)?,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The check rejects decimal values for a non-decimal column. But the reverse is not rejected: an Int8 array checked against an INT32 decimal or Date32 column is hashed with no error. This cannot give a false negative, but the behavior is not symmetric. Should a decimal column also require a decimal array?

},
ArrowDataType::Decimal64(_, _) => {
visitor.slice(column.as_primitive::<Decimal64Type>().values())
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

visit_i64 has no Decimal32 arm. Thus check with a Decimal32(9, 2) array against a Decimal64(12, 2) column returns Cannot coerce Decimal32(9, 2) to I64. The ArrowSbbf docs say that decimals of any precision can be checked against a column of the same scale.

Suggested change
}
}
ArrowDataType::Decimal32(_, _) => {
visitor.map(column.as_primitive::<Decimal32Type>().values(), |v| v as i64)
}

Comment thread parquet/src/arrow/arrow_writer/mod.rs Resolved
}

match array.data_type() {
DataType::Null => return Ok(BooleanArray::new_null(array.len())),

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The tests pass if this arm is removed, because they check Null only against an INT32 column. Please add a test that checks NullArray::new(2) against a Float64 or Utf8 column and expects [None, None].

Comment thread parquet/src/arrow/bloom_filter.rs Outdated
impl FixedLenByteArrayVisitor for CheckFixedLenByteArray<'_> {
type Output = Result<BooleanBuffer>;

fn decimal_length(&self, _precision: u8) -> usize {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Arrow does not validate decimal precision, so Decimal128(20, 2) can hold i128::MAX / 3. For a FIXED_LEN_BYTE_ARRAY column the writer truncates this value, but check on the same array returns false. For INT32 and INT64 columns, check truncates like the writer and returns true. Please make the two paths the same, or document the difference.

| DataType::Decimal64(_, scale)
| DataType::Decimal128(_, scale)
| DataType::Decimal256(_, scale) => self.validate_decimal_scale(*scale)?,
_ => {}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Two small points:

  • The writer accepts RunEndEncoded columns, but check returns Cannot coerce RunEndEncoded(..) to I32. Please support REE (check the values, then map by run ends), or document this.
  • A type mismatch against a FIXED_LEN_BYTE_ARRAY column returns NYI: Attempting to write an Arrow type that is not yet implemented. That message is incorrect for a check.

@adriangb

Copy link
Copy Markdown
Contributor

run benchmark arrow_writer

@adriangbot

Copy link
Copy Markdown

🤖 Benchmark starting (GKE) | trigger

Target: arrow_writer

Sharding factor: 1 (1 workers).

Comparing fix/bloom-filter-arrow-check (8b4abea) to bece8ca (merge-base) diff

Run configuration
run benchmark arrow_writer
shards: 1

Results will be posted when all workers finish.


File an issue against this benchmark runner

@adriangb

Copy link
Copy Markdown
Contributor

run benchmark arrow_writer

shards: 4

@adriangbot

Copy link
Copy Markdown

🤖 Benchmark starting (GKE) | trigger

Target: arrow_writer

Sharding factor: 4 (4 workers).

Comparing fix/bloom-filter-arrow-check (8b4abea) to bece8ca (merge-base) diff

Run configuration
run benchmark arrow_writer
shards: 4

Results will be posted when all workers finish.


File an issue against this benchmark runner

…in ArrowSbbf

- Choose the decimal resize once per array instead of checking each value,
  so the writer's FIXED_LEN_BYTE_ARRAY loop is the same as before, and
  restore the assert that the precision fits the native decimal type.
- Truncate decimals that are too large for a FIXED_LEN_BYTE_ARRAY column in
  ArrowSbbf the same way the writer does, as the INT32 and INT64 paths
  already do, so a written value is never reported as absent.
- Check run-end encoded arrays by their values, and return a check-specific
  error for an Arrow type that cannot be stored in the column.
- Require a decimal array for a decimal column, as well as the reverse.
- Check Decimal32 values against INT64 decimal columns.
- Visitor methods return Result, so the visit functions return a single
  Result. Store the column length as usize. Add a sign_byte helper.
- Document which Arrow types change their physical representation.
- Test Date64 to days, Null arrays against DOUBLE and BYTE_ARRAY columns,
  Decimal32 against an INT64 column, out-of-precision decimals, REE arrays
  and the new errors.
@adriangbot

Copy link
Copy Markdown

🤖 Benchmark completed (GKE) | trigger

Target: arrow_writer

Comparing fix/bloom-filter-arrow-check (8b4abea) to bece8ca (merge-base) diff

Run configuration
run benchmark arrow_writer
shards: 4

Comparison results: View the complete native critcmp output.

The comparison is published in an unlisted Gist because the original inline report exceeded GitHub's comment limit.

Per-runner information
arrow_writer — shard 1/4

Node: gk3-benchmark-cluster-nap-1iq4pjyb-5c94fccf-v9sf

Instance: c4a-highmem-16 (12 vCPU / 65 GiB)

uname:

Linux bench-c6250583741-3568-fk95k 6.12.94+ #1 SMP Fri Aug 21 08:00:16 UTC 2026 aarch64 GNU/Linux

BENCH_COMMAND:

cargo bench --features=arrow,async,test_common,experimental,object_store --bench arrow_writer
CPU Details (lscpu)
Architecture:                            aarch64
CPU op-mode(s):                          64-bit
Byte Order:                              Little Endian
CPU(s):                                  16
On-line CPU(s) list:                     0-15
Vendor ID:                               ARM
Model name:                              Neoverse-V2
Model:                                   1
Thread(s) per core:                      1
Core(s) per cluster:                     16
Socket(s):                               -
Cluster(s):                              1
Stepping:                                r0p1
BogoMIPS:                                2000.00
Flags:                                   fp asimd evtstrm aes pmull sha1 sha2 crc32 atomics fphp asimdhp cpuid asimdrdm jscvt fcma lrcpc dcpop sha3 sm3 sm4 asimddp sha512 sve asimdfhm dit uscat ilrcpc flagm sb paca pacg dcpodp sve2 sveaes svepmull svebitperm svesha3 svesm4 flagm2 frint svei8mm svebf16 i8mm bf16 dgh rng bti
L1d cache:                               1 MiB (16 instances)
L1i cache:                               1 MiB (16 instances)
L2 cache:                                32 MiB (16 instances)
L3 cache:                                80 MiB (1 instance)
NUMA node(s):                            1
NUMA node0 CPU(s):                       0-15
Vulnerability Gather data sampling:      Not affected
Vulnerability Indirect target selection: Not affected
Vulnerability Itlb multihit:             Not affected
Vulnerability L1tf:                      Not affected
Vulnerability Mds:                       Not affected
Vulnerability Meltdown:                  Not affected
Vulnerability Mmio stale data:           Not affected
Vulnerability Reg file data sampling:    Not affected
Vulnerability Retbleed:                  Not affected
Vulnerability Spec rstack overflow:      Not affected
Vulnerability Spec store bypass:         Mitigation; Speculative Store Bypass disabled via prctl
Vulnerability Spectre v1:                Mitigation; __user pointer sanitization
Vulnerability Spectre v2:                Mitigation; CSV2, BHB
Vulnerability Srbds:                     Not affected
Vulnerability Tsa:                       Not affected
Vulnerability Tsx async abort:           Not affected
Vulnerability Vmscape:                   Not affected
Resource Usage

base (merge-base)

Metric Value
Wall time 1750.4s
Peak memory 2.2 GiB
Avg memory 349.4 MiB
CPU user 1712.3s
CPU sys 34.8s
Peak spill 0 B

branch

Metric Value
Wall time 1800.4s
Peak memory 2.1 GiB
Avg memory 299.8 MiB
CPU user 1727.9s
CPU sys 67.2s
Peak spill 0 B
arrow_writer — shard 2/4

Node: gk3-benchmark-cluster-nap-1iq4pjyb-5c94fccf-l97c

Instance: c4a-highmem-16 (12 vCPU / 65 GiB)

uname:

Linux bench-c6250583741-3569-pjxs5 6.12.94+ #1 SMP Fri Aug 21 08:00:16 UTC 2026 aarch64 GNU/Linux

BENCH_COMMAND:

cargo bench --features=arrow,async,test_common,experimental,object_store --bench arrow_writer
CPU Details (lscpu)
Architecture:                            aarch64
CPU op-mode(s):                          64-bit
Byte Order:                              Little Endian
CPU(s):                                  16
On-line CPU(s) list:                     0-15
Vendor ID:                               ARM
Model name:                              Neoverse-V2
Model:                                   1
Thread(s) per core:                      1
Core(s) per cluster:                     16
Socket(s):                               -
Cluster(s):                              1
Stepping:                                r0p1
BogoMIPS:                                2000.00
Flags:                                   fp asimd evtstrm aes pmull sha1 sha2 crc32 atomics fphp asimdhp cpuid asimdrdm jscvt fcma lrcpc dcpop sha3 sm3 sm4 asimddp sha512 sve asimdfhm dit uscat ilrcpc flagm sb paca pacg dcpodp sve2 sveaes svepmull svebitperm svesha3 svesm4 flagm2 frint svei8mm svebf16 i8mm bf16 dgh rng bti
L1d cache:                               1 MiB (16 instances)
L1i cache:                               1 MiB (16 instances)
L2 cache:                                32 MiB (16 instances)
L3 cache:                                80 MiB (1 instance)
NUMA node(s):                            1
NUMA node0 CPU(s):                       0-15
Vulnerability Gather data sampling:      Not affected
Vulnerability Indirect target selection: Not affected
Vulnerability Itlb multihit:             Not affected
Vulnerability L1tf:                      Not affected
Vulnerability Mds:                       Not affected
Vulnerability Meltdown:                  Not affected
Vulnerability Mmio stale data:           Not affected
Vulnerability Reg file data sampling:    Not affected
Vulnerability Retbleed:                  Not affected
Vulnerability Spec rstack overflow:      Not affected
Vulnerability Spec store bypass:         Mitigation; Speculative Store Bypass disabled via prctl
Vulnerability Spectre v1:                Mitigation; __user pointer sanitization
Vulnerability Spectre v2:                Mitigation; CSV2, BHB
Vulnerability Srbds:                     Not affected
Vulnerability Tsa:                       Not affected
Vulnerability Tsx async abort:           Not affected
Vulnerability Vmscape:                   Not affected
Resource Usage

base (merge-base)

Metric Value
Wall time 1775.4s
Peak memory 2.1 GiB
Avg memory 465.7 MiB
CPU user 1726.2s
CPU sys 42.9s
Peak spill 0 B

branch

Metric Value
Wall time 1805.4s
Peak memory 2.1 GiB
Avg memory 481.8 MiB
CPU user 1756.6s
CPU sys 46.5s
Peak spill 0 B
arrow_writer — shard 3/4

Node: gk3-benchmark-cluster-nap-1rvnyzuz-5973f194-q2kq

Instance: c4a-highmem-16 (12 vCPU / 65 GiB)

uname:

Linux bench-c6250583741-3570-6q7gm 6.12.94+ #1 SMP Fri Aug 21 08:00:16 UTC 2026 aarch64 GNU/Linux

BENCH_COMMAND:

cargo bench --features=arrow,async,test_common,experimental,object_store --bench arrow_writer
CPU Details (lscpu)
Architecture:                            aarch64
CPU op-mode(s):                          64-bit
Byte Order:                              Little Endian
CPU(s):                                  16
On-line CPU(s) list:                     0-15
Vendor ID:                               ARM
Model name:                              Neoverse-V2
Model:                                   1
Thread(s) per core:                      1
Core(s) per cluster:                     16
Socket(s):                               -
Cluster(s):                              1
Stepping:                                r0p1
BogoMIPS:                                2000.00
Flags:                                   fp asimd evtstrm aes pmull sha1 sha2 crc32 atomics fphp asimdhp cpuid asimdrdm jscvt fcma lrcpc dcpop sha3 sm3 sm4 asimddp sha512 sve asimdfhm dit uscat ilrcpc flagm sb paca pacg dcpodp sve2 sveaes svepmull svebitperm svesha3 svesm4 flagm2 frint svei8mm svebf16 i8mm bf16 dgh rng bti
L1d cache:                               1 MiB (16 instances)
L1i cache:                               1 MiB (16 instances)
L2 cache:                                32 MiB (16 instances)
L3 cache:                                80 MiB (1 instance)
NUMA node(s):                            1
NUMA node0 CPU(s):                       0-15
Vulnerability Gather data sampling:      Not affected
Vulnerability Indirect target selection: Not affected
Vulnerability Itlb multihit:             Not affected
Vulnerability L1tf:                      Not affected
Vulnerability Mds:                       Not affected
Vulnerability Meltdown:                  Not affected
Vulnerability Mmio stale data:           Not affected
Vulnerability Reg file data sampling:    Not affected
Vulnerability Retbleed:                  Not affected
Vulnerability Spec rstack overflow:      Not affected
Vulnerability Spec store bypass:         Mitigation; Speculative Store Bypass disabled via prctl
Vulnerability Spectre v1:                Mitigation; __user pointer sanitization
Vulnerability Spectre v2:                Mitigation; CSV2, BHB
Vulnerability Srbds:                     Not affected
Vulnerability Tsa:                       Not affected
Vulnerability Tsx async abort:           Not affected
Vulnerability Vmscape:                   Not affected
Resource Usage

base (merge-base)

Metric Value
Wall time 1950.4s
Peak memory 2.1 GiB
Avg memory 426.3 MiB
CPU user 1914.3s
CPU sys 31.0s
Peak spill 0 B

branch

Metric Value
Wall time 2025.4s
Peak memory 2.2 GiB
Avg memory 462.8 MiB
CPU user 1992.3s
CPU sys 28.0s
Peak spill 0 B
arrow_writer — shard 4/4

Node: gk3-benchmark-cluster-nap-1rvnyzuz-7240f749-29lj

Instance: c4a-highmem-16 (12 vCPU / 65 GiB)

uname:

Linux bench-c6250583741-3571-mkgs4 6.12.94+ #1 SMP Fri Aug 21 08:00:16 UTC 2026 aarch64 GNU/Linux

BENCH_COMMAND:

cargo bench --features=arrow,async,test_common,experimental,object_store --bench arrow_writer
CPU Details (lscpu)
Architecture:                            aarch64
CPU op-mode(s):                          64-bit
Byte Order:                              Little Endian
CPU(s):                                  16
On-line CPU(s) list:                     0-15
Vendor ID:                               ARM
Model name:                              Neoverse-V2
Model:                                   1
Thread(s) per core:                      1
Core(s) per cluster:                     16
Socket(s):                               -
Cluster(s):                              1
Stepping:                                r0p1
BogoMIPS:                                2000.00
Flags:                                   fp asimd evtstrm aes pmull sha1 sha2 crc32 atomics fphp asimdhp cpuid asimdrdm jscvt fcma lrcpc dcpop sha3 sm3 sm4 asimddp sha512 sve asimdfhm dit uscat ilrcpc flagm sb paca pacg dcpodp sve2 sveaes svepmull svebitperm svesha3 svesm4 flagm2 frint svei8mm svebf16 i8mm bf16 dgh rng bti
L1d cache:                               1 MiB (16 instances)
L1i cache:                               1 MiB (16 instances)
L2 cache:                                32 MiB (16 instances)
L3 cache:                                80 MiB (1 instance)
NUMA node(s):                            1
NUMA node0 CPU(s):                       0-15
Vulnerability Gather data sampling:      Not affected
Vulnerability Indirect target selection: Not affected
Vulnerability Itlb multihit:             Not affected
Vulnerability L1tf:                      Not affected
Vulnerability Mds:                       Not affected
Vulnerability Meltdown:                  Not affected
Vulnerability Mmio stale data:           Not affected
Vulnerability Reg file data sampling:    Not affected
Vulnerability Retbleed:                  Not affected
Vulnerability Spec rstack overflow:      Not affected
Vulnerability Spec store bypass:         Mitigation; Speculative Store Bypass disabled via prctl
Vulnerability Spectre v1:                Mitigation; __user pointer sanitization
Vulnerability Spectre v2:                Mitigation; CSV2, BHB
Vulnerability Srbds:                     Not affected
Vulnerability Tsa:                       Not affected
Vulnerability Tsx async abort:           Not affected
Vulnerability Vmscape:                   Not affected
Resource Usage

base (merge-base)

Metric Value
Wall time 1810.4s
Peak memory 2.0 GiB
Avg memory 286.8 MiB
CPU user 1717.2s
CPU sys 89.0s
Peak spill 0 B

branch

Metric Value
Wall time 1830.4s
Peak memory 2.0 GiB
Avg memory 260.6 MiB
CPU user 1760.8s
CPU sys 63.3s
Peak spill 0 B

File an issue against this benchmark runner

@adriangbot

Copy link
Copy Markdown

🤖 Benchmark failed or incomplete (GKE) | trigger

Target: arrow_writer

Comparing fix/bloom-filter-arrow-check (8b4abea) to bece8ca (merge-base) diff

Run configuration
run benchmark arrow_writer
shards: 1
Errors / missing shards
arrow_writer — shard 1/1: Benchmark for [this request](https://github.com/apache/arrow-rs/pull/11549#issuecomment-6250570158) hit the 7200s job deadline before finishing.

Benchmarks requested: `arrow_writer`

<details><summary>Kubernetes message</summary>

```
Job was active longer than specified deadline
```

</details>

---
[File an issue](https://github.com/adriangb/datafusion-benchmarking/issues) against this benchmark runner
Per-runner information
arrow_writer — shard 1/1

Node: gk3-benchmark-cluster-nap-1rvnyzuz-894d94a5-2x5v

Instance: c4a-highmem-16 (12 vCPU / 65 GiB)

uname:

Linux bench-c6250570158-3567-jsbz6 6.12.94+ #1 SMP Fri Aug 21 08:00:16 UTC 2026 aarch64 GNU/Linux

BENCH_COMMAND:

cargo bench --features=arrow,async,test_common,experimental,object_store --bench arrow_writer
CPU Details (lscpu)
Architecture:                            aarch64
CPU op-mode(s):                          64-bit
Byte Order:                              Little Endian
CPU(s):                                  16
On-line CPU(s) list:                     0-15
Vendor ID:                               ARM
Model name:                              Neoverse-V2
Model:                                   1
Thread(s) per core:                      1
Core(s) per cluster:                     16
Socket(s):                               -
Cluster(s):                              1
Stepping:                                r0p1
BogoMIPS:                                2000.00
Flags:                                   fp asimd evtstrm aes pmull sha1 sha2 crc32 atomics fphp asimdhp cpuid asimdrdm jscvt fcma lrcpc dcpop sha3 sm3 sm4 asimddp sha512 sve asimdfhm dit uscat ilrcpc flagm sb paca pacg dcpodp sve2 sveaes svepmull svebitperm svesha3 svesm4 flagm2 frint svei8mm svebf16 i8mm bf16 dgh rng bti
L1d cache:                               1 MiB (16 instances)
L1i cache:                               1 MiB (16 instances)
L2 cache:                                32 MiB (16 instances)
L3 cache:                                80 MiB (1 instance)
NUMA node(s):                            1
NUMA node0 CPU(s):                       0-15
Vulnerability Gather data sampling:      Not affected
Vulnerability Indirect target selection: Not affected
Vulnerability Itlb multihit:             Not affected
Vulnerability L1tf:                      Not affected
Vulnerability Mds:                       Not affected
Vulnerability Meltdown:                  Not affected
Vulnerability Mmio stale data:           Not affected
Vulnerability Reg file data sampling:    Not affected
Vulnerability Retbleed:                  Not affected
Vulnerability Spec rstack overflow:      Not affected
Vulnerability Spec store bypass:         Mitigation; Speculative Store Bypass disabled via prctl
Vulnerability Spectre v1:                Mitigation; __user pointer sanitization
Vulnerability Spectre v2:                Mitigation; CSV2, BHB
Vulnerability Srbds:                     Not affected
Vulnerability Tsa:                       Not affected
Vulnerability Tsx async abort:           Not affected
Vulnerability Vmscape:                   Not affected
Resource Usage

No completed measurement resource samples.


File an issue against this benchmark runner

@adriangb

Copy link
Copy Markdown
Contributor

run benchmark arrow_writer

shards: 8

@adriangbot

Copy link
Copy Markdown

🤖 Benchmark starting (GKE) | trigger

Target: arrow_writer

Sharding factor: 8 (8 workers).

Comparing fix/bloom-filter-arrow-check (77a7137) to bece8ca (merge-base) diff

Run configuration
run benchmark arrow_writer
shards: 8

Results will be posted when all workers finish.


File an issue against this benchmark runner

@adriangbot

Copy link
Copy Markdown

🤖 Benchmark completed (GKE) | trigger

Target: arrow_writer

Comparing fix/bloom-filter-arrow-check (77a7137) to bece8ca (merge-base) diff

Run configuration
run benchmark arrow_writer
shards: 8

Comparison results: View the complete native critcmp output.

The comparison is published in an unlisted Gist because the original inline report exceeded GitHub's comment limit.

Per-runner information
arrow_writer — shard 1/8

Node: gk3-benchmark-cluster-nap-28gt8wfj-9d94965e-tzhv

Instance: c4a-highmem-16 (12 vCPU / 65 GiB)

uname:

Linux bench-c6261641978-3581-rrz68 6.12.94+ #1 SMP Fri Aug 21 08:00:16 UTC 2026 aarch64 GNU/Linux

BENCH_COMMAND:

cargo bench --features=arrow,async,test_common,experimental,object_store --bench arrow_writer
CPU Details (lscpu)
Architecture:                            aarch64
CPU op-mode(s):                          64-bit
Byte Order:                              Little Endian
CPU(s):                                  16
On-line CPU(s) list:                     0-15
Vendor ID:                               ARM
Model name:                              Neoverse-V2
Model:                                   1
Thread(s) per core:                      1
Core(s) per cluster:                     16
Socket(s):                               -
Cluster(s):                              1
Stepping:                                r0p1
BogoMIPS:                                2000.00
Flags:                                   fp asimd evtstrm aes pmull sha1 sha2 crc32 atomics fphp asimdhp cpuid asimdrdm jscvt fcma lrcpc dcpop sha3 sm3 sm4 asimddp sha512 sve asimdfhm dit uscat ilrcpc flagm sb paca pacg dcpodp sve2 sveaes svepmull svebitperm svesha3 svesm4 flagm2 frint svei8mm svebf16 i8mm bf16 dgh rng bti
L1d cache:                               1 MiB (16 instances)
L1i cache:                               1 MiB (16 instances)
L2 cache:                                32 MiB (16 instances)
L3 cache:                                80 MiB (1 instance)
NUMA node(s):                            1
NUMA node0 CPU(s):                       0-15
Vulnerability Gather data sampling:      Not affected
Vulnerability Indirect target selection: Not affected
Vulnerability Itlb multihit:             Not affected
Vulnerability L1tf:                      Not affected
Vulnerability Mds:                       Not affected
Vulnerability Meltdown:                  Not affected
Vulnerability Mmio stale data:           Not affected
Vulnerability Reg file data sampling:    Not affected
Vulnerability Retbleed:                  Not affected
Vulnerability Spec rstack overflow:      Not affected
Vulnerability Spec store bypass:         Mitigation; Speculative Store Bypass disabled via prctl
Vulnerability Spectre v1:                Mitigation; __user pointer sanitization
Vulnerability Spectre v2:                Mitigation; CSV2, BHB
Vulnerability Srbds:                     Not affected
Vulnerability Tsa:                       Not affected
Vulnerability Tsx async abort:           Not affected
Vulnerability Vmscape:                   Not affected
Resource Usage

base (merge-base)

Metric Value
Wall time 995.2s
Peak memory 1.9 GiB
Avg memory 178.6 MiB
CPU user 936.8s
CPU sys 53.4s
Peak spill 0 B

branch

Metric Value
Wall time 1005.2s
Peak memory 2.0 GiB
Avg memory 212.0 MiB
CPU user 950.8s
CPU sys 49.4s
Peak spill 0 B
arrow_writer — shard 2/8

Node: gk3-benchmark-cluster-nap-28gt8wfj-cf05c2d9-hm2z

Instance: c4a-highmem-16 (12 vCPU / 65 GiB)

uname:

Linux bench-c6261641978-3582-w46wz 6.12.94+ #1 SMP Fri Aug 21 08:00:16 UTC 2026 aarch64 GNU/Linux

BENCH_COMMAND:

cargo bench --features=arrow,async,test_common,experimental,object_store --bench arrow_writer
CPU Details (lscpu)
Architecture:                            aarch64
CPU op-mode(s):                          64-bit
Byte Order:                              Little Endian
CPU(s):                                  16
On-line CPU(s) list:                     0-15
Vendor ID:                               ARM
Model name:                              Neoverse-V2
Model:                                   1
Thread(s) per core:                      1
Core(s) per cluster:                     16
Socket(s):                               -
Cluster(s):                              1
Stepping:                                r0p1
BogoMIPS:                                2000.00
Flags:                                   fp asimd evtstrm aes pmull sha1 sha2 crc32 atomics fphp asimdhp cpuid asimdrdm jscvt fcma lrcpc dcpop sha3 sm3 sm4 asimddp sha512 sve asimdfhm dit uscat ilrcpc flagm sb paca pacg dcpodp sve2 sveaes svepmull svebitperm svesha3 svesm4 flagm2 frint svei8mm svebf16 i8mm bf16 dgh rng bti
L1d cache:                               1 MiB (16 instances)
L1i cache:                               1 MiB (16 instances)
L2 cache:                                32 MiB (16 instances)
L3 cache:                                80 MiB (1 instance)
NUMA node(s):                            1
NUMA node0 CPU(s):                       0-15
Vulnerability Gather data sampling:      Not affected
Vulnerability Indirect target selection: Not affected
Vulnerability Itlb multihit:             Not affected
Vulnerability L1tf:                      Not affected
Vulnerability Mds:                       Not affected
Vulnerability Meltdown:                  Not affected
Vulnerability Mmio stale data:           Not affected
Vulnerability Reg file data sampling:    Not affected
Vulnerability Retbleed:                  Not affected
Vulnerability Spec rstack overflow:      Not affected
Vulnerability Spec store bypass:         Mitigation; Speculative Store Bypass disabled via prctl
Vulnerability Spectre v1:                Mitigation; __user pointer sanitization
Vulnerability Spectre v2:                Mitigation; CSV2, BHB
Vulnerability Srbds:                     Not affected
Vulnerability Tsa:                       Not affected
Vulnerability Tsx async abort:           Not affected
Vulnerability Vmscape:                   Not affected
Resource Usage

base (merge-base)

Metric Value
Wall time 1070.2s
Peak memory 2.1 GiB
Avg memory 456.5 MiB
CPU user 1040.5s
CPU sys 25.7s
Peak spill 0 B

branch

Metric Value
Wall time 1075.2s
Peak memory 2.1 GiB
Avg memory 375.7 MiB
CPU user 1030.5s
CPU sys 42.7s
Peak spill 0 B
arrow_writer — shard 3/8

Node: gk3-benchmark-cluster-nap-i0c7et67-c69b8f21-hb79

Instance: c4a-highmem-16 (12 vCPU / 65 GiB)

uname:

Linux bench-c6261641978-3583-d9jjz 6.12.94+ #1 SMP Fri Aug 21 08:00:16 UTC 2026 aarch64 GNU/Linux

BENCH_COMMAND:

cargo bench --features=arrow,async,test_common,experimental,object_store --bench arrow_writer
CPU Details (lscpu)
Architecture:                            aarch64
CPU op-mode(s):                          64-bit
Byte Order:                              Little Endian
CPU(s):                                  16
On-line CPU(s) list:                     0-15
Vendor ID:                               ARM
Model name:                              Neoverse-V2
Model:                                   1
Thread(s) per core:                      1
Core(s) per cluster:                     16
Socket(s):                               -
Cluster(s):                              1
Stepping:                                r0p1
BogoMIPS:                                2000.00
Flags:                                   fp asimd evtstrm aes pmull sha1 sha2 crc32 atomics fphp asimdhp cpuid asimdrdm jscvt fcma lrcpc dcpop sha3 sm3 sm4 asimddp sha512 sve asimdfhm dit uscat ilrcpc flagm sb paca pacg dcpodp sve2 sveaes svepmull svebitperm svesha3 svesm4 flagm2 frint svei8mm svebf16 i8mm bf16 dgh rng bti
L1d cache:                               1 MiB (16 instances)
L1i cache:                               1 MiB (16 instances)
L2 cache:                                32 MiB (16 instances)
L3 cache:                                80 MiB (1 instance)
NUMA node(s):                            1
NUMA node0 CPU(s):                       0-15
Vulnerability Gather data sampling:      Not affected
Vulnerability Indirect target selection: Not affected
Vulnerability Itlb multihit:             Not affected
Vulnerability L1tf:                      Not affected
Vulnerability Mds:                       Not affected
Vulnerability Meltdown:                  Not affected
Vulnerability Mmio stale data:           Not affected
Vulnerability Reg file data sampling:    Not affected
Vulnerability Retbleed:                  Not affected
Vulnerability Spec rstack overflow:      Not affected
Vulnerability Spec store bypass:         Mitigation; Speculative Store Bypass disabled via prctl
Vulnerability Spectre v1:                Mitigation; __user pointer sanitization
Vulnerability Spectre v2:                Mitigation; CSV2, BHB
Vulnerability Srbds:                     Not affected
Vulnerability Tsa:                       Not affected
Vulnerability Tsx async abort:           Not affected
Vulnerability Vmscape:                   Not affected
Resource Usage

base (merge-base)

Metric Value
Wall time 895.2s
Peak memory 2.1 GiB
Avg memory 330.9 MiB
CPU user 852.6s
CPU sys 36.4s
Peak spill 0 B

branch

Metric Value
Wall time 905.2s
Peak memory 2.2 GiB
Avg memory 408.1 MiB
CPU user 885.2s
CPU sys 15.9s
Peak spill 0 B
arrow_writer — shard 4/8

Node: gk3-benchmark-cluster-nap-28gt8wfj-c1668e86-vvb7

Instance: c4a-highmem-16 (12 vCPU / 65 GiB)

uname:

Linux bench-c6261641978-3584-2qn8r 6.12.94+ #1 SMP Fri Aug 21 08:00:16 UTC 2026 aarch64 GNU/Linux

BENCH_COMMAND:

cargo bench --features=arrow,async,test_common,experimental,object_store --bench arrow_writer
CPU Details (lscpu)
Architecture:                            aarch64
CPU op-mode(s):                          64-bit
Byte Order:                              Little Endian
CPU(s):                                  16
On-line CPU(s) list:                     0-15
Vendor ID:                               ARM
Model name:                              Neoverse-V2
Model:                                   1
Thread(s) per core:                      1
Core(s) per cluster:                     16
Socket(s):                               -
Cluster(s):                              1
Stepping:                                r0p1
BogoMIPS:                                2000.00
Flags:                                   fp asimd evtstrm aes pmull sha1 sha2 crc32 atomics fphp asimdhp cpuid asimdrdm jscvt fcma lrcpc dcpop sha3 sm3 sm4 asimddp sha512 sve asimdfhm dit uscat ilrcpc flagm sb paca pacg dcpodp sve2 sveaes svepmull svebitperm svesha3 svesm4 flagm2 frint svei8mm svebf16 i8mm bf16 dgh rng bti
L1d cache:                               1 MiB (16 instances)
L1i cache:                               1 MiB (16 instances)
L2 cache:                                32 MiB (16 instances)
L3 cache:                                80 MiB (1 instance)
NUMA node(s):                            1
NUMA node0 CPU(s):                       0-15
Vulnerability Gather data sampling:      Not affected
Vulnerability Indirect target selection: Not affected
Vulnerability Itlb multihit:             Not affected
Vulnerability L1tf:                      Not affected
Vulnerability Mds:                       Not affected
Vulnerability Meltdown:                  Not affected
Vulnerability Mmio stale data:           Not affected
Vulnerability Reg file data sampling:    Not affected
Vulnerability Retbleed:                  Not affected
Vulnerability Spec rstack overflow:      Not affected
Vulnerability Spec store bypass:         Mitigation; Speculative Store Bypass disabled via prctl
Vulnerability Spectre v1:                Mitigation; __user pointer sanitization
Vulnerability Spectre v2:                Mitigation; CSV2, BHB
Vulnerability Srbds:                     Not affected
Vulnerability Tsa:                       Not affected
Vulnerability Tsx async abort:           Not affected
Vulnerability Vmscape:                   Not affected
Resource Usage

base (merge-base)

Metric Value
Wall time 785.2s
Peak memory 1.9 GiB
Avg memory 188.1 MiB
CPU user 721.0s
CPU sys 59.0s
Peak spill 0 B

branch

Metric Value
Wall time 790.2s
Peak memory 2.1 GiB
Avg memory 216.5 MiB
CPU user 743.5s
CPU sys 40.4s
Peak spill 0 B
arrow_writer — shard 5/8

Node: gk3-benchmark-cluster-nap-28gt8wfj-9d94965e-bwws

Instance: c4a-highmem-16 (12 vCPU / 65 GiB)

uname:

Linux bench-c6261641978-3585-tbtmg 6.12.94+ #1 SMP Fri Aug 21 08:00:16 UTC 2026 aarch64 GNU/Linux

BENCH_COMMAND:

cargo bench --features=arrow,async,test_common,experimental,object_store --bench arrow_writer
CPU Details (lscpu)
Architecture:                            aarch64
CPU op-mode(s):                          64-bit
Byte Order:                              Little Endian
CPU(s):                                  16
On-line CPU(s) list:                     0-15
Vendor ID:                               ARM
Model name:                              Neoverse-V2
Model:                                   1
Thread(s) per core:                      1
Core(s) per cluster:                     16
Socket(s):                               -
Cluster(s):                              1
Stepping:                                r0p1
BogoMIPS:                                2000.00
Flags:                                   fp asimd evtstrm aes pmull sha1 sha2 crc32 atomics fphp asimdhp cpuid asimdrdm jscvt fcma lrcpc dcpop sha3 sm3 sm4 asimddp sha512 sve asimdfhm dit uscat ilrcpc flagm sb paca pacg dcpodp sve2 sveaes svepmull svebitperm svesha3 svesm4 flagm2 frint svei8mm svebf16 i8mm bf16 dgh rng bti
L1d cache:                               1 MiB (16 instances)
L1i cache:                               1 MiB (16 instances)
L2 cache:                                32 MiB (16 instances)
L3 cache:                                80 MiB (1 instance)
NUMA node(s):                            1
NUMA node0 CPU(s):                       0-15
Vulnerability Gather data sampling:      Not affected
Vulnerability Indirect target selection: Not affected
Vulnerability Itlb multihit:             Not affected
Vulnerability L1tf:                      Not affected
Vulnerability Mds:                       Not affected
Vulnerability Meltdown:                  Not affected
Vulnerability Mmio stale data:           Not affected
Vulnerability Reg file data sampling:    Not affected
Vulnerability Retbleed:                  Not affected
Vulnerability Spec rstack overflow:      Not affected
Vulnerability Spec store bypass:         Mitigation; Speculative Store Bypass disabled via prctl
Vulnerability Spectre v1:                Mitigation; __user pointer sanitization
Vulnerability Spectre v2:                Mitigation; CSV2, BHB
Vulnerability Srbds:                     Not affected
Vulnerability Tsa:                       Not affected
Vulnerability Tsx async abort:           Not affected
Vulnerability Vmscape:                   Not affected
Resource Usage

base (merge-base)

Metric Value
Wall time 950.2s
Peak memory 2.2 GiB
Avg memory 404.8 MiB
CPU user 933.1s
CPU sys 13.0s
Peak spill 0 B

branch

Metric Value
Wall time 965.2s
Peak memory 2.2 GiB
Avg memory 366.8 MiB
CPU user 941.4s
CPU sys 18.6s
Peak spill 0 B
arrow_writer — shard 6/8

Node: gk3-benchmark-cluster-nap-28gt8wfj-c1668e86-vvb7

Instance: c4a-highmem-16 (12 vCPU / 65 GiB)

uname:

Linux bench-c6261641978-3586-bqtm2 6.12.94+ #1 SMP Fri Aug 21 08:00:16 UTC 2026 aarch64 GNU/Linux

BENCH_COMMAND:

cargo bench --features=arrow,async,test_common,experimental,object_store --bench arrow_writer
CPU Details (lscpu)
Architecture:                            aarch64
CPU op-mode(s):                          64-bit
Byte Order:                              Little Endian
CPU(s):                                  16
On-line CPU(s) list:                     0-15
Vendor ID:                               ARM
Model name:                              Neoverse-V2
Model:                                   1
Thread(s) per core:                      1
Core(s) per cluster:                     16
Socket(s):                               -
Cluster(s):                              1
Stepping:                                r0p1
BogoMIPS:                                2000.00
Flags:                                   fp asimd evtstrm aes pmull sha1 sha2 crc32 atomics fphp asimdhp cpuid asimdrdm jscvt fcma lrcpc dcpop sha3 sm3 sm4 asimddp sha512 sve asimdfhm dit uscat ilrcpc flagm sb paca pacg dcpodp sve2 sveaes svepmull svebitperm svesha3 svesm4 flagm2 frint svei8mm svebf16 i8mm bf16 dgh rng bti
L1d cache:                               1 MiB (16 instances)
L1i cache:                               1 MiB (16 instances)
L2 cache:                                32 MiB (16 instances)
L3 cache:                                80 MiB (1 instance)
NUMA node(s):                            1
NUMA node0 CPU(s):                       0-15
Vulnerability Gather data sampling:      Not affected
Vulnerability Indirect target selection: Not affected
Vulnerability Itlb multihit:             Not affected
Vulnerability L1tf:                      Not affected
Vulnerability Mds:                       Not affected
Vulnerability Meltdown:                  Not affected
Vulnerability Mmio stale data:           Not affected
Vulnerability Reg file data sampling:    Not affected
Vulnerability Retbleed:                  Not affected
Vulnerability Spec rstack overflow:      Not affected
Vulnerability Spec store bypass:         Mitigation; Speculative Store Bypass disabled via prctl
Vulnerability Spectre v1:                Mitigation; __user pointer sanitization
Vulnerability Spectre v2:                Mitigation; CSV2, BHB
Vulnerability Srbds:                     Not affected
Vulnerability Tsa:                       Not affected
Vulnerability Tsx async abort:           Not affected
Vulnerability Vmscape:                   Not affected
Resource Usage

base (merge-base)

Metric Value
Wall time 990.2s
Peak memory 2.1 GiB
Avg memory 437.5 MiB
CPU user 931.3s
CPU sys 55.9s
Peak spill 0 B

branch

Metric Value
Wall time 1020.2s
Peak memory 2.1 GiB
Avg memory 369.3 MiB
CPU user 926.6s
CPU sys 89.6s
Peak spill 0 B
arrow_writer — shard 7/8

Node: gk3-benchmark-cluster-nap-i0c7et67-c69b8f21-hb79

Instance: c4a-highmem-16 (12 vCPU / 65 GiB)

uname:

Linux bench-c6261641978-3587-8nshr 6.12.94+ #1 SMP Fri Aug 21 08:00:16 UTC 2026 aarch64 GNU/Linux

BENCH_COMMAND:

cargo bench --features=arrow,async,test_common,experimental,object_store --bench arrow_writer
CPU Details (lscpu)
Architecture:                            aarch64
CPU op-mode(s):                          64-bit
Byte Order:                              Little Endian
CPU(s):                                  16
On-line CPU(s) list:                     0-15
Vendor ID:                               ARM
Model name:                              Neoverse-V2
Model:                                   1
Thread(s) per core:                      1
Core(s) per cluster:                     16
Socket(s):                               -
Cluster(s):                              1
Stepping:                                r0p1
BogoMIPS:                                2000.00
Flags:                                   fp asimd evtstrm aes pmull sha1 sha2 crc32 atomics fphp asimdhp cpuid asimdrdm jscvt fcma lrcpc dcpop sha3 sm3 sm4 asimddp sha512 sve asimdfhm dit uscat ilrcpc flagm sb paca pacg dcpodp sve2 sveaes svepmull svebitperm svesha3 svesm4 flagm2 frint svei8mm svebf16 i8mm bf16 dgh rng bti
L1d cache:                               1 MiB (16 instances)
L1i cache:                               1 MiB (16 instances)
L2 cache:                                32 MiB (16 instances)
L3 cache:                                80 MiB (1 instance)
NUMA node(s):                            1
NUMA node0 CPU(s):                       0-15
Vulnerability Gather data sampling:      Not affected
Vulnerability Indirect target selection: Not affected
Vulnerability Itlb multihit:             Not affected
Vulnerability L1tf:                      Not affected
Vulnerability Mds:                       Not affected
Vulnerability Meltdown:                  Not affected
Vulnerability Mmio stale data:           Not affected
Vulnerability Reg file data sampling:    Not affected
Vulnerability Retbleed:                  Not affected
Vulnerability Spec rstack overflow:      Not affected
Vulnerability Spec store bypass:         Mitigation; Speculative Store Bypass disabled via prctl
Vulnerability Spectre v1:                Mitigation; __user pointer sanitization
Vulnerability Spectre v2:                Mitigation; CSV2, BHB
Vulnerability Srbds:                     Not affected
Vulnerability Tsa:                       Not affected
Vulnerability Tsx async abort:           Not affected
Vulnerability Vmscape:                   Not affected
Resource Usage

base (merge-base)

Metric Value
Wall time 730.2s
Peak memory 2.1 GiB
Avg memory 309.3 MiB
CPU user 689.0s
CPU sys 37.9s
Peak spill 0 B

branch

Metric Value
Wall time 750.2s
Peak memory 1.9 GiB
Avg memory 287.9 MiB
CPU user 711.8s
CPU sys 36.1s
Peak spill 0 B
arrow_writer — shard 8/8

Node: gk3-benchmark-cluster-nap-28gt8wfj-9d94965e-bwws

Instance: c4a-highmem-16 (12 vCPU / 65 GiB)

uname:

Linux bench-c6261641978-3588-zlgxh 6.12.94+ #1 SMP Fri Aug 21 08:00:16 UTC 2026 aarch64 GNU/Linux

BENCH_COMMAND:

cargo bench --features=arrow,async,test_common,experimental,object_store --bench arrow_writer
CPU Details (lscpu)
Architecture:                            aarch64
CPU op-mode(s):                          64-bit
Byte Order:                              Little Endian
CPU(s):                                  16
On-line CPU(s) list:                     0-15
Vendor ID:                               ARM
Model name:                              Neoverse-V2
Model:                                   1
Thread(s) per core:                      1
Core(s) per cluster:                     16
Socket(s):                               -
Cluster(s):                              1
Stepping:                                r0p1
BogoMIPS:                                2000.00
Flags:                                   fp asimd evtstrm aes pmull sha1 sha2 crc32 atomics fphp asimdhp cpuid asimdrdm jscvt fcma lrcpc dcpop sha3 sm3 sm4 asimddp sha512 sve asimdfhm dit uscat ilrcpc flagm sb paca pacg dcpodp sve2 sveaes svepmull svebitperm svesha3 svesm4 flagm2 frint svei8mm svebf16 i8mm bf16 dgh rng bti
L1d cache:                               1 MiB (16 instances)
L1i cache:                               1 MiB (16 instances)
L2 cache:                                32 MiB (16 instances)
L3 cache:                                80 MiB (1 instance)
NUMA node(s):                            1
NUMA node0 CPU(s):                       0-15
Vulnerability Gather data sampling:      Not affected
Vulnerability Indirect target selection: Not affected
Vulnerability Itlb multihit:             Not affected
Vulnerability L1tf:                      Not affected
Vulnerability Mds:                       Not affected
Vulnerability Meltdown:                  Not affected
Vulnerability Mmio stale data:           Not affected
Vulnerability Reg file data sampling:    Not affected
Vulnerability Retbleed:                  Not affected
Vulnerability Spec rstack overflow:      Not affected
Vulnerability Spec store bypass:         Mitigation; Speculative Store Bypass disabled via prctl
Vulnerability Spectre v1:                Mitigation; __user pointer sanitization
Vulnerability Spectre v2:                Mitigation; CSV2, BHB
Vulnerability Srbds:                     Not affected
Vulnerability Tsa:                       Not affected
Vulnerability Tsx async abort:           Not affected
Vulnerability Vmscape:                   Not affected
Resource Usage

base (merge-base)

Metric Value
Wall time 975.2s
Peak memory 1.0 GiB
Avg memory 361.0 MiB
CPU user 933.3s
CPU sys 39.8s
Peak spill 0 B

branch

Metric Value
Wall time 1005.2s
Peak memory 924.3 MiB
Avg memory 348.0 MiB
CPU user 958.1s
CPU sys 42.1s
Peak spill 0 B

File an issue against this benchmark runner

The arrow_writer benchmark on aarch64 still showed the writer slower than
the base commit with the shared visitors, including on paths whose
per-value work did not change. Restore arrow_writer/mod.rs to the base
commit, so this PR no longer changes the writer, and give ArrowSbbf its
own copy of the INT32, INT64 and FIXED_LEN_BYTE_ARRAY conversions.

With the writer's source restored, adding ArrowSbbf to the crate still
changed the writer's machine code, because it changes how the crate is
split into codegen units and inlined. Mark ArrowSbbf's non-generic
functions #[inline] so they are only compiled in crates that call them.
The arrow_writer benchmark binary is then identical to the base
commit's.

ArrowSbbf behaves as before: FIXED_LEN_BYTE_ARRAY decimals are truncated
to the column's length like the writer, narrower decimals are
sign-extended, and run-end encoded and dictionary arrays are checked by
their values.

To keep the two copies from drifting apart, add a test that writes a
column of every type ArrowWriter supports, with and without
coerce_types, and checks that ArrowSbbf finds every written value in the
column's bloom filter. Also test a decimal column longer than 32 bytes.
@adriangb

Copy link
Copy Markdown
Contributor

@bhaskargurram-ai I ran the decimal* writer benchmarks on a dedicated aarch64 VM (3 interleaved rounds, pinned to fixed cores). I could not reproduce the slowdowns: every benchmark was within ±2% of base, and the decimal hot loops compile to identical assembly. I think the CI results were noise plus codegen lottery. The version with shared write paths is less code and the right long-term architecture. Two options:

  1. (My preference) Split the write-path changes into a precursor PR. It would be a pure refactor, so we can review and benchmark it on its own. This PR then only adds ArrowSbbf, and both PRs are small.
  2. Add the shared write paths back here and rerun the benchmarks.

@bhaskargurram-ai

Copy link
Copy Markdown
Contributor Author

@bhaskargurram-ai I ran the decimal* writer benchmarks on a dedicated aarch64 VM (3 interleaved rounds, pinned to fixed cores). I could not reproduce the slowdowns: every benchmark was within ±2% of base, and the decimal hot loops compile to identical assembly. I think the CI results were noise plus codegen lottery. The version with shared write paths is less code and the right long-term architecture. Two options:

  1. (My preference) Split the write-path changes into a precursor PR. It would be a pure refactor, so we can review and benchmark it on its own. This PR then only adds ArrowSbbf, and both PRs are small.
  2. Add the shared write paths back here and rerun the benchmarks.

@adriangb Thanks for running those, good to know it was noise. I’ll put the shared write paths back in this PR (keeping the every-type test as a guard against drift) and ping you for a benchmark run once it’s pushed.

@adriangb

Copy link
Copy Markdown
Contributor

are you opposed to splitting out the refactor pr as a precursor? i think both @mbutrovich and i would prefer that

Benchmarks on a dedicated machine showed no writer slowdown from the
shared visitors, so go back to them: ArrowWriter and ArrowSbbf again use
the same INT32, INT64 and FIXED_LEN_BYTE_ARRAY conversions through
visit_i32, visit_i64 and visit_fixed_len_byte_array, and ArrowSbbf's own
copy is removed. The #[inline] attributes are removed with it, since
they were only there to keep the writer's machine code unchanged.

Keep the test that writes a column of every type ArrowWriter supports
and checks that ArrowSbbf finds every written value, and the test for a
decimal column longer than 32 bytes.
@bhaskargurram-ai

Copy link
Copy Markdown
Contributor Author

@adriangb Thanks for running those, good to know it was noise. I’ll put the shared write paths back in this PR (keeping the every-type test as a guard against drift) and ping you for a benchmark run once it’s pushed.

@adriangb Not opposed at all, happy to split it. I’ll open the write-path refactor (the shared visit_i32 / visit_i64 / visit_fixed_len_byte_array) as its own PR so it can be reviewed and benchmarked by itself, and this PR will then only add ArrowSbbf on top of it.

@bhaskargurram-ai

Copy link
Copy Markdown
Contributor Author

Opened #11741 with just the write-path refactor (visit_i32 / visit_i64 / visit_fixed_len_byte_array), so it can be reviewed and benchmarked on its own. Once that lands I’ll merge main into this branch, and this PR will only add ArrowSbbf (plus the small hooks it needs in those visitors).

@etseidl

etseidl commented Oct 28, 2026

Copy link
Copy Markdown
Contributor

Marking this as draft to unblock #11741

@etseidl
etseidl self-requested a review October 28, 2026 22:46
@etseidl

etseidl commented Oct 28, 2026

Copy link
Copy Markdown
Contributor

I tried rebasing on main, but don't have push permission for this PR. @bhaskargurram-ai can you please rebase on main?

jaideeppyne pushed a commit to jaideeppyne/arrow-rs that referenced this pull request Oct 28, 2026
… visitors (apache#11741)

# Which issue does this PR close?

- Part of apache#5550.

This is the write-path refactor from apache#11549, split out as the reviewers
asked so it can be reviewed and benchmarked on its own. apache#11549 will then
only add `ArrowSbbf` on top of it.

# Rationale for this change

`ArrowSbbf` in apache#11549 checks Arrow values against a bloom filter written
by `ArrowWriter`, so it has to apply the same conversions the writer
applies before hashing (`Int8` to `INT32`, `Date64` to days, decimals to
`INT32`, `INT64` or `FIXED_LEN_BYTE_ARRAY`, and so on). Instead of a
second copy of those conversions that could drift from the writer, I
moved them out of `write_leaf` into functions both can use.

# What changes are included in this PR?

- The `INT32`, `INT64` and `FIXED_LEN_BYTE_ARRAY` arms of `write_leaf`
move into crate-private `visit_i32`, `visit_i64` and
`visit_fixed_len_byte_array`. These pass the values to a visitor:
- `PrimitiveVisitor` gets either the Arrow values as they are (`slice`)
or the values with a per-value conversion (`map`).
- `FixedLenByteArrayVisitor` gets the value length and a closure that
fills each value.
- The writer implements these with `WritePrimitive` and
`WriteFixedLenByteArray`. Types that were written straight from the
Arrow buffer still are. Types that went through `unary` are collected
into a `Vec` with the same conversion. FLBA values are still built with
`chunk_array_slice`. The one difference is `Decimal32` to `INT32`, which
now borrows the buffer instead of copying it through an identity
`unary`.
- The `get_*_array_slice` helpers for intervals, decimals, `Float16` and
`FixedSizeBinary` are folded into `visit_fixed_len_byte_array` and
`visit_decimal`. The match arms, the error messages and the decimal
length `assert!` are unchanged.

The parts only `ArrowSbbf` needs (its own error for unsupported types, a
`Decimal32` arm in `visit_i64`, and sign extension for decimal columns
wider than the Arrow type) stay in apache#11549.

# Are these changes tested?

There is no change in behaviour, so I didn't add tests. The existing
`arrow_writer` tests cover every arm.

adriangb benchmarked these write paths on apache#11549 and they were within
noise of main. Could someone run `run benchmark arrow_writer` on this PR
as well?

# Are there any significant user-facing changes?

No. The new functions and traits are crate-private.

---------

Co-authored-by: Adrian Garcia Badaracco <1755071+adriangb@users.noreply.github.com>
Co-authored-by: Ed Seidl <etseidl@users.noreply.github.com>
…row-check

# Conflicts:
#	parquet/src/arrow/arrow_writer/mod.rs
@bhaskargurram-ai

Copy link
Copy Markdown
Contributor Author

Thanks @etseidl. I merged main into the branch (947b3e9) rather than rebasing, so the history stays as it was. Now that #11741 is in, this PR is just ArrowSbbf on top of the shared visitors. The writer only gains the small hooks ArrowSbbf needs: an unsupported hook on the visitor traits, Decimal32 in visit_i64, and sign extension in visit_decimal. Parquet tests and clippy pass locally. It's ready for review again.

@etseidl etseidl left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll do a better pass tomorrow, but here's a nit my agent found.

Comment thread parquet/src/arrow/bloom_filter.rs Resolved
A timestamp, time or duration is checked in the unit of the probe array's data type, so it must match the unit the column was written in. Add a test showing that the same instant in another unit is not found.

@etseidl etseidl left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @bhaskargurram-ai, no more nits from me.

@adriangb are all of your concerns addressed?

@alamb

alamb commented Oct 30, 2026

Copy link
Copy Markdown
Contributor

This PR looks like it is AI bot / merge harvesting to maxxx out statistics for the github account https://github.com/bhaskargurram-ai

I know @etseidl has spent time on it, but we can also close it if it isn't worth our time

@bhaskargurram-ai

Copy link
Copy Markdown
Contributor Author

This PR looks like it is AI bot / merge harvesting to maxxx out statistics for the github account https://github.com/bhaskargurram-ai

I know @etseidl has spent time on it, but we can also close it if it isn't worth our time

@alamb I disagree with that characterization. Yes, I use AI tools in my workflow, and I don't think that's wrong. What matters is whether the change is correct, tested and worth having, and I take responsibility for every line I submit.

This PR fixes a real problem: ArrowSbbf::check could give wrong answers for Arrow types whose physical representation differs from the Parquet one. I added tests for each of those cases, including time_units_are_not_converted. I went through several review rounds with @etseidl, rebased when asked, and addressed each nit until he approved it. On #11741 I tracked the dictionary-writer benchmark regression down to codegen-unit placement with callgrind and fixed it before it merged. None of that is "merge harvesting".
Yes, contributing helps my profile, the same way it helps anyone who contributes to open source. It also improves the project, and the two aren't in conflict.

That said, it's your project and your call. If you'd rather not take this, feel free to close it, and I won't send further PRs to arrow-rs. Thanks @etseidl and @adriangb for the careful reviews either way

@etseidl

etseidl commented Oct 30, 2026

Copy link
Copy Markdown
Contributor

This PR closes a long-standing issue and it appears there's interest from @mbutrovich and @adriangb. I wouldn't lump this PR in with the usual slop, and the author has been responsive. I think we should merge it.

@adriangb

Copy link
Copy Markdown
Contributor

It looks good to me, my vote would be to move forward with it.

@alamb

alamb commented Oct 31, 2026

Copy link
Copy Markdown
Contributor

@alamb I disagree with that characterization. Yes, I use AI tools in my workflow, and I don't think that's wrong. What matters is whether the change is correct, tested and worth having, and I take responsibility for every line I submit.

I think what matters is how much effort you are willing to put in with us, the maintainers, to help verify and get this ready for merge. We all have access to AI tools as well, and making correct looking changes is quite easy. Confirming and merging is hard. It takes effort to load enough context into our brains to evaluate such changes

Thank you for your engagement

@alamb

alamb commented Oct 31, 2026

Copy link
Copy Markdown
Contributor

(also sorry if I sounded harsh / uncaring -- I spent my last few days closing around 100 PRs from various contributors in arrow and datafusion and I was not in a good mood).

@alamb alamb left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you @bhaskargurram-ai @etseidl and @adriangb

I (really) like the high level idea of this PR / API, but I have some suggestions on how to clean up the implementation / avoid so much duplication

Normally cleanups could come as a follow on PR, but in this case I think there may be API implications (e.g. different arguments to the public constructor)

I don't oppose merging it as is, but I do think we should revisit how the mapping is constructed

Comment thread parquet/src/arrow/arrow_writer/mod.rs Resolved
Comment thread parquet/src/arrow/bloom_filter.rs Outdated
/// [`ArrowSbbf::check`] applies the same conversion as [`ArrowWriter`]
/// before checking the filter.
///
/// These are the Arrow types whose values [`ArrowWriter`] stores differently:

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a bit of an implementation detail that I think can be removed from these comments. The whole point of this API is so that users don't have to worry about the details of parquet -> arrow conversion

/// [`ArrowWriter`]: crate::arrow::ArrowWriter
/// [`coerce_types`]: crate::file::properties::WriterPropertiesBuilder::set_coerce_types
#[derive(Debug, Clone, Copy)]
pub struct ArrowSbbf<'a> {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am surprised that this structure doesn't also have the Arrow type

For example the array reader does it like this

// Check if Arrow type is specified, else create it from Parquet type
let data_type = match arrow_type {
Some(t) => t,
None => parquet_to_arrow_field(column_desc.as_ref())?
.data_type()
.clone(),
};

And then does the validation for compatible physical type once rather than on each input array

Comment thread parquet/src/arrow/bloom_filter.rs Outdated

impl<'a> ArrowSbbf<'a> {
/// Create a new [`ArrowSbbf`] for the bloom filter of the column described by `column`
pub fn new(sbbf: &'a Sbbf, column: &ColumnDescriptor) -> Self {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As above, it might be nice to pass in the optional arrow type here, to mirror what the array reader does and reuse the type mapping

Comment thread parquet/src/arrow/bloom_filter.rs Outdated
}
_ => {}
}
self.validate_decimal(array.data_type())?;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this whole function seems to re-derive the Arrow -> Parquet type mapping, which seems like it adds significant unecessary complexity.

Is there any way we can reuse the existing conversion?

Here are the existing conversions found by my agent

Parquet → Arrow

  • parquet_to_arrow_field(&ColumnDescriptor) -> Result:
    /// Convert parquet column schema to arrow field.
    pub fn parquet_to_arrow_field(parquet_column: &ColumnDescriptor) -> Result<Field> {
    let field = complex::convert_type(&parquet_column.self_type_ptr())?;
    let mut ret = Field::new(parquet_column.name(), field.arrow_type, field.nullable);
    let parquet_type = parquet_column.self_type();
    let basic_info = parquet_type.get_basic_info();
    let mut hash_map_size = 0;
    if basic_info.has_id() {
    hash_map_size += 1;
    }
    if has_extension_type(parquet_type) {
    hash_map_size += 1;
    }
    if hash_map_size == 0 {
    return Ok(ret);
    }
    ret.set_metadata(HashMap::with_capacity(hash_map_size));
    if basic_info.has_id() {
    ret.metadata_mut().insert(
    PARQUET_FIELD_ID_META_KEY.to_string(),
    basic_info.id().to_string(),
    );
    }
    try_add_extension_type(ret, parquet_column.self_type())
    }
  • convert_primitive(parquet_type, hint):
    /// Converts [`Type`] to [`DataType`] with an optional `arrow_type_hint`
    /// provided by the arrow schema
    ///
    /// Note: the values embedded in the schema are advisory,
    pub fn convert_primitive(
    parquet_type: &Type,
    arrow_type_hint: Option<DataType>,
    ) -> Result<DataType> {
    let physical_type = from_parquet(parquet_type)?;
    Ok(match arrow_type_hint {
    Some(hint) => apply_hint(physical_type, hint),
    None => physical_type,
    })
    }
  • apply_hint, which restores types from the embedded Arrow schema (Date64, Timestamp(Second), decimal widths, intervals, …):
    /// Uses an type hint from the embedded arrow schema to aid in faithfully
    /// reproducing the data as it was written into parquet
    fn apply_hint(parquet: DataType, hint: DataType) -> DataType {
    match (&parquet, &hint) {
    // Not all time units can be represented as LogicalType / ConvertedType
    (DataType::Int32 | DataType::Int64, DataType::Timestamp(_, _)) => hint,
    (DataType::Int32, DataType::Time32(_)) => hint,
    (DataType::Int64, DataType::Time64(_)) => hint,
    (DataType::Int64, DataType::Duration(_)) => hint,
    // Date64 doesn't have a corresponding LogicalType / ConvertedType
    (DataType::Int64, DataType::Date64) => hint,
    // Coerce Date32 back to Date64 (#1666)
    (DataType::Date32, DataType::Date64) => hint,
    // Timestamps of the same resolution can be converted to a a different timezone.
    (DataType::Timestamp(p, _), DataType::Timestamp(h, Some(_))) if p == h => hint,
    // INT96 default to Timestamp(TimeUnit::Nanosecond, None) (see from_parquet below).
    // Allow different resolutions to support larger date ranges.
    (
    DataType::Timestamp(TimeUnit::Nanosecond, None),
    DataType::Timestamp(TimeUnit::Second, _),
    ) => hint,
    (
    DataType::Timestamp(TimeUnit::Nanosecond, None),
    DataType::Timestamp(TimeUnit::Millisecond, _),
    ) => hint,
    (
    DataType::Timestamp(TimeUnit::Nanosecond, None),
    DataType::Timestamp(TimeUnit::Microsecond, _),
    ) => hint,
    // Determine offset size
    (DataType::Utf8, DataType::LargeUtf8) => hint,
    (DataType::Binary, DataType::LargeBinary) => hint,
    // Read as Utf8
    (DataType::Binary, DataType::Utf8) => hint,
    (DataType::Binary, DataType::LargeUtf8) => hint,
    (DataType::Binary, DataType::Utf8View) => hint,
    // Determine view type
    (DataType::Utf8, DataType::Utf8View) => hint,
    (DataType::Binary, DataType::BinaryView) => hint,
    // Determine interval time unit (#1666)
    (DataType::Interval(_), DataType::Interval(_)) => hint,
    // Promote to Decimal256 or narrow to Decimal32 or Decimal64
    (DataType::Decimal128(_, _), DataType::Decimal32(_, _)) => hint,
    (DataType::Decimal128(_, _), DataType::Decimal64(_, _)) => hint,
    (DataType::Decimal128(_, _), DataType::Decimal256(_, _)) => hint,
    // Potentially preserve dictionary encoding
    (_, DataType::Dictionary(_, value)) => {
    // Apply hint to inner type
    let hinted = apply_hint(parquet, value.as_ref().clone());
    // If matches dictionary value - preserve dictionary
    // otherwise use hinted inner type
    match &hinted == value.as_ref() {
    true => hint,
    false => hinted,
    }
    }
    _ => parquet,
    }
  • from_parquet, the type the reader uses when there's no embedded schema (decimals, unsigned integers, Float16, FixedSizeBinary(n), …):
    fn from_parquet(parquet_type: &Type) -> Result<DataType> {

Arrow → Parquet

physical_type: PhysicalType,
/// The length of the column's values if it is a `FIXED_LEN_BYTE_ARRAY` column,
/// otherwise 0
type_length: usize,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

including the arrow type would naturally also include the type length and decimal details

…iter's conversions

ArrowSbbf now holds the Arrow type of the arrays it checks, like the array
readers do. `ArrowSbbf::try_new` takes it as an optional argument and
otherwise derives it from the column with `parquet_to_arrow_field`. It
validates the type once, with the writer's `arrow_to_parquet_type`, instead
of matching each array's type against the column's physical type, so the
decimal scale checks and the FIXED_LEN_BYTE_ARRAY length check move out of
`check`. `check` only compares each array's type with the stored type and
passes the values to the writer's `visit_i32`, `visit_i64` and
`visit_fixed_len_byte_array`.

As the type is validated up front, the visitors no longer need an
`unsupported` hook for ArrowSbbf's error messages, so that change to the
writer is reverted.

The table of Arrow types that ArrowWriter converts is removed from the docs.
@adriangb

Copy link
Copy Markdown
Contributor

Thanks for reviewing Andrew. I agree it looks like adding a field holding the arrow type alongside the physical type would be a significant simplification and shouldn't be hard to do. @bhaskargurram-ai could you handle that?

@bhaskargurram-ai

Copy link
Copy Markdown
Contributor Author

Thanks @alamb for the review, and @adriangb for picking it up. I've pushed the change. ArrowSbbf now keeps the Arrow type alongside the physical type. It is either passed to ArrowSbbf::try_new(sbbf, column, Option) or, if None, derived from the column with parquet_to_arrow_field, the same way PrimitiveArrayReader does it. The type is validated against the column once in try_new using the writer's arrow_to_parquet_type, and check then passes the values straight to the writer's visit_i32 / visit_i64 / visit_fixed_len_byte_array. That removes the second hand-written mapping and the per-array decimal checks. I also removed the type table from the docs.One special case is left: decimals use the column's own width, so files that store decimals in wider fixed-length arrays (for example from Impala) can still be checked.
Since the constructor is now try_new and returns a Result, a mismatched type is reported up front instead of on the first check. Happy to adjust the naming or signature if you'd prefer something else.

@etseidl etseidl left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @bhaskargurram-ai, just two minor nits my agent found.

Thanks for the review @alamb. I'm hoping to get as much of the Bloom filter work in as we can so we can move on to extracting into its own crate.

Comment thread parquet/src/arrow/bloom_filter.rs Outdated
///
/// Returns an error if values of `arrow_type` cannot be stored in `column`,
/// or if `arrow_type` is a decimal and `column` is not a decimal of the same
/// scale, or the other way around.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggest adding a note about INTERVAL handling requiring a type hint. ArrowSbbf::try_new(..., None) derives its type through parquet_to_arrow_field (parquet/src/arrow/bloom_filter.rs:120–124). Parquet INTERVAL inference chooses DayTime, explicitly acknowledging ambiguity in parquet/src/arrow/schema/primitive.rs:324–333. The reader's DayTime conversion drops the stored months component (parquet/src/arrow/array_reader/fixed_len_byte_array.rs:264–271).

/// Returns an error if values of `arrow_type` cannot be stored in `column`,
/// or if `arrow_type` is a decimal and `column` is not a decimal of the same
/// scale, or the other way around.
pub fn try_new(

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For an actual INTERVAL FLBA(12) column, try_new(..., Some(Interval(MonthDayNano))) succeeds because schema conversion accepts that layout. A matching array subsequently returns NYI in the writer visitor. Recommend rejecting this unsupported type in construction, including dictionary/REE wrappers.

@mbutrovich mbutrovich left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @bhaskargurram-ai, the try_new shape with the Arrow type reads better than the earlier version.

Could you update the PR description to match the head commit? It still describes the visit_i32 / visit_i64 / visit_fixed_len_byte_array extraction, which landed in #11741, and the docs table of converted types, which was removed. It also says ArrowSbbf is built from an Sbbf and a ColumnDescriptor, and that decimals of any precision can be passed to check. As I read check, the array now has to have exactly the type given to try_new, and precision differences are handled only between that type and the column.

Comment on lines +262 to +266
(None, None) => {
written.get_physical_type() == physical_type
&& (physical_type != PhysicalType::FIXED_LEN_BYTE_ARRAY
|| type_length(written) == type_length(column))
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should try_new also return an error when the Arrow type's time unit differs from the column's? This arm compares only the physical type, so a TIMESTAMP(MILLIS) column accepts Timestamp(Second), Timestamp(Microsecond) and Timestamp(Nanosecond), and a TIME(MICROS) column accepts Time64(Nanosecond). Each then returns a definite false for an instant that is in the file. array_type_differs_from_arrow_type shows the Microsecond case succeeding in try_new.

The docs note for units was added before try_new had the Arrow type. Now that it has both types, the decimal arm above already rejects a scale mismatch, which is the same kind of error: the same physical values mean different things. A wrong unit gives a false negative, so a caller that prunes on it skips row groups that match. Comparing the Timestamp and Time units of written and column when both have one would cover the microsecond and nanosecond cases. Timestamp(Second) is written with no logical type, so it would need its own check against a column that has a unit. Could time_units_are_not_converted then expect the error instead of false?

@bhaskargurram-ai

Copy link
Copy Markdown
Contributor Author

Thanks @bhaskargurram-ai, the try_new shape with the Arrow type reads better than the earlier version.

Could you update the PR description to match the head commit? It still describes the visit_i32 / visit_i64 / visit_fixed_len_byte_array extraction, which landed in #11741, and the docs table of converted types, which was removed. It also says ArrowSbbf is built from an Sbbf and a ColumnDescriptor, and that decimals of any precision can be passed to check. As I read check, the array now has to have exactly the type given to try_new, and precision differences are handled only between that type and the column.

Thanks @mbutrovich, good catch. Pushed 72ba948: try_new now compares the time unit of the Arrow type with the column's, so a TIMESTAMP(MILLIS) column rejects Timestamp(µs)/Timestamp(ns) and a TIME(MICROS) column rejects Time64(ns). Timestamp(Second) and Time32(Second) are written without a unit, so they're rejected against any column that has one. time_units_are_not_converted is now time_units_must_match and expects the errors. I also updated the PR description to match the current code.

Comment on lines +279 to +283
// The same values mean different instants in different units
&& match (time_unit(written), time_unit(column)) {
(Some(written_unit), Some(unit)) => written_unit == unit,
_ => true,
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not blocking, and fine as a follow-up: should a column without a unit also reject an Arrow type that has one? This arm accepts it, so a plain INT64 column written from Timestamp(Second) accepts Timestamp(Millisecond), and check then returns false for instants that are in the file. ArrowWriter always writes a unit for millisecond, microsecond, and nanosecond timestamps, so as I read it such a column was not written from that type. Rejecting it would break a caller that checks a plain INT64 column, such as epoch milliseconds stored as a long, with a timestamp type. If that case should keep working, a sentence in the try_new docs would make it explicit.

@mbutrovich mbutrovich left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @bhaskargurram-ai, this looks good to me.

Cargo unifies features, so enabling it here turned it on for every crate
using the same hashbrown version. #[inline] on Interner::intern is what keeps
the dictionary encoder's per-value call inlined.
Comment thread parquet/Cargo.toml
tokio = { version = "1.0", optional = true, default-features = false, features = ["macros", "rt", "io-util"] }
# `inline-more` marks `HashTable::entry` `#[inline]`, so the dictionary encoder's
# hash probe is compiled into `Interner::intern` (see the note there).
hashbrown = { version = "0.17", default-features = false, features = ["inline-more"] }

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This got changed after my approval. It seems out of scope for this PR. Why did it change?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it got pulled in from #11741 (comment), but agreed it shuold not be handled in this PR, but yes before release.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry about that, you're right that it's out of scope. I added it after the discussion on #11741 and shouldn't have put it here. I've reverted it (ab809c7), so the PR is back to what you approved. As @adriangb suggested, I'll open a separate small PR to drop inline-more before the release.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug parquet Changes to the parquet crate

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Bloom filters for i8 and i16 always return false negatives

6 participants