ScoDoc/app/scodoc/sco_core.py

196 lines
6.9 KiB
Python

# -*- 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]