zeroshade commented on code in PR #1033:
URL: https://github.com/apache/arrow-go/pull/1033#discussion_r3666628168
##########
parquet/writer_properties.go:
##########
@@ -366,6 +371,9 @@ func WithPageIndexEnabledPath(path ColumnPath, enabled
bool) WriterProperty {
// it is abandoned and not written to the file.
func WithMaxBloomFilterBytes(nbytes int64) WriterProperty {
return func(cfg *writerPropConfig) {
+ if nbytes < minimumBloomFilterBytes || nbytes >
maximumBloomFilterBytes {
+ panic("parquet: maximum bloom filter size must be
between 32 bytes and 128 MiB")
+ }
Review Comment:
This is the item I'd most like addressed before merge, because it changes
documented public behavior.
The **lower** bound is a strict improvement — values below 32 already
crashed at write time (`InsertHash` on a zero-byte filter), so panicking early
is better in every way.
The **upper** bound is different: `WithMaxBloomFilterBytes(nbytes > 128
MiB)` previously *worked*. It was silently clamped downstream by
`min(maximumBloomFilterBytes, maxBytes)`, so a caller passing e.g. 256 MiB got
a valid 128 MiB filter and a working writer. After this change that same call
panics.
Failing fast is arguably the right choice, and it's the consistent
resolution of the policy question I raised. But the doc comment just above
(`:369-371`) doesn't mention that this setter can panic at all, so a caller has
no way to know. Could you add a line — something like *"Panics if nbytes is
outside the supported range of 32 bytes to 128 MiB."*
@zeroshade may also want a release-note entry, since this converts
previously-working input into a panic.
Minor wording point: the panic message hard-codes "32 bytes and 128 MiB"
while the bounds come from `minimumBloomFilterBytes`/`maximumBloomFilterBytes`.
If those constants ever change, the message goes stale — worth formatting them
in.
##########
parquet/writer_properties.go:
##########
@@ -404,10 +413,17 @@ func WithBloomFilterFPP(fpp float64) WriterProperty {
// for writing bloom filters.
func WithBloomFilterFPPFor(path string, fpp float64) WriterProperty {
return func(cfg *writerPropConfig) {
+ validateBloomFilterFPP(fpp)
cfg.bloomFilterFPPs[path] = fpp
}
}
+func validateBloomFilterFPP(fpp float64) {
+ if fpp <= 0 || fpp >= 1 || math.IsNaN(fpp) {
+ panic("parquet: bloom filter false-positive probability must be
in (0, 1)")
+ }
+}
Review Comment:
No change needed here — the helper and its placement are right, and routing
`WithBloomFilterFPP`, `WithBloomFilterFPPFor` and (transitively)
`WithBloomFilterFPPPath` through it is what makes the deeper panic unreachable
from the public API. I verified there's no bypass:
`defColumnProps`/`columnProps` are unexported and no `WriterProperty` injects a
whole `ColumnProperties`.
One note for the future, since this file now needs the bounds:
`minimumBloomFilterBytes`/`maximumBloomFilterBytes` exist in **both** `parquet`
(`:64-65`) and `parquet/metadata` (`bloom_filter.go:44,46`). I confirmed the
duplication is forced — `parquet/metadata` imports `parquet`, so the reverse
direction would be an import cycle — and the values agree today (32 /
134217728). Still a silent-drift hazard if one side is ever tuned. A short
comment on each pair pointing at the other would make that safe.
##########
parquet/metadata/bloom_filter_test.go:
##########
@@ -272,6 +287,27 @@ func TestAdaptiveBloomFilterEdgeCases(t *testing.T) {
assert.Truef(t, bf.CheckHash(h), "hash %d not found
after GC - potential GC safety issue", h)
}
})
+
+ t.Run("clamps maximum size to the minimum allocation", func(t
*testing.T) {
+ bf := NewAdaptiveBlockSplitBloomFilter(0, 1, 0.01, col,
mem).(*adaptiveBlockSplitBloomFilter)
+ defer func() {
+ for _, candidate := range bf.candidates {
+ candidate.bloomFilter.cancelCleanup()
+ candidate.bloomFilter.data.Release()
+ }
+ }()
+
+ assert.EqualValues(t, minimumBloomFilterBytes, bf.maxBytes)
+ assert.NotPanics(t, func() { bf.InsertHash(1) })
+ })
+
+ t.Run("rejects invalid false-positive probabilities", func(t
*testing.T) {
+ for _, fpp := range []float64{-0.1, 0, 1, math.NaN()} {
+ assert.PanicsWithValue(t,
+ "parquet: bloom filter false-positive
probability must be in (0, 1)",
+ func() { NewAdaptiveBlockSplitBloomFilter(1024,
1, fpp, col, mem) })
Review Comment:
`assert.PanicsWithValue` pins the exact panic string, so any rewording of
the message — including the `parquet:` prefix — breaks this test with no
behavior change. The sibling subtest just above uses `assert.NotPanics`, and
the rest of this file favors the looser forms; `assert.Panics` would match.
If you want to keep asserting *which* validation fired, a substring check on
the recovered value would be more durable than exact equality.
Also worth knowing about this subtest specifically: against **unpatched**
code it wouldn't fail cleanly, it would **hang** (that's the infinite loop in
`expectedNDV` this PR fixes), so on the pre-fix tree it manifests as a CI
timeout rather than a crisp assertion failure. That's fine for guarding the
patched behavior — just noting it isn't a sharp regression detector if anyone
ever reverts the production change.
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: [email protected]
For queries about this service, please contact Infrastructure at:
[email protected]