[IMP] registry: adapt LRU sizes for registries and ormcache

The registry size is now assumed to be around 10Mb, and ormcache size is
proportional to the maximum number of registries.  Statistics about ormcache
usage is shown to the log when receiving signal SIGUSR1.
This commit is contained in:
Raphael Collet 2015-03-12 15:46:47 +01:00
parent 93c341a52c
commit b156c2e27e
3 changed files with 37 additions and 11 deletions

View File

@ -305,8 +305,9 @@ class RegistryManager(object):
# cannot specify the memory limit soft on windows...
size = 42
else:
# On average, a clean registry take 25MB of memory + cache
avgsz = 30 * 1024 * 1024
# A registry takes 10MB of memory on average, so we reserve
# 10Mb (registry) + 5Mb (working memory) per registry
avgsz = 15 * 1024 * 1024
size = int(config['limit_memory_soft'] / avgsz)
cls._registries = LRU(size)
@ -319,8 +320,8 @@ class RegistryManager(object):
"""
with cls.lock():
if cls._cache is None:
# we allocate one cache entry per 32KB of memory
size = max(8192, int(config['limit_memory_soft'] / 32768))
# we allocate 8192 cache entries per registry
size = 8192 * cls.registries.count
cls._cache = LRU(size)
return cls._cache

View File

@ -38,7 +38,7 @@ import openerp
from openerp.modules.registry import RegistryManager
from openerp.release import nt_service_name
import openerp.tools.config as config
from openerp.tools.misc import stripped_sys_argv, dumpstacks
from openerp.tools import stripped_sys_argv, dumpstacks, log_ormcache_stats
_logger = logging.getLogger(__name__)
@ -296,6 +296,7 @@ class ThreadedServer(CommonServer):
signal.signal(signal.SIGCHLD, self.signal_handler)
signal.signal(signal.SIGHUP, self.signal_handler)
signal.signal(signal.SIGQUIT, dumpstacks)
signal.signal(signal.SIGUSR1, log_ormcache_stats)
elif os.name == 'nt':
import win32api
win32api.SetConsoleCtrlHandler(lambda sig: self.signal_handler(sig, None), 1)
@ -389,6 +390,7 @@ class GeventServer(CommonServer):
if os.name == 'posix':
signal.signal(signal.SIGQUIT, dumpstacks)
signal.signal(signal.SIGUSR1, log_ormcache_stats)
gevent.spawn(self.watch_parent)
self.httpd = WSGIServer((self.interface, self.port), self.app)
@ -510,6 +512,9 @@ class PreforkServer(CommonServer):
elif sig == signal.SIGQUIT:
# dump stacks on kill -3
self.dumpstacks()
elif sig == signal.SIGUSR1:
# log ormcache stats on kill -SIGUSR1
log_ormcache_stats()
elif sig == signal.SIGTTIN:
# increase number of workers
self.population += 1
@ -586,6 +591,7 @@ class PreforkServer(CommonServer):
signal.signal(signal.SIGTTIN, self.signal_handler)
signal.signal(signal.SIGTTOU, self.signal_handler)
signal.signal(signal.SIGQUIT, dumpstacks)
signal.signal(signal.SIGUSR1, log_ormcache_stats)
# listen to socket
self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

View File

@ -27,7 +27,7 @@ from inspect import getargspec
import lru
import logging
logger = logging.getLogger(__name__)
_logger = logging.getLogger(__name__)
class ormcache(object):
@ -47,9 +47,9 @@ class ormcache(object):
return lookup
def stat(self):
return "lookup-stats hit=%s miss=%s err=%s ratio=%.1f" % \
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))
(100*float(self.stat_hit))/(self.stat_miss+self.stat_hit or 1))
def lru(self, model):
return model.pool.cache, (model.pool.db_name, model._name, self.method)
@ -73,9 +73,9 @@ class ormcache(object):
""" Remove *args entry from the cache or all keys if *args is undefined """
d, key0 = self.lru(model)
if args:
logger.warn("ormcache.clear arguments are deprecated and ignored "
"(while clearing caches on (%s).%s)",
model._name, self.method.__name__)
_logger.warn("ormcache.clear arguments are deprecated and ignored "
"(while clearing caches on (%s).%s)",
model._name, self.method.__name__)
d.clear_prefix(key0)
model.pool._any_cache_cleared = True
@ -168,6 +168,25 @@ class dummy_cache(object):
pass
def log_ormcache_stats(sig=None, frame=None):
""" Log statistics of ormcache usage by database, model, and method. """
from openerp.modules.registry import RegistryManager
from collections import defaultdict
import threading
me = threading.currentThread()
entries = defaultdict(int)
for key in RegistryManager.cache.iterkeys():
entries[key[:3]] += 1
for (dbname, model_name, method), count in sorted(entries.items()):
me.dbname = dbname
model = RegistryManager.get(dbname)[model_name]
func = getattr(model, method.__name__).im_func
ormcache = func.clear_cache.im_self
_logger.info("%6d entries, %s, for %s.%s",
count, ormcache.stat(), model_name, method.__name__)
# For backward compatibility
cache = ormcache