# -*- mode: python -*- # -*- coding: utf-8 -*- """Gestion des caches XXX à déplacer dans sco_cache ? Ré-écriture en cours pour ScoDoc8, utilise flask_caching et memcached ScoDoc est maintenant multiprocessus / mono-thread, avec un cache en mémoire partagé. """ import time import six.moves._thread from scodoc_manager import sco_mgr import app.scodoc.sco_utils as scu from app.scodoc.notes_log import log from app.scodoc.sco_exceptions import NoteProcessError from app.scodoc import sco_cache # # Cache global: chaque instance, repérée par sa connexion db, a un cache # qui est recréé à la demande # NOTES_CACHE_INST = {} # { db_cnx_string : CacheNotesTable instance } # XXX OBSOLETE... XXX class CacheNotesTable(object): """gestion rudimentaire de cache pour les NotesTables""" def __init__(self): log("new CacheTable (id=%s)" % id(self)) # self.lock = six.moves._thread.allocate_lock() self.owner_thread = None # thread owning this cache self.nref = 0 # Cache des NotesTables self.cache = {} # { formsemestre_id : NoteTable instance } # Cache des classeur PDF (bulletins) self.pdfcache = {} # { (formsemestre_id, version) : (filename, pdfdoc) } # Listeners: self.listeners = scu.DictDefault( defaultvalue={} ) # {formsemestre_id : {listener_id : callback }} def acquire(self): # OBSOLETE "If this thread does not own the cache, acquire the lock" if six.moves._thread.get_ident() != self.owner_thread: if self.lock.locked(): log( "acquire: ident=%s waiting for lock" % six.moves._thread.get_ident() ) # XXX debug self.lock.acquire() self.owner_thread = six.moves._thread.get_ident() if self.owner_thread is None: # bug catching log("WARNING: None thread id !") self.nref += 1 # log('nref=%d' % self.nref) def release(self): # OBSOLETE "Release the lock" cur_owner_thread = self.owner_thread # log('release: ident=%s (nref=%d)' % (thread.get_ident(), self.nref)) self.nref -= 1 if self.nref == 0: self.lock.release() self.owner_thread = None # Debug: if six.moves._thread.get_ident() != cur_owner_thread: log( "WARNING: release: ident=%s != owner=%s nref=%d" % (six.moves._thread.get_ident(), cur_owner_thread, self.nref) ) raise NoteProcessError("problem with notes cache") def get_NotesTable(self, context, formsemestre_id): # > from app.scodoc import notes_table try: self.acquire() if formsemestre_id in self.cache: # log('cache hit %s (id=%s, thread=%s)' # % (formsemestre_id, id(self), thread.get_ident())) return self.cache[formsemestre_id] else: t0 = time.time() nt = notes_table.NotesTable(context, formsemestre_id) dt = time.time() - t0 self.cache[formsemestre_id] = nt log( "caching formsemestre_id=%s (id=%s) (%gs)" % (formsemestre_id, id(self), dt) ) return nt finally: self.release() def inval_cache(self, context, formsemestre_id=None, pdfonly=False): # > "expire cache pour un semestre (ou tous si pas d'argument)" from app.scodoc import sco_parcours_dut XXX def store_bulletins_pdf(self, formsemestre_id, version, filename, pdfdoc): "cache pdf data" log( "caching PDF formsemestre_id=%s version=%s (id=%s)" % (formsemestre_id, version, id(self)) ) try: self.acquire() self.pdfcache[(formsemestre_id, version)] = (filename, pdfdoc) finally: self.release() def get_bulletins_pdf(self, formsemestre_id, version): "returns cached PDF, or None if not in the cache" try: self.acquire() if not hasattr(self, "pdfcache"): self.pdfcache = {} # fix for old zope instances... r = self.pdfcache.get((formsemestre_id, version), None) if r: log( "get_bulletins_pdf(%s): cache hit %s (id=%s, thread=%s)" % ( version, formsemestre_id, id(self), six.moves._thread.get_ident(), ) ) return r finally: self.release() def add_listener(self, callback, formsemestre_id, listener_id): """Add a "listener": a function called each time a formsemestre is modified""" self.listeners[formsemestre_id][listener_id] = callback def remove_listener(self, formsemestre_id, listener_id): # UNUSED """Remove a listener. May raise exception if does not exists. """ del self.listeners[formsemestre_id][listener_id] def _call_listeners(self, formsemestre_id): for listener_id, callback in self.listeners[formsemestre_id].items(): callback(listener_id) def _call_all_listeners(self): for formsemestre_id in self.listeners: self._call_listeners(formsemestre_id) def get_notes_cache(context): "returns CacheNotesTable instance for us" u = sco_mgr.get_db_uri() # identifie le dept de facon unique if u not in NOTES_CACHE_INST: log("getNotesCache: creating cache for %s" % u) NOTES_CACHE_INST[u] = CacheNotesTable() return NOTES_CACHE_INST[u] def inval_cache( context, formsemestre_id=None, pdfonly=False, formsemestre_id_list=None ): # > "expire cache pour un semestre (ou tous si pas d'argument)" if formsemestre_id_list: for formsemestre_id in formsemestre_id_list: get_notes_cache(context).inval_cache( context, formsemestre_id=formsemestre_id, pdfonly=pdfonly ) # Affecte aussi cache inscriptions get_formsemestre_inscription_cache(context).inval_cache(key=formsemestre_id) else: get_notes_cache(context).inval_cache( context, formsemestre_id=formsemestre_id, pdfonly=pdfonly ) # Affecte aussi cache inscriptions get_formsemestre_inscription_cache(context).inval_cache(key=formsemestre_id) # Cache inscriptions semestres def get_formsemestre_inscription_cache(context): u = sco_mgr.get_db_uri() if u in CACHE_formsemestre_inscription: return CACHE_formsemestre_inscription[u] else: log("get_formsemestre_inscription_cache: new simpleCache") CACHE_formsemestre_inscription[u] = sco_cache.simpleCache() return CACHE_formsemestre_inscription[u]