From 743942d5a43381096cb75f62e92d1137ca7668ee Mon Sep 17 00:00:00 2001 From: Aymeric Ducroquetz Date: Thu, 23 Dec 2021 17:29:42 +0100 Subject: [PATCH] IAM: Replace LRU cache with one that works Previously, when we saved the same key multiple times in the cache (after expiration) and we overflowed the cache, a KeyError exception was raised. Also, if we overflowed the cache with many different keys, the overall cache size would only increase. --- requirements.txt | 1 + swift3/iam.py | 37 ++++++++++++++++++++++++++++++++++--- 2 files changed, 35 insertions(+), 3 deletions(-) diff --git a/requirements.txt b/requirements.txt index 85214f2d..d2f880a3 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,5 @@ swift>=2.13.0 +functools32 lxml requests!=2.9.0,>=2.8.1 # Apache-2.0 six>=1.9.0 diff --git a/swift3/iam.py b/swift3/iam.py index b3b195ee..be399a30 100644 --- a/swift3/iam.py +++ b/swift3/iam.py @@ -13,10 +13,12 @@ # See the License for the specific language governing permissions and # limitations under the License. +import time from fnmatch import fnmatchcase from functools import wraps +from functools32 import lru_cache -from swift.common.utils import config_auto_int_value, get_logger, LRUCache +from swift.common.utils import config_auto_int_value, get_logger from swift3.exception import IAMException from swift3.response import AccessDenied @@ -355,6 +357,30 @@ def wrapper(*args, **kwargs): return real_check_iam_access +def tlru_cache(maxtime=30, maxsize=1000): + """ + Least-recently-used cache decorator with time-based cache invalidation. + + Due to the technique used, an entry can expire before the TTL, + but never after. + """ + def tlru_cache_decorator(func): + @lru_cache(maxsize=maxsize) + def salted_func(__salt, *args, **kwargs): + return func(*args, **kwargs) + + @wraps(func) + def tlru_cache_wrapper(*args, **kwargs): + # Generate an extra argument, which will be the same for + # maxtime seconds. After maxtime seconds, the argument changes, + # and thus triggers a cache miss. + return salted_func(int(time.time() / maxtime), *args, **kwargs) + + return tlru_cache_wrapper + + return tlru_cache_decorator + + class IamMiddleware(object): """ Base class for IAM implementations. @@ -368,8 +394,13 @@ def __init__(self, app, conf): self.connection = conf.get('connection') maxsize = config_auto_int_value(conf.get('cache_size'), 1000) maxtime = config_auto_int_value(conf.get('cache_ttl'), 30) - self._load_rules_matcher = LRUCache(maxsize=maxsize, maxtime=maxtime)( - self._build_rules_matcher) + if maxsize > 0: + if maxtime <= 0: + maxtime = float('inf') + self._load_rules_matcher = tlru_cache( + maxsize=maxsize, maxtime=maxtime)(self._build_rules_matcher) + else: + self._load_rules_matcher = self._build_rules_matcher def __call__(self, env, start_response): # Put the rules callback in the request environment so middlewares