fsst: regression test for i32 offset overflow in fsst_compress#7832
Closed
fsst: regression test for i32 offset overflow in fsst_compress#7832
Conversation
`fsst_compress_iter` (encodings/fsst/src/compress.rs:72) hardcodes
`VarBinBuilder::<i32>` for the compressed output, so any input whose
cumulative compressed bytes exceed `i32::MAX` panics in
`vortex-array/src/arrays/varbin/builder.rs:62` with
Other error: Failed to convert sum of N and M to offset of type i32
Hit in practice on a real >4 GiB string column going through `vxio.write`.
The bug isn't in the input-conversion path — that's zero-copy and respects
the input offset width — so widening the input to `large_string` (i64
offsets) at the pyarrow side does NOT help; FSST's output builder runs
either way.
Add a stress regression test that constructs a `VarBinArray<i64>` with
~2.5 GiB of high-entropy ASCII (FSST cannot compress it below the i32
ceiling) and runs `fsst_compress` end-to-end. The test currently panics
with the documented message; it's wrapped in `#[should_panic]` so the
test passes today and trips when the underlying bug is fixed — at which
point the maintainer drops `#[should_panic]` and the trailing
`assert_eq!(compressed.len(), len)` becomes the live assertion.
Gated with `#[test_with::env(CI)]` + `#[test_with::no_env(VORTEX_SKIP_SLOW_TESTS)]`
(matching the precedent in vortex-btrblocks/src/schemes/integer.rs:1113)
because the test allocates ~5 GiB peak and runs in ~6 s under release.
Verified locally:
- `cargo test -p vortex-fsst fsst_compress_offsets`
→ ignored, because variable CI not found
- `CI=1 cargo test --release -p vortex-fsst fsst_compress_offsets`
→ 1 passed (panics as expected, captured by should_panic)
- `CI=1 VORTEX_SKIP_SLOW_TESTS=1 cargo test --release -p vortex-fsst fsst_compress_offsets`
→ ignored, because variable VORTEX_SKIP_SLOW_TESTS was found
- `cargo +nightly fmt --all` clean
- `cargo clippy -p vortex-fsst --all-targets --all-features` clean
Co-Authored-By: Claude <noreply@anthropic.com>
Signed-off-by: mprammer <martin@spiraldb.com>
Comment on lines
+53
to
+55
| let pool: Vec<u8> = (0..POOL_LEN) | ||
| .map(|_| *ALPHABET.choose(&mut rng).unwrap()) | ||
| .collect(); |
Contributor
There was a problem hiding this comment.
do you need this? vs using a single char (so the test runs faster)
Author
There was a problem hiding this comment.
Tried first: FSST collapses repetitive bytes, so the output never crosses 2 GiB.
Comment on lines
+57
to
+67
| let mut builder = VarBinBuilder::<i64>::with_capacity(N); | ||
| for i in 0..N { | ||
| let off = (i.wrapping_mul(31337)) % (POOL_LEN - STRING_LEN); | ||
| builder.append_value(&pool[off..off + STRING_LEN]); | ||
| } | ||
| let array = builder.finish(DType::Utf8(Nullability::NonNullable)); | ||
|
|
||
| let compressor = fsst_train_compressor(&array); | ||
| let len = array.len(); | ||
| let dtype = array.dtype().clone(); | ||
| let mut ctx = LEGACY_SESSION.create_execution_ctx(); |
Contributor
There was a problem hiding this comment.
could we directly build the fsst array to save some time?
Author
There was a problem hiding this comment.
Bug is in fsst_compress_iter (compress.rs:72); constructing the array directly skips the panicking path.
Co-Authored-By: Claude <noreply@anthropic.com> Signed-off-by: mprammer <martin@spiraldb.com>
joseph-isaacs
approved these changes
May 7, 2026
Contributor
|
Can we just.... also fix it?? |
connortsui20
requested changes
May 7, 2026
Contributor
connortsui20
left a comment
There was a problem hiding this comment.
we should just fix this
Contributor
|
We should just write the offsets as i64 (as an aside, why do we use signed integers here and not unsigned?), since the compressor is going to narrow the codes regardless: let utf8 = data.array_as_utf8().into_owned();
let compressor_fsst = fsst_train_compressor(&utf8);
let fsst = fsst_compress(&utf8, utf8.len(), utf8.dtype(), &compressor_fsst, exec_ctx);
let uncompressed_lengths_primitive = fsst
.uncompressed_lengths()
.clone()
.execute::<PrimitiveArray>(exec_ctx)?
.narrow()?;
let compressed_original_lengths = compressor.compress_child(
&uncompressed_lengths_primitive.into_array(),
&compress_ctx,
self.id(),
0,
exec_ctx,
)?;
let codes_offsets_primitive = fsst
.codes()
.offsets()
.clone()
.execute::<PrimitiveArray>(exec_ctx)?
.narrow()?;
let compressed_codes_offsets = compressor.compress_child(
&codes_offsets_primitive.into_array(),
&compress_ctx,
self.id(),
1,
exec_ctx,
)?; |
This was referenced May 7, 2026
connortsui20
pushed a commit
that referenced
this pull request
May 8, 2026
Adds an `#[ignore]`d regression test for #7833 to the existing `encodings/fsst/src/tests.rs`. The test allocates ~5 GiB total, so it is opt-in via `--ignored`: cargo test --release -p vortex-fsst -- --ignored fsst_compress_offsets This is an alternative to #7832 that keeps the test alongside the other FSST tests instead of introducing a new module, and avoids the `test-with` dev-dependency. Signed-off-by: Claude <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Regression test for #7833 — FSST
i32offset overflow. Wrapped in#[should_panic(expected = "to offset of type i32")]so it merges green; drop the attribute alongside the fix and the trailingassert_eq!(compressed.len(), len)becomes the live assertion.Gated
#[test_with::env(CI)]+#[test_with::no_env(VORTEX_SKIP_SLOW_TESTS)]to matchvortex-btrblocks/src/schemes/integer.rs:1113.🤖 Generated with Claude Code