Skip to content

feat: add block-level Brotli/GZip compression for SingleFile storage - #344

Merged
MPCoreDeveloper merged 1 commit into
MPCoreDeveloper:masterfrom
saltus7:feat/block-compression
Aug 30, 2026
Merged

feat: add block-level Brotli/GZip compression for SingleFile storage#344
MPCoreDeveloper merged 1 commit into
MPCoreDeveloper:masterfrom
saltus7:feat/block-compression

Conversation

@saltus7

@saltus7 saltus7 commented Aug 29, 2026

Copy link
Copy Markdown
Collaborator

Block-level Brotli/GZip compression for SingleFile (.scdb) storage

Summary

Adds transparent, per-block compression to the SingleFile storage mode. Compression is applied before encryption on write and removed after decryption on read, so the two layers compose cleanly without interfering with each other. A per-block Compressed flag in the block registry tracks state, allowing mixed compressed and uncompressed blocks within the same file.

Motivation

SingleFile mode pre-allocates metadata pages (registry, FSM, WAL, table directory) and stores every block uncompressed. For workloads with repetitive or text-heavy data, this wastes significant disk space. Block-level compression lets the engine shrink on-disk payloads without changing the file format's structural layout or breaking compatibility with existing encrypted databases.

Implementation

  • BlockCompressionMode enum: None, Brotli, GZip — selectable via DatabaseOptions.BlockCompression
  • BlockCompressor service: applies compression only to blocks at or above DatabaseOptions.CompressionThreshold (default 64 bytes); smaller blocks pass through uncompressed to avoid negative compression ratios
  • SingleFileStorageProvider: compression happens between the serialization layer and the encryption layer on write; decompression happens between decryption and deserialization on read
  • Per-block flag: the block registry records whether each block was compressed, so mixed-mode files read correctly regardless of which blocks were above/below threshold
  • Vacuum preservation: VacuumFullAsync propagates BlockCompression and CompressionThreshold to the temp file options, so vacuumed files retain their compression settings

POC statistics

Statistics below are from the Brotli benchmark. GZip is supported and tested but not benchmarked in the POC.

A 10 million record proof-of-concept was run before implementing the production path:

Metric Result
Peak file size reduction 87%
Insert throughput improvement 30% faster
Spot-read verifications All passed with human-readable data integrity

Test coverage

16 regression tests in SingleFileCompressionTests.cs covering:

  • Brotli, GZip, and None roundtrips (write → reopen → read → verify)
  • Compression + AES-256-GCM encryption combined roundtrips
  • Plaintext-not-on-disk verification for encrypted + compressed blocks
  • Wrong compression mode on reopen throws InvalidOperationException
  • Encryption mismatch on reopen throws
  • Small block threshold (below threshold = uncompressed)
  • Large block threshold (above threshold = compressed)
  • Mixed compressed + uncompressed blocks in the same file
  • Full vacuum preserves compressed data
  • Incremental vacuum preserves compressed data
  • On-disk block length reduction verified via GetBlockMetadata
  • DatabaseFactory integration with compression-enabled options
  • DatabaseFactory wrong compression mode on reopen throws

All 16 tests pass.

Compatibility note

If you have a different compression or storage strategy planned for v2.0 that I can't see from the public repo, no concerns at all — happy to rework, rebase, or withdraw this if it conflicts with your roadmap. This was built against the v1.9.8 codebase and is intended to be fully backward compatible (compression defaults to None, so existing files open unchanged).

Files changed

  • src/SharpCoreDB/Services/BlockCompressor.cs (new)
  • src/SharpCoreDB/Storage/BlockCompressionMode.cs (new)
  • src/SharpCoreDB/Storage/SingleFileStorageProvider.cs (modified — compression integration + vacuum fix)
  • tests/SharpCoreDB.Tests/Storage/SingleFileCompressionTests.cs (new)

@sonarqubecloud

Copy link
Copy Markdown

@MPCoreDeveloper
MPCoreDeveloper merged commit 1398245 into MPCoreDeveloper:master Aug 30, 2026
7 checks passed
MPCoreDeveloper pushed a commit that referenced this pull request Aug 30, 2026
… document compression/encryption/metadata sizing (#344 #345 #346)
MPCoreDeveloper pushed a commit that referenced this pull request Aug 30, 2026
… block flag on rewrites + read row-cache via ReadBlockAsync

A table row-cache block that was rewritten while it already existed (auto-flush as JSON grows past the compression threshold, or grow/realloc) was stored compressed WITHOUT the per-block Compressed flag, so on reopen the raw Brotli/GZip bytes were handed to the JSON parser (JsonException '0x0B is an invalid start of a value'). WriteBlockAsync now recomputes the flag for every write, and SingleFileTable.EnsureCacheLoaded reads through ReadBlockAsync (transparent decrypt+decompress). Regression tests added for compression and compression+encryption reopen+SELECT.
MPCoreDeveloper pushed a commit that referenced this pull request Aug 30, 2026
Updates documentation and code comments that still described block
compression as 'not implemented' or 'reserved' despite being shipped
in v1.9.8 (PR #344).

Co-authored-by: scdb-dev <scdb-dev@localhost>
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