Cache the rendered Tag Browser rather than the result of get_categories() in the server

get_categories() now has its own independent cache which is used by the
old AJAX API kept around for backwards compatibility.
This commit is contained in:
Kovid Goyal 2015-11-23 17:09:41 +05:30
parent 87b4e0906b
commit ebddee8f59
2 changed files with 38 additions and 4 deletions

View File

@ -46,6 +46,7 @@ class LibraryBroker(object):
self.lmap[library_id] = path self.lmap[library_id] = path
self.category_caches = {lid:OrderedDict() for lid in self.lmap} self.category_caches = {lid:OrderedDict() for lid in self.lmap}
self.search_caches = {lid:OrderedDict() for lid in self.lmap} self.search_caches = {lid:OrderedDict() for lid in self.lmap}
self.tag_browser_caches = {lid:OrderedDict() for lid in self.lmap}
def get(self, library_id=None): def get(self, library_id=None):
with self.lock: with self.lock:
@ -127,6 +128,22 @@ class Context(object):
cache[key] = old cache[key] = old
return old[1] return old[1]
def get_tag_browser(self, data, db, opts, render, restrict_to_ids=None):
if restrict_to_ids is None:
restrict_to_ids = self.allowed_book_ids(data, db)
key = (restrict_to_ids, opts)
with self.lock:
cache = self.library_broker.category_caches[db.server_library_id]
old = cache.pop(key, None)
if old is None or old[0] <= db.last_modified():
categories = db.get_categories(book_ids=restrict_to_ids, sort=opts.sort_by, first_letter_sort=opts.collapse_model == 'first letter')
cache[key] = old = (utcnow(), render(categories))
if len(cache) > self.CATEGORY_CACHE_SIZE:
cache.popitem(last=False)
else:
cache[key] = old
return old[1]
def search(self, data, db, query, restrict_to_ids=None): def search(self, data, db, query, restrict_to_ids=None):
if restrict_to_ids is None: if restrict_to_ids is None:
restrict_to_ids = self.allowed_book_ids(data, db) restrict_to_ids = self.allowed_book_ids(data, db)

View File

@ -7,6 +7,7 @@ from __future__ import (unicode_literals, division, absolute_import,
from copy import copy from copy import copy
from collections import namedtuple from collections import namedtuple
from datetime import datetime, time from datetime import datetime, time
from functools import partial
from calibre.db.categories import Tag from calibre.db.categories import Tag
from calibre.utils.date import isoformat, UNDEFINED_DATE, local_tz from calibre.utils.date import isoformat, UNDEFINED_DATE, local_tz
@ -92,6 +93,21 @@ def category_item_as_json(x, clear_rating=False):
CategoriesSettings = namedtuple( CategoriesSettings = namedtuple(
'CategoriesSettings', 'dont_collapse collapse_model collapse_at sort_by template using_hierarchy grouped_search_terms hidden_categories') 'CategoriesSettings', 'dont_collapse collapse_model collapse_at sort_by template using_hierarchy grouped_search_terms hidden_categories')
class GroupedSearchTerms(object):
__slots__ = ('keys', 'vals', 'hash')
def __init__(self, src):
self.keys = frozenset(src)
self.vals = frozenset(tuple(v) for v in src.itervalues())
self.hash = hash((self.keys, self.vals))
def __contains__(self, val):
return val in self.keys
def __hash__(self):
return self.hash
def categories_settings(query, db): def categories_settings(query, db):
dont_collapse = frozenset(query.get('dont_collapse', '').split(',')) dont_collapse = frozenset(query.get('dont_collapse', '').split(','))
partition_method = query.get('partition_method', 'first letter') partition_method = query.get('partition_method', 'first letter')
@ -111,9 +127,11 @@ def categories_settings(query, db):
collapse_model = 'partition' collapse_model = 'partition'
template = tweaks['categories_collapsed_%s_template' % sort_by] template = tweaks['categories_collapsed_%s_template' % sort_by]
using_hierarchy = frozenset(db.pref('categories_using_hierarchy', [])) using_hierarchy = frozenset(db.pref('categories_using_hierarchy', []))
hidden_categories = db.pref('tag_browser_hidden_categories', set()) hidden_categories = frozenset(db.pref('tag_browser_hidden_categories', set()))
return CategoriesSettings( return CategoriesSettings(
dont_collapse, collapse_model, collapse_at, sort_by, template, using_hierarchy, db.pref('grouped_search_terms', {}), hidden_categories) dont_collapse, collapse_model, collapse_at, sort_by, template,
using_hierarchy, GroupedSearchTerms(db.pref('grouped_search_terms', {})),
hidden_categories)
def create_toplevel_tree(category_data, items, field_metadata, opts): def create_toplevel_tree(category_data, items, field_metadata, opts):
# Create the basic tree, containing all top level categories , user # Create the basic tree, containing all top level categories , user
@ -446,8 +464,7 @@ def render_categories(field_metadata, opts, category_data):
def categories_as_json(ctx, rd, db): def categories_as_json(ctx, rd, db):
opts = categories_settings(rd.query, db) opts = categories_settings(rd.query, db)
category_data = ctx.get_categories(rd, db, sort=opts.sort_by, first_letter_sort=opts.collapse_model == 'first letter') return ctx.get_tag_browser(rd, db, opts, partial(render_categories, db.field_metadata, opts))
render_categories(db.field_metadata, opts, category_data)
# Test tag browser {{{ # Test tag browser {{{