Repository navigation
Conversation
|
Note this is stacked on #11953. |
ffa993a to
4bd0911
Compare
e86ec10 to
8a73701
Compare
|
👀 |
| #[derive(Debug)] | ||
| enum DecodeState { | ||
| /// Finding the first Bloom filter range | ||
| FindingRange, | ||
| // Reading the SBBF header | ||
| ReadingSbbfHeader(Range<u64>), | ||
| // Reading the SBBF bitset bytes | ||
| ReadingSbbfBitset(Range<u64>), | ||
| // Decoding is complete | ||
| Finished, | ||
| /// State left during the `try_decode` method so something valid is present. | ||
| /// This state should never be observed. | ||
| Intermediate, | ||
| } |
| /// Attempts to decode the filter, returning the file ranges needed if data | ||
| /// is missing. Repeated calls before supplying data return the same ranges. | ||
| /// | ||
| /// Without prefetched data, requests one range when `bloom_filter_length` is | ||
| /// known, or two otherwise (a 20-byte header estimate, then the bitset). | ||
| /// Headers larger than the estimate require an advertised length. | ||
| /// After returning [`DecodeResult::Data`], subsequent calls return | ||
| /// [`DecodeResult::Finished`]. | ||
| pub fn try_decode(&mut self) -> Result<DecodeResult<Sbbf>> { |
There was a problem hiding this comment.
Would it be helpful to have an agent generate a state transition diagram so readers can easily visualize the code?
There was a problem hiding this comment.
I'll see what it comes up with, although I think @alamb enjoys making ascii art 😂
There was a problem hiding this comment.
Ok, it's there, @adriangb. That's the best we could come up with 😅
There was a problem hiding this comment.
I do enjoy making ascii art. Though recently I had a 🤯 experience of having my agent reverse engineer the monodraw file format so I can have it draft ascii art diagrams and then tweak them visually ❤️
| /// Headers larger than the estimate require an advertised length. | ||
| /// After returning [`DecodeResult::Data`], subsequent calls return | ||
| /// [`DecodeResult::Finished`]. |
There was a problem hiding this comment.
Do we know under what circumstances this happens? Does this error if no length is provided or does it request more data?
There was a problem hiding this comment.
This is a guard for any changes that may be made to the thrift header, either via a new version of the spec, or proprietary changes that would otherwise be ignored by a streaming thrift decoder. One would hope that the bloom filter length field would be populated in such a scenario, which is what the comment is driving at. Were an expanded header not be accompanied by a valid length, then read_bloom_filter_header_and_length would hit an EoF and return an error, and the state machine would be left in an unusable state.
Co-authored-by: Adrian Garcia Badaracco <1755071+adriangb@users.noreply.github.com>
Replace hand-written `match ... other => panic!` blocks and `assert!(matches!(...))` checks with the `expect_data`, `expect_needs_data` and `expect_finished` helpers used by the other push decoder tests. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
There was a problem hiding this comment.
Thanks @etseidl and @adriangb -- this looks great to me
I did take the liberty of tweaking some tests to use the same pattern as the other push decoders in 4d4935e, just for consistency (e.g.
arrow-rs/parquet/src/file/metadata/push_decoder.rs
Lines 1079 to 1094 in b231cf1
| ); | ||
| } | ||
|
|
||
| #[derive(Default)] |
| "Bloom filter length does not match its header" | ||
| )); | ||
| loop { | ||
| let result = decoder.try_decode()?; |
| /// Attempts to decode the filter, returning the file ranges needed if data | ||
| /// is missing. Repeated calls before supplying data return the same ranges. | ||
| /// | ||
| /// Without prefetched data, requests one range when `bloom_filter_length` is | ||
| /// known, or two otherwise (a 20-byte header estimate, then the bitset). | ||
| /// Headers larger than the estimate require an advertised length. | ||
| /// After returning [`DecodeResult::Data`], subsequent calls return | ||
| /// [`DecodeResult::Finished`]. | ||
| pub fn try_decode(&mut self) -> Result<DecodeResult<Sbbf>> { |
There was a problem hiding this comment.
I do enjoy making ascii art. Though recently I had a 🤯 experience of having my agent reverse engineer the monodraw file format so I can have it draft ascii art diagrams and then tweak them visually ❤️
| .contains("Bloom filter read range overflow") | ||
| ); | ||
| assert_eq!(reader.position(), 0); | ||
| #[test] |
There was a problem hiding this comment.
it isn't clear to me why these tests are not with the others in parquet/tests/arrow_reader/bloom_filter/push.rs -- as they all seem to be testing the public APIs
I recommend consolidating to parquet/tests/arrow_reader/bloom_filter/push.rs
| #[inline] | ||
| fn hash_as_bytes<A: AsBytes + ?Sized>(value: &A) -> u64 { | ||
| XxHash64::oneshot(SEED, value.as_bytes()) | ||
| /// Decoding state machine |
There was a problem hiding this comment.
as a minor nit I might move this into its own mod like bloom_filter/push.rs or something, but this is totally fine too
Which issue does this PR close?
Sbbf::read_from_column_chunk_asyncBloom filter API #11953Rationale for this change
Reduce code duplication and centralize Bloom filter parsing and validation using a push decoder.
What changes are included in this PR?
SbbfPushDecoderto decode Bloom filters from caller-provided byte ranges without I/O.Sbbf::from_bytes.Are these changes tested?
Yes, new tests are added
Are there any significant user-facing changes?
Adds the public
SbbfPushDecoderAPI. Existing read APIs are unchanged, but synchronous reads andSbbf::from_bytesnow reject invalid bitset sizes and inconsistent declared lengths, matching the asynchronous behavior introduced in #11953.AI disclosure
Implementation and regression tests were prepared with coding-agent assistance, but were reviewed by me.