Server: Implement caching for get_categories() and search()

This commit is contained in:
Kovid Goyal 2015-06-15 09:08:45 +05:30
parent c10ba11e30
commit c29015a3ad
2 changed files with 39 additions and 4 deletions

View File

@ -205,7 +205,7 @@ def get(ctx, rd, what, book_id, library_id):
if db is None:
raise HTTPNotFound('Library %r not found' % library_id)
with db.safe_read_lock:
if not db.has_id(book_id):
if book_id not in ctx.allowed_book_ids(rd, db):
raise HTTPNotFound('Book with id %r does not exist' % book_id)
library_id = db.server_library_id # in case library_id was None
if what == 'thumb':

View File

@ -7,6 +7,7 @@ __license__ = 'GPL v3'
__copyright__ = '2015, Kovid Goyal <kovid at kovidgoyal.net>'
import os
from collections import OrderedDict
from importlib import import_module
from threading import Lock
@ -14,6 +15,7 @@ from calibre import force_unicode
from calibre.db.cache import Cache
from calibre.db.legacy import create_backend, LibraryDatabase
from calibre.srv.routes import Router
from calibre.utils.date import utcnow
def init_library(library_path):
db = Cache(create_backend(library_path))
@ -36,6 +38,8 @@ class LibraryBroker(object):
if path is libraries[0]:
self.default_library = library_id
self.lmap[library_id] = path
self.category_caches = {lid:OrderedDict() for lid in self.lmap}
self.search_caches = {lid:OrderedDict() for lid in self.lmap}
def get(self, library_id=None):
with self.lock:
@ -57,6 +61,8 @@ class Context(object):
log = None
url_for = None
CATEGORY_CACHE_SIZE = 25
SEARCH_CACHE_SIZE = 100
def __init__(self, libraries, opts, testing=False):
self.opts = opts
@ -72,14 +78,43 @@ class Context(object):
def get_library(self, library_id=None):
return self.library_broker.get(library_id)
def restrict_to_ids(self, db, data):
def allowed_book_ids(self, data, db):
# TODO: Implement this based on data.username caching result on the
# data object
ans = data.restrict_to_ids.get(db.server_library_id)
ans = data.allowed_book_ids.get(db.server_library_id)
if ans is None:
ans = data.restrict_to_ids[db.server_library_id] = db.all_book_ids()
ans = data.allowed_book_ids[db.server_library_id] = db.all_book_ids()
return ans
def get_categories(self, data, db, restrict_to_ids=None):
if restrict_to_ids is None:
restrict_to_ids = self.allowed_book_ids(data, db)
cache = self.library_broker.category_caches[db.server_library_id]
old = cache.pop(restrict_to_ids, None)
if old is None or old[0] <= db.last_modified():
categories = db.get_categories(book_ids=restrict_to_ids)
cache[restrict_to_ids] = old = (utcnow(), categories)
if len(cache) > self.CATEGORY_CACHE_SIZE:
cache.popitem(last=False)
else:
cache[restrict_to_ids] = old
return old[1]
def search(self, data, db, query, restrict_to_ids=None):
if restrict_to_ids is None:
restrict_to_ids = self.allowed_book_ids(data, db)
cache = self.library_broker.search_caches[db.server_library_id]
key = (query, restrict_to_ids)
old = cache.pop(key, None)
if old is None or old[0] < db.clear_search_cache_count:
matches = db.search(query, book_ids=restrict_to_ids)
cache[key] = old = (db.clear_search_cache_count, matches)
if len(self._search_cache) > self.SEARCH_CACHE_SIZE:
cache.popitem(last=False)
else:
cache[key] = old
return old[1]
class Handler(object):
def __init__(self, libraries, opts, testing=False):