Problem
Every cached value is wrapped in a JSON CacheEntry (src/NatsDistributedCache/NatsCache.cs), whose Data is a byte[]. System.Text.Json serializes byte[] as base64 — roughly 33% size inflation — plus a JSON serialize/parse on every Set/Get.
Investigation
The natural "store raw bytes + put expiration in NATS message headers" approach is blocked on the read path: NatsKVEntry<T> exposes no Headers (only Bucket/Created/Delta/Error/Key/Operation/Revision/Value), so header metadata can't be recovered through the standard KV read API without dropping to raw JetStream.
Proposal
Replace the JSON envelope with a custom INatsSerialize<CacheEntry> / INatsDeserialize<CacheEntry> using compact binary framing, e.g.:
[version:1][flags:1][absExpTicks:8][slidingExpTicks:8][raw payload …]
No base64, no JSON; metadata stays in the value (no dependency on header reads). A leading version byte allows future format evolution.
Acceptance criteria
- Stored size ≈ payload + small fixed header (no base64).
- Absolute + sliding expiration round-trip correctly.
version byte present and checked on read.
- Benchmark demonstrates reduced bytes and allocations vs. the current JSON path.
Notes / risk
Breaking on-wire format change. Bump the format version and document that pre-existing entries should be treated as misses (or add a one-version read shim for a transition window). This is the biggest single efficiency lever and deserves its own design pass.
Problem
Every cached value is wrapped in a JSON
CacheEntry(src/NatsDistributedCache/NatsCache.cs), whoseDatais abyte[].System.Text.Jsonserializesbyte[]as base64 — roughly 33% size inflation — plus a JSON serialize/parse on everySet/Get.Investigation
The natural "store raw bytes + put expiration in NATS message headers" approach is blocked on the read path:
NatsKVEntry<T>exposes noHeaders(onlyBucket/Created/Delta/Error/Key/Operation/Revision/Value), so header metadata can't be recovered through the standard KV read API without dropping to raw JetStream.Proposal
Replace the JSON envelope with a custom
INatsSerialize<CacheEntry>/INatsDeserialize<CacheEntry>using compact binary framing, e.g.:No base64, no JSON; metadata stays in the value (no dependency on header reads). A leading
versionbyte allows future format evolution.Acceptance criteria
versionbyte present and checked on read.Notes / risk
Breaking on-wire format change. Bump the format
versionand document that pre-existing entries should be treated as misses (or add a one-version read shim for a transition window). This is the biggest single efficiency lever and deserves its own design pass.