From 3ea49739d39d572866885a4889909078f48df840 Mon Sep 17 00:00:00 2001 From: mdipierro Date: Sun, 22 Mar 2015 13:40:05 -0500 Subject: [PATCH] expire cache when ram excees 90% --- gluon/cache.py | 57 ++++++++++++++++++++++++++++++++++---------------- 1 file changed, 39 insertions(+), 18 deletions(-) diff --git a/gluon/cache.py b/gluon/cache.py index 60b31351..eaa97d6f 100644 --- a/gluon/cache.py +++ b/gluon/cache.py @@ -22,6 +22,7 @@ caching will be provided by the GAE memcache import time import thread import os +import gc import sys import logging import re @@ -40,6 +41,30 @@ try: except: import pickle +try: + from collections import OrderedDict + import psutil + CAN_DO_CLEANUP = True +except ImportError: + CAN_DO_CLEANUP = False + +def remove_oldest_entries(storage, percentage=90): + # compute current memory usage (%) + old_mem = psutil.virtual_memory().percent + # if we have data in storage and utilization exceeds 90% + while storage and old_mem > percentage: + # removed oldest entry + storage.popitem(last=False) + # garbage collect + gc.collect(2) + # comute used memory again + new_mem = psutil.virtual_memory().percent + # if the used memory did not decrease stop + if new_mem >= old_mem: break + # net new measurement for memory usage and loop + old_mem = new_mem + + logger = logging.getLogger("web2py.cache") __all__ = ['Cache', 'lazy_cache'] @@ -47,8 +72,6 @@ __all__ = ['Cache', 'lazy_cache'] DEFAULT_TIME_EXPIRE = 300 - - class CacheAbstract(object): """ Abstract class for cache implementations. @@ -145,28 +168,25 @@ class CacheInRam(CacheAbstract): locker = thread.allocate_lock() meta_storage = {} + stats = {} def __init__(self, request=None): self.initialized = False self.request = request - self.storage = {} - + self.storage = OrderedDict() if CAN_DO_CLEANUP else {} + self.app = request.application if request else '' def initialize(self): if self.initialized: return else: self.initialized = True self.locker.acquire() - request = self.request - if request: - app = request.application + if not self.app in self.meta_storage: + self.storage = self.meta_storage[self.app] = \ + OrderedDict() if CAN_DO_CLEANUP else {} + self.stats[self.app] = {'hit_total': 0, 'misses': 0} else: - app = '' - if not app in self.meta_storage: - self.storage = self.meta_storage[app] = { - CacheAbstract.cache_stats_name: {'hit_total': 0, 'misses': 0}} - else: - self.storage = self.meta_storage[app] + self.storage = self.meta_storage[self.app] self.locker.release() def clear(self, regex=None): @@ -178,9 +198,8 @@ class CacheInRam(CacheAbstract): else: self._clear(storage, regex) - if not CacheAbstract.cache_stats_name in storage.keys(): - storage[CacheAbstract.cache_stats_name] = { - 'hit_total': 0, 'misses': 0} + if not self.app in self.stats: + self.stats[self.app] = {'hit_total': 0, 'misses': 0} self.locker.release() @@ -211,7 +230,7 @@ class CacheInRam(CacheAbstract): del self.storage[key] if destroyer: destroyer(item[1]) - self.storage[CacheAbstract.cache_stats_name]['hit_total'] += 1 + self.stats[self.app]['hit_total'] += 1 self.locker.release() if f is None: @@ -224,7 +243,9 @@ class CacheInRam(CacheAbstract): self.locker.acquire() self.storage[key] = (now, value) - self.storage[CacheAbstract.cache_stats_name]['misses'] += 1 + self.stats[self.app]['misses'] += 1 + if CAN_DO_CLEANUP: + remove_oldest_entries(self.storage) self.locker.release() return value