Skip to main content

storage/sqlite: store the JSON-serialised key in the database

ID
9e42abf
date
2026-11-06 22:06:41+00:00
author
Alex Chan <alexc@tailscale.com>
parent
9393430
message
storage/sqlite: store the JSON-serialised key in the database

We use slightly more storage, but it'll be much easier to debug a
misbehaving database or cache if this is there. We only select the value
column when looking up values, so this won't affect lookup speed.
changed files
1 file, 19 additions, 7 deletions

Changed files

src/funkycache/storage.py (6427 → 6794)

diff --git a/src/funkycache/storage.py b/src/funkycache/storage.py
index 7495dce..a9f009c 100644
--- a/src/funkycache/storage.py
+++ b/src/funkycache/storage.py
@@ -147,7 +147,15 @@ class SQLiteCacheStorage[V](TypedCacheStorage[V]):
         self.con.execute("PRAGMA synchronous = NORMAL;")
 
         cursor = self.con.cursor()
-        cursor.execute("CREATE TABLE IF NOT EXISTS funkycache(key PRIMARY KEY, value)")
+
+        # Each row in the table contains three columns:
+        #   - enc_key is the JSON-serialised, hashed key
+        #   - raw_key is the JSON-serialised key (for debugging)
+        #   - value is the JSON-serialised value
+        cursor.execute(
+            "CREATE TABLE IF NOT EXISTS funkycache(enc_key PRIMARY KEY, raw_key, value)"
+        )
+
         self.con.commit()
         super().__init__(data_type=data_type)
 
@@ -174,12 +182,12 @@ class SQLiteCacheStorage[V](TypedCacheStorage[V]):
         """
         self.close()
 
-    def _encode_key(self, key: Mapping[str, Any]) -> str:
+    def _encode_key(self, key: Mapping[str, Any]) -> tuple[str, str]:
         """
         Encode a mapping as a string key.
         """
         json_key = json.dumps(key, sort_keys=True, default=str)
-        return _hash(json_key)
+        return json_key, _hash(json_key)
 
     def _set_str(self, key: Mapping[str, Any], value: str) -> None:
         """
@@ -188,10 +196,12 @@ class SQLiteCacheStorage[V](TypedCacheStorage[V]):
         Throws an exception if you overwrite an existing key with
         a different value.
         """
-        enc_key = self._encode_key(key)
+        json_key, enc_key = self._encode_key(key)
 
         try:
-            self.con.execute("INSERT INTO funkycache VALUES (?,?)", (enc_key, value))
+            self.con.execute(
+                "INSERT INTO funkycache VALUES (?,?,?)", (enc_key, json_key, value)
+            )
             self.con.commit()
         except sqlite3.IntegrityError as err:
             if err.sqlite_errorname == "SQLITE_CONSTRAINT_PRIMARYKEY":
@@ -208,9 +218,11 @@ class SQLiteCacheStorage[V](TypedCacheStorage[V]):
 
         Throws a KeyError if you look up a key which isn't in the cache.
         """
-        enc_key = self._encode_key(key)
+        _, enc_key = self._encode_key(key)
 
-        res = self.con.execute("SELECT value FROM funkycache WHERE key=?", (enc_key,))
+        res = self.con.execute(
+            "SELECT value FROM funkycache WHERE enc_key=?", (enc_key,)
+        )
 
         try:
             (value,) = res.fetchone()