-
Type:
Bug
-
Resolution: Fixed
-
Priority:
Major - P3
-
Affects Version/s: None
-
Component/s: Block Cache
-
None
-
Environment:GCC 13, Ubuntu 24.04
-
Storage Engines - Persistence
-
144.062
-
StorEng - Defined Pipeline
-
1
In __read_decompress (src/block_cache/block_io.c line 383), the function signature omits the on-disk compressed block size, accepting only mem_sz (dsk->mem_size, the uncompressed in-memory size). This produces two coupled bugs: first, at lines 406-408 the call to compressor->decompress passes mem_sz - WT_BLOCK_COMPRESS_SKIP as src_len instead of the correct ip->size - WT_BLOCK_COMPRESS_SKIP, weakening the bounds check every compressor implementation uses to guard against reading past the compressed payload (for snappy, decompression still succeeds because snappy self delimits via a stored 8-byte prefix and ignores src_len for actual decompression; for non-self-delimiting compressors the wrong src_len causes the decompressor to process bytes beyond the compressed data). Second, at line 416 __wt_buf_initsize(session, out, mem_sz) sets out->size = mem_sz, and because successful decompression enforces result_len = mem_sz - WT_BLOCK_COMPRESS_SKIP (line 409), the expression result_len / (out->size - WT_BLOCK_COMPRESS_SKIP) reduces to (mem_sz - SKIP) / (mem_sz - SKIP) = 1 identically, regardless of the actual compression factor, so every disaggregated read of a compressed page increments compress_read_ratio_hist_2 and starves hist_4 through hist_max. The non-disagg path in __wt_blkcache_read (lines 311, 325) correctly uses tmp->size (the scratch buffer holding the on-disk block) in both positions; __read_decompress was written without an equivalent parameter.
For Reproduce;
I added python suite test (test/suite/test_disagg_compression_ratio01.py) reproduces the issue and verifies the fix. (I added before/after outputs and telemetry outputs on JIRA).
The test writes 2000 snappy-compressed rows to a disaggregated table, checkpoints, reopens without discarding the page store, and performs a full scan to force all pages through __read_decompress.
Without the fix, all 14 page reads land in hist_2 (hist_2=14, hist_ge4=0) and the assertion assertGreater(hist_ge4, 0) fails.
With the fix, all 14 reads correctly land in hist_32 (hist_32=14, reflecting the true snappy ratio) and the test passes.
Proposed Fix;
Adds in_size to the signature, passes ip->size at both call sites (lines 582 and 635), adds a bounds guard against unsigned underflow, and substitutes in_size - WT_BLOCK_COMPRESS_SKIP in the decompress call and ratio denominator.