From db4b905769c55faff5c996a004ae8e3f80ccccc7 Mon Sep 17 00:00:00 2001 From: August Vi Date: Mon, 2 Nov 2026 06:55:24 -0800 Subject: [PATCH 1/2] Make Cache.get select store_time and build the metadata tuple only when asked, restoring 5.6.3 hit speed. --- CHANGELOG.md | 1 + diskcache_community/core.py | 87 ++++++++++++++++++++++++++----------- 2 files changed, 62 insertions(+), 26 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 61ba1f8..eb5faa5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,7 @@ diskcache-community forked from DiskCache at upstream commit ebfa37c (3 March 20 ### Fixes +- `Cache.get`, and so `FanoutCache.get` and `DjangoCache.get`, is as fast on a hit as in 5.6.3 again. Since 5.7.0 every call selected the `store_time` column and built the metadata tuple even when `store_time` was not asked for, which made a hit about 5% slower. Both now happen only when metadata is requested, and results are unchanged (PR #35). - A float NaN stored as a value is kept, on every disk. It was written to SQLite, which stores NaN as NULL, so it read back as `None`. It is now stored as a pickle, which 5.6.3 reads too (PR #34). - `memoize_stampede` works with `SafeDisk`: the key of its early recomputation marker held the internal `ENOVAL` constant, which `SafeDisk` refuses, so a call raised `TypeError` once a cached value neared expiry (PR #32). diff --git a/diskcache_community/core.py b/diskcache_community/core.py index e1b7944..6887497 100644 --- a/diskcache_community/core.py +++ b/diskcache_community/core.py @@ -2617,6 +2617,9 @@ def get( :raises Timeout: if database timeout occurs """ + # Kept in one function: a helper call costs a measurable share of a + # cache hit. + # pylint: disable=too-many-branches,too-many-statements db_key, raw = self._disk.put(key) update_column = EVICTION_POLICY[self.eviction_policy]['get'] statistics = self.statistics @@ -2626,13 +2629,24 @@ def get( # the path that does not write. update_column = None statistics = False - select = ( - 'SELECT rowid, expire_time, tag, mode, filename, value, store_time' - ' FROM Cache WHERE key = ? AND raw = ?' - ' AND (expire_time IS NULL OR expire_time > ?)' - ) + # Select store_time only when asked: each extra column costs a + # measurable share of a cache hit. + if store_time: + select = ( + 'SELECT rowid, expire_time, tag, mode, filename, value,' + ' store_time FROM Cache WHERE key = ? AND raw = ?' + ' AND (expire_time IS NULL OR expire_time > ?)' + ) + else: + select = ( + 'SELECT rowid, expire_time, tag, mode, filename, value' + ' FROM Cache WHERE key = ? AND raw = ?' + ' AND (expire_time IS NULL OR expire_time > ?)' + ) - default = _meta_result(default, expire_time, tag, store_time) + meta_wanted = expire_time or tag or store_time + if meta_wanted: + default = _meta_result(default, expire_time, tag, store_time) if not statistics and update_column is None: # Fast path, no transaction necessary. @@ -2642,17 +2656,23 @@ def get( if not rows: return default - ( + if store_time: ( - rowid, - db_expire_time, - db_tag, - mode, - filename, - db_value, - db_store_time, - ), - ) = rows + ( + rowid, + db_expire_time, + db_tag, + mode, + filename, + db_value, + db_store_time, + ), + ) = rows + else: + ( + (rowid, db_expire_time, db_tag, mode, filename, db_value), + ) = rows + db_store_time = None try: value = self._disk.fetch(mode, filename, db_value, read) @@ -2681,17 +2701,30 @@ def get( sql(cache_miss) return default - ( + if store_time: ( - rowid, - db_expire_time, - db_tag, - mode, - filename, - db_value, - db_store_time, - ), - ) = rows + ( + rowid, + db_expire_time, + db_tag, + mode, + filename, + db_value, + db_store_time, + ), + ) = rows + else: + ( + ( + rowid, + db_expire_time, + db_tag, + mode, + filename, + db_value, + ), + ) = rows + db_store_time = None try: value = self._disk.fetch(mode, filename, db_value, read) @@ -2719,6 +2752,8 @@ def get( if update_column is not None: sql(update % update_column.format(now=now), (rowid,)) + if not meta_wanted: + return value meta = (db_expire_time, db_tag, db_store_time) return _meta_result(value, expire_time, tag, store_time, meta) From 3b3f22f8d96c9cb666c80f2c89cde19dcc4ff2e9 Mon Sep 17 00:00:00 2001 From: August Vi Date: Mon, 2 Nov 2026 10:46:09 -0800 Subject: [PATCH 2/2] Shorten the changelog entry for the Cache.get speed fix. --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index eb5faa5..baf5bd7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,7 +13,7 @@ diskcache-community forked from DiskCache at upstream commit ebfa37c (3 March 20 ### Fixes -- `Cache.get`, and so `FanoutCache.get` and `DjangoCache.get`, is as fast on a hit as in 5.6.3 again. Since 5.7.0 every call selected the `store_time` column and built the metadata tuple even when `store_time` was not asked for, which made a hit about 5% slower. Both now happen only when metadata is requested, and results are unchanged (PR #35). +- `Cache.get` is as fast on a hit as in 5.6.3 again; since 5.7.0 it was about 5% slower (PR #35). - A float NaN stored as a value is kept, on every disk. It was written to SQLite, which stores NaN as NULL, so it read back as `None`. It is now stored as a pickle, which 5.6.3 reads too (PR #34). - `memoize_stampede` works with `SafeDisk`: the key of its early recomputation marker held the internal `ENOVAL` constant, which `SafeDisk` refuses, so a call raised `TypeError` once a cached value neared expiry (PR #32).