Skip to content

Commit fac04fc

Browse files
committed
salt.cache: allow cache.store() to set expires per key
rather than force a cache.cache() interface and a uniform expiry across the cache, we add a more common per store() expiry that can also be used instead or in addition to the original cache-wide expiry.
1 parent ead1ff0 commit fac04fc

File tree

1 file changed

+47
-20
lines changed

1 file changed

+47
-20
lines changed

salt/cache/__init__.py

Lines changed: 47 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
.. versionadded:: 2016.11.0
55
"""
66

7+
import datetime
78
import logging
89
import time
910

@@ -119,7 +120,7 @@ def cache(self, bank, key, fun, loop_fun=None, **kwargs):
119120

120121
return data
121122

122-
def store(self, bank, key, data):
123+
def store(self, bank, key, data, expires=None):
123124
"""
124125
Store data using the specified module
125126
@@ -136,12 +137,26 @@ def store(self, bank, key, data):
136137
The data which will be stored in the cache. This data should be
137138
in a format which can be serialized by msgpack.
138139
139-
:raises SaltCacheError:
140+
:param expires:
141+
how many seconds from now the data should be considered stale.
142+
143+
:raises SaltCacheError:
140144
Raises an exception if cache driver detected an error accessing data
141145
in the cache backend (auth, permissions, etc).
142146
"""
143147
fun = f"{self.driver}.store"
144-
return self.modules[fun](bank, key, data, **self._kwargs)
148+
try:
149+
return self.modules[fun](bank, key, data, expires=expires, **self._kwargs)
150+
except TypeError:
151+
# if the backing store doesnt natively support expiry, we handle it as a fallback
152+
if expires:
153+
expires_at = datetime.datetime.now().astimezone() + datetime.timedelta(
154+
seconds=expires
155+
)
156+
expires_at = int(expires_at.timestamp())
157+
return self.modules[fun](
158+
bank, key, {"data": data, "_expires": expires_at}, **self._kwargs
159+
)
145160

146161
def fetch(self, bank, key):
147162
"""
@@ -165,7 +180,17 @@ def fetch(self, bank, key):
165180
in the cache backend (auth, permissions, etc).
166181
"""
167182
fun = f"{self.driver}.fetch"
168-
return self.modules[fun](bank, key, **self._kwargs)
183+
ret = self.modules[fun](bank, key, **self._kwargs)
184+
185+
# handle fallback if necessary
186+
if isinstance(ret, dict) and set(ret.keys()) == {"data", "_expires"}:
187+
now = datetime.datetime.now().astimezone()
188+
if ret["_expires"] > now:
189+
return ret["data"]
190+
else:
191+
return {}
192+
else:
193+
return ret
169194

170195
def updated(self, bank, key):
171196
"""
@@ -309,19 +334,21 @@ def fetch(self, bank, key):
309334
now = time.time()
310335
record = self.storage.pop((bank, key), None)
311336
# Have a cached value for the key
312-
if record is not None and record[0] + self.expire >= now:
313-
if self.debug:
314-
self.hit += 1
315-
log.debug(
316-
"MemCache stats (call/hit/rate): %s/%s/%s",
317-
self.call,
318-
self.hit,
319-
float(self.hit) / self.call,
320-
)
321-
# update atime and return
322-
record[0] = now
323-
self.storage[(bank, key)] = record
324-
return record[1]
337+
if record is not None:
338+
(created_at, expires, data) = record
339+
if (created_at + (expires or self.expire)) >= now:
340+
if self.debug:
341+
self.hit += 1
342+
log.debug(
343+
"MemCache stats (call/hit/rate): %s/%s/%s",
344+
self.call,
345+
self.hit,
346+
float(self.hit) / self.call,
347+
)
348+
# update atime and return
349+
record[0] = now
350+
self.storage[(bank, key)] = record
351+
return record[1]
325352

326353
# Have no value for the key or value is expired
327354
data = super().fetch(bank, key)
@@ -333,15 +360,15 @@ def fetch(self, bank, key):
333360
self.storage[(bank, key)] = [now, data]
334361
return data
335362

336-
def store(self, bank, key, data):
363+
def store(self, bank, key, data, expires=None):
337364
self.storage.pop((bank, key), None)
338-
super().store(bank, key, data)
365+
super().store(bank, key, data, expires)
339366
if len(self.storage) >= self.max:
340367
if self.cleanup:
341368
MemCache.__cleanup(self.expire)
342369
if len(self.storage) >= self.max:
343370
self.storage.popitem(last=False)
344-
self.storage[(bank, key)] = [time.time(), data]
371+
self.storage[(bank, key)] = [time.time(), expires, data]
345372

346373
def flush(self, bank, key=None):
347374
if key is None:

0 commit comments

Comments
 (0)