Skip to content

feat(testtool): generate FSST reference data for other implementations - #153

Merged
CurtHagenlocher merged 2 commits into
mainfrom
feat-testtool-fsst-testdata
Aug 13, 2026
Merged

feat(testtool): generate FSST reference data for other implementations#153
CurtHagenlocher merged 2 commits into
mainfrom
feat-testtool-fsst-testdata

Conversation

@CurtHagenlocher

@CurtHagenlocher CurtHagenlocher commented Aug 13, 2026

Copy link
Copy Markdown
Collaborator

Adds ew-test-tool create_fsst_test_file, which writes one small Parquet file exercising the FSST encoding's corner cases, for use as reference data by other implementations.

Modelled on the ALP file in apache/parquet-testing#119: one file rather than a corpus, and every FSST column duplicated by a DELTA_LENGTH_BYTE_ARRAY column holding the same values — so a reader can bit-compare the two without needing any expected data shipped alongside the file. That predecessor was rejected partly for size, so this one is 182 KB uncompressed (80 KB with --compression zstd).

The corpus is synthetic and seeded, so the file is reproducible from source and carries no third-party licensing. Four row groups, each training its own symbol table (§1.4):

Row group Rows Coverage
urls 400 happy path — high-cardinality URLs over 14 pages sharing one table
nulls_and_empties 240 NULLs and zero-length values, def levels alongside FSST pages
escapes 200 bytes absent from the table — forces escape sequences
long_values 16 ~400-byte values, few per page, so the offset array encoding differs

It verifies rather than asserts

The command reads the file back before reporting, because most of what makes it useful is chosen rather than declared: the writer picks the offset encoding per page, the trainer picks the symbol table, and §7.5 may decline FSST for a chunk outright. Two rounds of that feedback changed the corpus:

  • The escape section produced no escapes at all. It put a rare code point in every value, so the trainer simply learned it as a symbol. Only every ninth value carries one now, and the file contains 181 escape sequences.
  • A binary chunk fell back under §7.5 and silently vanished from the summary, which read as a bug in the summary rather than the writer declining. Fallback is now reported explicitly.

Sample output:

Row group 3 — long_values
  string_fsst       FSST    table of  131 symbols (longest  8, body 463 bytes), 4 data pages, offsets 4 PLAIN / 0 DELTA, 32 escapes
  string_fsst16     FSST_16 table of  421 symbols (longest  8, body 954 bytes), 4 data pages, offsets 4 PLAIN / 0 DELTA, no escapes

OK — every FSST column decodes bit-identically to its reference column.

Both offset encodings are covered (long_values is the row group where PLAIN beats DELTA), and both symbol table widths appear in every row group.

Known coverage gap

No FSST_16 column contains an escape sequence, so the file does not exercise FSST_16 escape decoding. That is not fixable from the writer: Clast.Fsst's 16-bit tables always contain all 256 single-byte symbols, so nothing is ever left to escape. It is also the one place the two variants are genuinely asymmetric, and the one part of the FSST_16 wire format still resting on inference rather than confirmed spec text.

Also

Grants Core internals to the TestTool — every other consuming project already had them — so the page walk can decompress a compressed values section and report page detail for --compression zstd files too.

🤖 Generated with Claude Code

Adds `create_fsst_test_file`, which writes one small Parquet file exercising
the FSST encoding's corner cases, modelled on the ALP file in
apache/parquet-testing#119: one file rather than a corpus, and every FSST
column duplicated by a DELTA_LENGTH_BYTE_ARRAY column holding the same values,
so a reader can bit-compare the two without any expected data alongside the
file.

The corpus is synthetic and seeded, so the file is reproducible and carries no
third-party licensing. Four row groups, each training its own symbol table:
URLs, nulls-and-empties, escape-forcing values, and long values.

The command reads the file back before reporting, because most of what makes it
useful is chosen rather than declared — the writer picks the offset encoding per
page, the trainer picks the symbol table, and §7.5 may decline FSST for a chunk
entirely. Two rounds of that feedback changed the corpus:

- The escape section produced no escapes at all. It put a rare code point in
  every value, so the trainer simply learned it as a symbol. Only every ninth
  value carries one now, and the file has 78 escape sequences.
- A binary chunk fell back under §7.5 and vanished from the summary, which read
  as a bug in the summary rather than the writer declining. Fallback is now
  reported explicitly.

Also shrank the file from 520 KB to 182 KB uncompressed (80 KB with --compression
zstd), since parquet-testing#119 rejected its predecessor for size.

Grants Core internals to the TestTool, which every other consuming project
already had, so the page walk can decompress a compressed values section.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

Adds a new ew-test-tool create_fsst_test_file command that generates a small, reproducible Parquet file designed to exercise FSST encoding corner cases, then reads it back to verify FSST columns decode bit-identically to reference columns and to report what was actually written (table details, per-page offset encoding, escape counts). It also extends Core internals access to the TestTool so the command can walk/decompress pages when describing the output.

Changes:

  • Add CLI command wiring and help text for create_fsst_test_file.
  • Introduce FsstTestData to synthesize the corpus, write the file with FSST/FSST_16 columns plus reference columns, then verify and describe the encoded pages.
  • Grant EngineeredWood.Parquet.TestTool access to EngineeredWood.Core internals via InternalsVisibleTo.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 3 comments.

File Description
src/EngineeredWood.Parquet.TestTool/Program.cs Adds command dispatch + usage/help text for the new FSST test-file generator.
src/EngineeredWood.Parquet.TestTool/FsstTestData.cs Implements corpus generation, Parquet writing options, verification, and page-walk reporting for FSST/FSST_16.
src/EngineeredWood.Core/EngineeredWood.Core.csproj Adds InternalsVisibleTo for the TestTool so it can use Core internals needed for detailed page inspection.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread src/EngineeredWood.Parquet.TestTool/FsstTestData.cs Outdated
Comment thread src/EngineeredWood.Parquet.TestTool/FsstTestData.cs Outdated
Comment thread src/EngineeredWood.Parquet.TestTool/FsstTestData.cs
The binary escape corpus overwrote the last byte of its own prefix. The
overwrite index was hardcoded to 24 against a 25-byte prefix, so it ate the
trailing '-'. Deriving the offset from the prefix instead is not cosmetic: it
lifts the file from 110 escape sequences to 181, and the binary FSST column in
the escapes row group goes from 0 escapes to 2 — that column was not exercising
the escape path at all.

DescribeTable printed `headerSize + body.Length - headerSize`, which is just
body.Length, leaving headerSize computed and unused. The whole serialized body
is the right thing to report, so the number is unchanged; the dead term is gone
and the label is now explained.

--compression parsed the entire CompressionCodec enum, so LZO — which the writer
cannot emit — was accepted and failed later as a NotSupportedException from
inside the page writer. Validates against the codecs the writer supports and
fails with a usage error naming them.

Found by Copilot review on #153.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@CurtHagenlocher
CurtHagenlocher merged commit 452ebcb into main Aug 13, 2026
1 check passed
@CurtHagenlocher
CurtHagenlocher deleted the feat-testtool-fsst-testdata branch August 13, 2026 12:56
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants