diff --git a/maloja/database/dbcache.py b/maloja/database/dbcache.py index f3e233e..41259ea 100644 --- a/maloja/database/dbcache.py +++ b/maloja/database/dbcache.py @@ -10,18 +10,24 @@ from doreah.logging import log from ..globalconf import malojaconfig -USE_CACHE = True HIGH_NUMBER = 1000000 cache = lru.LRU(HIGH_NUMBER) hits, misses = 0, 0 +if malojaconfig['USE_GLOBAL_CACHE']: + log("Using global DB Cache") +if malojaconfig['USE_REQUEST_CACHE']: + log("Using request-local DB Cache") + + @runhourly def maintenance(): - print_stats() - trim_cache() + if malojaconfig['USE_GLOBAL_CACHE']: + print_stats() + trim_cache() def print_stats(): log(f"Cache Size: {len(cache)}, System RAM Utilization: {psutil.virtual_memory().percent}%, Cache Hits: {hits}/{hits+misses}") @@ -29,7 +35,7 @@ def print_stats(): def cached_wrapper(inner_func): - if not USE_CACHE: return inner_func + if not malojaconfig['USE_GLOBAL_CACHE']: return inner_func def outer_func(*args,**kwargs): if 'dbconn' in kwargs: conn = kwargs.pop('dbconn') @@ -53,15 +59,16 @@ def cached_wrapper(inner_func): def invalidate_caches(scrobbletime): - cleared, kept = 0, 0 - for k in cache.keys(): - # VERY BIG TODO: differentiate between None as in 'unlimited timerange' and None as in 'time doesnt matter here'! - if (k[3] is None or scrobbletime >= k[3]) and (k[4] is None or scrobbletime <= k[4]): - cleared += 1 - del cache[k] - else: - kept += 1 - log(f"Invalidated {cleared} of {cleared+kept} DB cache entries") + if malojaconfig['USE_GLOBAL_CACHE']: + cleared, kept = 0, 0 + for k in cache.keys(): + # VERY BIG TODO: differentiate between None as in 'unlimited timerange' and None as in 'time doesnt matter here'! + if (k[3] is None or scrobbletime >= k[3]) and (k[4] is None or scrobbletime <= k[4]): + cleared += 1 + del cache[k] + else: + kept += 1 + log(f"Invalidated {cleared} of {cleared+kept} DB cache entries") diff --git a/maloja/database/jinjaview.py b/maloja/database/jinjaview.py index 14ec600..409a309 100644 --- a/maloja/database/jinjaview.py +++ b/maloja/database/jinjaview.py @@ -1,12 +1,19 @@ - from .. import database from . sqldb import engine +from .dbcache import serialize + +from ..globalconf import malojaconfig + # this is a wrapper object that provides a DB connection so that one jinja page # (with all its included partials) can use it for all functions # it also translates the non-unpacked calls to unpacked calls that the DB wants +# it also maintains a request-local cache since many webpages use the same stats +# several times class JinjaDBConnection: + def __init__(self): + self.cache = {} def __enter__(self): self.conn = engine.connect() return self @@ -19,6 +26,17 @@ class JinjaDBConnection: kwargs = {} for k in keys: kwargs.update(k) - return originalmethod(**kwargs,dbconn=self.conn) + if malojaconfig['USE_REQUEST_CACHE']: + cachekey = serialize((id(originalmethod),kwargs)) + if cachekey in self.cache: + return self.cache[cachekey] + else: + result = originalmethod(**kwargs,dbconn=self.conn) + self.cache[cachekey] = result + return result + else: + result = originalmethod(**kwargs,dbconn=self.conn) + return result + return packedmethod diff --git a/maloja/globalconf.py b/maloja/globalconf.py index f8dba79..e0107cb 100644 --- a/maloja/globalconf.py +++ b/maloja/globalconf.py @@ -149,7 +149,9 @@ malojaconfig = Configuration( "Technical":{ "cache_expire_positive":(tp.Integer(), "Image Cache Expiration", 60, "Days until images are refetched"), "cache_expire_negative":(tp.Integer(), "Image Cache Negative Expiration", 5, "Days until failed image fetches are reattempted"), - "db_max_memory":(tp.Integer(min=0,max=100), "RAM Percentage soft limit", 80, "RAM Usage in percent at which Maloja should no longer increase its database cache.") + "db_max_memory":(tp.Integer(min=0,max=100), "RAM Percentage soft limit", 80, "RAM Usage in percent at which Maloja should no longer increase its database cache."), + "use_request_cache":(tp.Boolean(), "Use request-local DB Cache", True), + "use_global_cache":(tp.Boolean(), "Use global DB Cache", True) }, "Fluff":{ "scrobbles_gold":(tp.Integer(), "Scrobbles for Gold", 250, "How many scrobbles a track needs to be considered 'Gold' status"),