[IMP] ormcache: compute hit/miss/err statistics per database

In ormcache, those statistic numbers were counted per cached method for all
registries.  We introduce separate statistic counters, and create a counter per
(database, model, method).  The existing attributes that are no longer used
have been removed.
This commit is contained in:
Raphael Collet 2015-03-18 15:31:16 +01:00
parent 68969c9716
commit 5aa23f8280
1 changed files with 31 additions and 26 deletions

View File

@ -21,24 +21,36 @@
# decorator makes wrappers that have the same API as their wrapped function; # decorator makes wrappers that have the same API as their wrapped function;
# this is important for the openerp.api.guess() that relies on signatures # this is important for the openerp.api.guess() that relies on signatures
from collections import defaultdict
from decorator import decorator from decorator import decorator
from inspect import getargspec from inspect import getargspec
import lru
import logging import logging
_logger = logging.getLogger(__name__) _logger = logging.getLogger(__name__)
class ormcache_counter(object):
""" Statistic counters for cache entries. """
__slots__ = ['hit', 'miss', 'err']
def __init__(self):
self.hit = 0
self.miss = 0
self.err = 0
@property
def ratio(self):
return 100.0 * self.hit / (self.hit + self.miss or 1)
# statistic counters dictionary, maps (dbname, modelname, method) to counter
STAT = defaultdict(ormcache_counter)
class ormcache(object): class ormcache(object):
""" LRU cache decorator for orm methods. """ """ LRU cache decorator for orm methods. """
def __init__(self, skiparg=2, size=8192, multi=None, timeout=None): def __init__(self, skiparg=2, size=8192, multi=None, timeout=None):
self.skiparg = skiparg self.skiparg = skiparg
self.size = size
self.stat_miss = 0
self.stat_hit = 0
self.stat_err = 0
def __call__(self, method): def __call__(self, method):
self.method = method self.method = method
@ -46,11 +58,6 @@ class ormcache(object):
lookup.clear_cache = self.clear lookup.clear_cache = self.clear
return lookup return lookup
def stat(self):
return "lookup-stats %6d hit %6d miss %6d err %4.1f ratio" % \
(self.stat_hit, self.stat_miss, self.stat_err,
(100*float(self.stat_hit))/(self.stat_miss+self.stat_hit or 1))
def lru(self, model): def lru(self, model):
return model.pool.cache, (model.pool.db_name, model._name, self.method) return model.pool.cache, (model.pool.db_name, model._name, self.method)
@ -59,14 +66,14 @@ class ormcache(object):
key = key0 + args[self.skiparg:] key = key0 + args[self.skiparg:]
try: try:
r = d[key] r = d[key]
self.stat_hit += 1 STAT[key0].hit += 1
return r return r
except KeyError: except KeyError:
self.stat_miss += 1 STAT[key0].miss += 1
value = d[key] = self.method(*args, **kwargs) value = d[key] = self.method(*args, **kwargs)
return value return value
except TypeError: except TypeError:
self.stat_err += 1 STAT[key0].err += 1
return self.method(*args, **kwargs) return self.method(*args, **kwargs)
def clear(self, model, *args): def clear(self, model, *args):
@ -107,14 +114,14 @@ class ormcache_context(ormcache):
key = key0 + args[self.skiparg:self.context_pos] + tuple(ckey) key = key0 + args[self.skiparg:self.context_pos] + tuple(ckey)
try: try:
r = d[key] r = d[key]
self.stat_hit += 1 STAT[key0].hit += 1
return r return r
except KeyError: except KeyError:
self.stat_miss += 1 STAT[key0].miss += 1
value = d[key] = self.method(*args, **kwargs) value = d[key] = self.method(*args, **kwargs)
return value return value
except TypeError: except TypeError:
self.stat_err += 1 STAT[key0].err += 1
return self.method(*args, **kwargs) return self.method(*args, **kwargs)
@ -136,9 +143,9 @@ class ormcache_multi(ormcache):
key = base_key + (i,) key = base_key + (i,)
try: try:
result[i] = d[key] result[i] = d[key]
self.stat_hit += 1 STAT[key0].hit += 1
except Exception: except Exception:
self.stat_miss += 1 STAT[key0].miss += 1
missed.append(i) missed.append(i)
if missed: if missed:
@ -171,20 +178,18 @@ class dummy_cache(object):
def log_ormcache_stats(sig=None, frame=None): def log_ormcache_stats(sig=None, frame=None):
""" Log statistics of ormcache usage by database, model, and method. """ """ Log statistics of ormcache usage by database, model, and method. """
from openerp.modules.registry import RegistryManager from openerp.modules.registry import RegistryManager
from collections import defaultdict
import threading import threading
me = threading.currentThread() me = threading.currentThread()
entries = defaultdict(int) entries = defaultdict(int)
for key in RegistryManager.cache.iterkeys(): for key in RegistryManager.cache.iterkeys():
entries[key[:3]] += 1 entries[key[:3]] += 1
for (dbname, model_name, method), count in sorted(entries.items()): for key, count in sorted(entries.items()):
dbname, model_name, method = key
me.dbname = dbname me.dbname = dbname
model = RegistryManager.get(dbname)[model_name] stat = STAT[key]
func = getattr(model, method.__name__).im_func _logger.info("%6d entries, %6d hit, %6d miss, %6d err, %4.1f%% ratio, for %s.%s",
ormcache = func.clear_cache.im_self count, stat.hit, stat.miss, stat.err, stat.ratio, model_name, method.__name__)
_logger.info("%6d entries, %s, for %s.%s",
count, ormcache.stat(), model_name, method.__name__)
# For backward compatibility # For backward compatibility