From 7665dc870ea69e3c79f3557df8c0a3e6d0ec2b48 Mon Sep 17 00:00:00 2001 From: Jim Huang Date: Wed, 11 Feb 2026 17:45:13 +0800 Subject: [PATCH] Replace serde_json with postcard for cache serialization Switch cache entry encoding from JSON to postcard binary format. Postcard uses varint encoding, producing more compact entries by eliminating JSON field names and syntax overhead. This increases effective cache capacity within the same max_size_mb limit. Legacy JSON entries are automatically evicted on first access (deserialization failure triggers db.remove), avoiding dead weight in the sled database. --- Cargo.lock | 97 ++++++++++++++++++++++++++++++++++++++++++++++++++++ Cargo.toml | 5 +-- src/cache.rs | 5 +-- 3 files changed, 103 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 6e49b88..034a25b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -59,6 +59,15 @@ dependencies = [ "tokio", ] +[[package]] +name = "atomic-polyfill" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8cf2bce30dfe09ef0bfaef228b9d414faaf7e563035494d7fe092dba54b300f4" +dependencies = [ + "critical-section", +] + [[package]] name = "atomic-waker" version = "1.1.2" @@ -196,6 +205,7 @@ dependencies = [ "objc2", "objc2-foundation", "objc2-natural-language", + "postcard", "regex", "reqwest", "serde", @@ -217,6 +227,15 @@ dependencies = [ "tokenizers", ] +[[package]] +name = "cobs" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fa961b519f0b462e3a3b4a34b64d119eeaca1d59af726fe450bbba07a9fc0a1" +dependencies = [ + "thiserror 2.0.17", +] + [[package]] name = "colored" version = "2.2.0" @@ -292,6 +311,12 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "critical-section" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "790eea4361631c5e7d22598ecd5723ff611904e3344ce8720784c93e3d83d40b" + [[package]] name = "crossbeam-deque" version = "0.8.6" @@ -441,6 +466,18 @@ version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" +[[package]] +name = "embedded-io" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef1a6892d9eef45c8fa6b9e0086428a2cca8491aca8f787c534a3d6d0bcb3ced" + +[[package]] +name = "embedded-io" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "edd0f118536f44f5ccd48bcb8b111bdc3de888b58c74639dfb034a357d0f206d" + [[package]] name = "encode_unicode" version = "1.0.0" @@ -693,12 +730,35 @@ dependencies = [ "tracing", ] +[[package]] +name = "hash32" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0c35f58762feb77d74ebe43bdbc3210f09be9fe6742234d573bacc26ed92b67" +dependencies = [ + "byteorder", +] + [[package]] name = "hashbrown" version = "0.16.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100" +[[package]] +name = "heapless" +version = "0.7.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cdc6457c0eb62c71aac4bc17216026d8410337c4126773b9c5daba343f17964f" +dependencies = [ + "atomic-polyfill", + "hash32", + "rustc_version", + "serde", + "spin", + "stable_deref_trait", +] + [[package]] name = "hex" version = "0.4.3" @@ -1391,6 +1451,19 @@ version = "1.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f89776e4d69bb58bc6993e99ffa1d11f228b839984854c7daeb5d37f87cbe950" +[[package]] +name = "postcard" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6764c3b5dd454e283a30e6dfe78e9b31096d9e32036b5d1eaac7a6119ccb9a24" +dependencies = [ + "cobs", + "embedded-io 0.4.0", + "embedded-io 0.6.1", + "heapless", + "serde", +] + [[package]] name = "potential_utf" version = "0.1.4" @@ -1597,6 +1670,15 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "rustc_version" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" +dependencies = [ + "semver", +] + [[package]] name = "rustix" version = "1.1.3" @@ -1693,6 +1775,12 @@ dependencies = [ "libc", ] +[[package]] +name = "semver" +version = "1.0.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d767eb0aabc880b29956c35734170f26ed551a859dbd361d140cdbeca61ab1e2" + [[package]] name = "serde" version = "1.0.228" @@ -1809,6 +1897,15 @@ dependencies = [ "windows-sys 0.60.2", ] +[[package]] +name = "spin" +version = "0.9.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" +dependencies = [ + "lock_api", +] + [[package]] name = "spm_precompiled" version = "0.1.4" diff --git a/Cargo.toml b/Cargo.toml index ae9e283..4ee5a9f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -20,8 +20,8 @@ path = "src/lib.rs" [features] default = ["cache", "tokenizer", "colored-output"] -# Translation cache with sled DB -cache = ["dep:sled", "dep:sha2", "dep:hex"] +# Translation cache with sled DB + postcard binary serialization +cache = ["dep:sled", "dep:sha2", "dep:hex", "dep:postcard"] # Claude tokenizer for precise token counting tokenizer = ["dep:claude-tokenizer"] # Colored terminal output @@ -46,6 +46,7 @@ fastrand = "2" # Lightweight RNG for retry jitter sled = { version = "0.34", optional = true } sha2 = { version = "0.10", optional = true } hex = { version = "0.4", optional = true } +postcard = { version = "1", features = ["alloc"], optional = true } # Optional: Colored output colored = { version = "2", optional = true } diff --git a/src/cache.rs b/src/cache.rs index 4f56fa2..8825a28 100644 --- a/src/cache.rs +++ b/src/cache.rs @@ -176,7 +176,7 @@ mod cache_impl { /// Get cached translation if available and not expired pub fn get(&self, key: &str) -> Option { match self.db.get(key) { - Ok(Some(bytes)) => match serde_json::from_slice::(&bytes) { + Ok(Some(bytes)) => match postcard::from_bytes::(&bytes) { Ok(entry) => { let now = Utc::now().timestamp(); let ttl_secs = self.config.ttl_days as i64 * 24 * 60 * 60; @@ -190,6 +190,7 @@ mod cache_impl { } } Err(_) => { + let _ = self.db.remove(key); CACHE_MISSES.fetch_add(1, Ordering::Relaxed); None } @@ -203,7 +204,7 @@ mod cache_impl { /// Store translation in cache pub fn put(&self, key: &str, entry: &CacheEntry) { - if let Ok(bytes) = serde_json::to_vec(entry) { + if let Ok(bytes) = postcard::to_allocvec(entry) { let entry_size = bytes.len(); let _ = self.db.insert(key, bytes);