ExportXMLWordPrintableJSON

    • Type: Bug
    • Resolution: Fixed
    • Priority: Major - P3
    • WT12.0.0, 9.1.0-rc0
    • Affects Version/s: None
    • Component/s: Block Cache
    • None
    • Environment:
      GCC 13, Ubuntu 24.04
    • Storage Engines - Persistence
    • 144.653
    • 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.

        1. before_histogram_output.txt
          2 kB
        2. before_fix_output.txt
          1 kB
        3. after_histogram_output.txt
          2 kB
        4. after_fix_output.txt
          0.2 kB

            Assignee:
            [DO NOT USE] Backlog - Storage Engines Team
            Reporter:
            Tuna KARABACAK (EXT)
            Votes:
            0 Vote for this issue
            Watchers:
            1 Start watching this issue

              Created:
              Updated:
              Resolved: