pubs/pubs/datacache.py
Olivier Mangin 801e0c035f
Adds code version to cache. (Fixes #129)
Current code version is added to the cache on writting. If the cache
version does not match the code version on read, the cache is ignored
which means it resets.
2018-03-30 19:16:23 -04:00

196 lines
6.3 KiB
Python

import os
import time
from . import databroker
class CacheEntry(object):
def __init__(self, data, timestamp):
self.data = data
self.timestamp = timestamp
class CacheEntrySet(object):
def __init__(self, databroker, name):
self.databroker = databroker
self.name = name
if name == 'metacache':
self._pull_fun = databroker.pull_metadata
self._push_fun = databroker.push_metadata
self._mtime_fun = databroker.filebroker.mtime_metafile
elif name == 'bibcache':
self._pull_fun = databroker.pull_bibentry
self._push_fun = databroker.push_bibentry
self._mtime_fun = databroker.filebroker.mtime_bibfile
else:
raise ValueError
self._entries = None
self.modified = False
# does the filesystem supports subsecond stat time?
self.nsec_support = os.stat('.').st_mtime != int(os.stat('.').st_mtime)
@property
def entries(self):
if self._entries is None:
self._entries = self._try_pull_cache()
return self._entries
def flush(self, force=False):
if force or self.modified:
self.databroker.push_cache(self.name, self.entries)
self.modified = False
def pull(self, citekey):
if self._is_outdated(citekey):
# if we get here, we must update the cache.
t = time.time()
data = self._pull_fun(citekey)
self.entries[citekey] = CacheEntry(data, t)
self.modified = True
return self.entries[citekey].data
def push(self, citekey, data):
self._push_fun(citekey, data)
self.push_to_cache(citekey, data)
def push_to_cache(self, citekey, data):
"""Push to cash only."""
mtime = self._mtime_fun(citekey)
self.entries[citekey] = CacheEntry(data, mtime)
self.modified = True
def remove_from_cache(self, citekey):
"""Removes from cache only."""
if citekey in self.entries:
self.entries.pop(citekey)
self.modified = True
def _try_pull_cache(self):
try:
return self.databroker.pull_cache(self.name)
except Exception: # take no prisonners; if something is wrong, no cache.
return {}
def _is_outdated(self, citekey):
if citekey in self.entries:
mtime = self._mtime_fun(citekey)
boundary = mtime if self.nsec_support else mtime + 1
return self.entries[citekey].timestamp < boundary
else:
return True
class DataCache(object):
""" DataCache class, provides a very similar interface as DataBroker
Has two roles :
1. Provides a buffer between the commands and the hard drive.
Until a command request a hard drive ressource, it does not touch it.
2. Keeps an up-to-date, pickled version of the repository, to speed up things
when they are a lot of files. Update are also done only when required.
Changes are detected using data modification timestamps.
"""
def __init__(self, pubsdir, docsdir, create=False):
self.pubsdir = pubsdir
self.docsdir = docsdir
self._databroker = None
self._metacache = None
self._bibcache = None
if create:
self._create()
def close(self):
self.flush_cache()
@property
def databroker(self):
if self._databroker is None:
self._databroker = databroker.DataBroker(self.pubsdir, self.docsdir,
create=False)
return self._databroker
@property
def metacache(self):
if self._metacache is None:
self._metacache = CacheEntrySet(self.databroker, 'metacache')
return self._metacache
@property
def bibcache(self):
if self._bibcache is None:
self._bibcache = CacheEntrySet(self.databroker, 'bibcache')
return self._bibcache
def _create(self):
self._databroker = databroker.DataBroker(self.pubsdir, self.docsdir,
create=True)
def flush_cache(self, force=False):
"""Write cache to disk"""
self.metacache.flush(force=force)
self.bibcache.flush(force=force)
def pull_metadata(self, citekey):
return self.metacache.pull(citekey)
def pull_bibentry(self, citekey):
return self.bibcache.pull(citekey)
def push_metadata(self, citekey, metadata):
self.metacache.push(citekey, metadata)
def push_bibentry(self, citekey, bibdata):
self.bibcache.push(citekey, bibdata)
def push(self, citekey, metadata, bibdata):
self.databroker.push(citekey, metadata, bibdata)
self.metacache.push_to_cache(citekey, metadata)
self.bibcache.push_to_cache(citekey, bibdata)
def remove(self, citekey):
self.databroker.remove(citekey)
self.metacache.remove_from_cache(citekey)
self.bibcache.remove_from_cache(citekey)
def exists(self, citekey, meta_check=False):
return self.databroker.exists(citekey, meta_check=meta_check)
def citekeys(self):
return self.databroker.citekeys()
def listing(self, filestats=True):
return self.databroker.listing(filestats=filestats)
def verify(self, bibdata_raw):
return self.databroker.verify(bibdata_raw)
# docbroker
def in_docsdir(self, docpath):
return self.databroker.in_docsdir(docpath)
def real_docpath(self, docpath):
return self.databroker.real_docpath(docpath)
def add_doc(self, citekey, source_path, overwrite=False):
return self.databroker.add_doc(citekey, source_path, overwrite=overwrite)
def remove_doc(self, docpath, silent=True):
return self.databroker.remove_doc(docpath, silent=silent)
def rename_doc(self, docpath, new_citekey):
return self.databroker.rename_doc(docpath, new_citekey)
# notesbroker
def real_notepath(self, citekey, extension):
return self.databroker.real_notepath(citekey, extension)
def remove_note(self, citekey, extension, silent=True):
return self.databroker.remove_note(citekey, extension, silent=True)
def rename_note(self, old_citekey, new_citekey, extension):
return self.databroker.rename_note(old_citekey, new_citekey, extension)