import time from deprecated.sphinx import deprecated from limits.storage.memcached import MemcachedStorage @deprecated( reason="""The implementation has not been tested at all in version 2.0 (and for many years) and will be removed in limits 2.5""", version="2.0", action="once", ) class GAEMemcachedStorage(MemcachedStorage): # noqa """ rate limit storage with GAE memcache as backend """ MAX_CAS_RETRIES = 10 STORAGE_SCHEME = ["gaememcached"] def __init__(self, uri: str, **options): options["library"] = "google.appengine.api.memcache" super().__init__(uri, **options) def incr( self, key: str, expiry: int, elastic_expiry: bool = False, amount: int = 1 ): """ increments the counter for a given rate limit key :param key: the key to increment :param expiry: amount in seconds for the key to expire in :param elastic_expiry: whether to keep extending the rate limit window every hit. :param amount: the number to increment by """ if not self.call_memcached_func(self.storage.add, key, amount, expiry): if elastic_expiry: # CAS id is set as state on the client object in GAE memcache value = self.storage.gets(key) retry = 0 while ( not self.call_memcached_func( self.storage.cas, key, int(value or 0) + amount, expiry ) and retry < self.MAX_CAS_RETRIES ): value = self.storage.gets(key) retry += 1 self.call_memcached_func( self.storage.set, key + "/expires", expiry + time.time(), expiry ) return int(value or 0) + amount else: return self.storage.incr(key, amount) self.call_memcached_func( self.storage.set, key + "/expires", expiry + time.time(), expiry ) return 1 def check(self) -> bool: """ check if storage is healthy """ try: self.call_memcached_func(self.storage.get_stats) return True except: # noqa return False