server/tag-categories: fix default categories
- Don't cache default category in its entirety - cache only its name - Purge cache on category name changes and default category changes - Lock records for updates where applicable
This commit is contained in:
parent
06ab98fa70
commit
ef0f74297f
5 changed files with 73 additions and 31 deletions
|
@ -40,7 +40,8 @@ def get_tag_category(ctx, params):
|
|||
|
||||
@routes.put('/tag-category/(?P<category_name>[^/]+)/?')
|
||||
def update_tag_category(ctx, params):
|
||||
category = tag_categories.get_category_by_name(params['category_name'])
|
||||
category = tag_categories.get_category_by_name(
|
||||
params['category_name'], lock=True)
|
||||
versions.verify_version(category, ctx)
|
||||
versions.bump_version(category)
|
||||
if ctx.has_param('name'):
|
||||
|
@ -60,7 +61,8 @@ def update_tag_category(ctx, params):
|
|||
|
||||
@routes.delete('/tag-category/(?P<category_name>[^/]+)/?')
|
||||
def delete_tag_category(ctx, params):
|
||||
category = tag_categories.get_category_by_name(params['category_name'])
|
||||
category = tag_categories.get_category_by_name(
|
||||
params['category_name'], lock=True)
|
||||
versions.verify_version(category, ctx)
|
||||
auth.verify_privilege(ctx.user, 'tag_categories:delete')
|
||||
tag_categories.delete_category(category)
|
||||
|
@ -73,8 +75,10 @@ def delete_tag_category(ctx, params):
|
|||
@routes.put('/tag-category/(?P<category_name>[^/]+)/default/?')
|
||||
def set_tag_category_as_default(ctx, params):
|
||||
auth.verify_privilege(ctx.user, 'tag_categories:set_default')
|
||||
category = tag_categories.get_category_by_name(params['category_name'])
|
||||
category = tag_categories.get_category_by_name(
|
||||
params['category_name'], lock=True)
|
||||
tag_categories.set_default_category(category)
|
||||
ctx.session.flush()
|
||||
snapshots.modify(category, ctx.user)
|
||||
ctx.session.commit()
|
||||
tags.export_to_json()
|
||||
|
|
|
@ -55,5 +55,10 @@ def get(key):
|
|||
return _CACHE.hash[key].value
|
||||
|
||||
|
||||
def remove(key):
|
||||
if has(key):
|
||||
del _CACHE.hash[key]
|
||||
|
||||
|
||||
def put(key, value):
|
||||
_CACHE.insert_item(LruCacheItem(key, value))
|
||||
|
|
|
@ -4,6 +4,9 @@ from szurubooru import config, db, errors
|
|||
from szurubooru.func import util, cache
|
||||
|
||||
|
||||
DEFAULT_CATEGORY_NAME_CACHE_KEY = 'default-tag-category'
|
||||
|
||||
|
||||
class TagCategoryNotFoundError(errors.NotFoundError):
|
||||
pass
|
||||
|
||||
|
@ -69,6 +72,7 @@ def update_category_name(category, name):
|
|||
raise InvalidTagCategoryNameError('Name is too long.')
|
||||
_verify_name_validity(name)
|
||||
category.name = name
|
||||
cache.remove(DEFAULT_CATEGORY_NAME_CACHE_KEY)
|
||||
|
||||
|
||||
def update_category_color(category, color):
|
||||
|
@ -82,15 +86,17 @@ def update_category_color(category, color):
|
|||
category.color = color
|
||||
|
||||
|
||||
def try_get_category_by_name(name):
|
||||
return db.session \
|
||||
def try_get_category_by_name(name, lock=False):
|
||||
query = db.session \
|
||||
.query(db.TagCategory) \
|
||||
.filter(sqlalchemy.func.lower(db.TagCategory.name) == name.lower()) \
|
||||
.one_or_none()
|
||||
.filter(sqlalchemy.func.lower(db.TagCategory.name) == name.lower())
|
||||
if lock:
|
||||
query = query.with_lockmode('update')
|
||||
return query.one_or_none()
|
||||
|
||||
|
||||
def get_category_by_name(name):
|
||||
category = try_get_category_by_name(name)
|
||||
def get_category_by_name(name, lock=False):
|
||||
category = try_get_category_by_name(name, lock)
|
||||
if not category:
|
||||
raise TagCategoryNotFoundError('Tag category %r not found.' % name)
|
||||
return category
|
||||
|
@ -104,38 +110,50 @@ def get_all_categories():
|
|||
return db.session.query(db.TagCategory).all()
|
||||
|
||||
|
||||
def try_get_default_category():
|
||||
key = 'default-tag-category'
|
||||
if cache.has(key):
|
||||
return cache.get(key)
|
||||
category = db.session \
|
||||
def try_get_default_category(lock=False):
|
||||
query = db.session \
|
||||
.query(db.TagCategory) \
|
||||
.filter(db.TagCategory.default) \
|
||||
.first()
|
||||
.filter(db.TagCategory.default)
|
||||
if lock:
|
||||
query = query.with_lockmode('update')
|
||||
category = query.first()
|
||||
# if for some reason (e.g. as a result of migration) there's no default
|
||||
# category, get the first record available.
|
||||
if not category:
|
||||
category = db.session \
|
||||
query = db.session \
|
||||
.query(db.TagCategory) \
|
||||
.order_by(db.TagCategory.tag_category_id.asc()) \
|
||||
.first()
|
||||
cache.put(key, category)
|
||||
.order_by(db.TagCategory.tag_category_id.asc())
|
||||
if lock:
|
||||
query = query.with_lockmode('update')
|
||||
category = query.first()
|
||||
return category
|
||||
|
||||
|
||||
def get_default_category():
|
||||
category = try_get_default_category()
|
||||
def get_default_category(lock=False):
|
||||
category = try_get_default_category(lock)
|
||||
if not category:
|
||||
raise TagCategoryNotFoundError('No tag category created yet.')
|
||||
return category
|
||||
|
||||
|
||||
def get_default_category_name():
|
||||
if cache.has(DEFAULT_CATEGORY_NAME_CACHE_KEY):
|
||||
return cache.get(DEFAULT_CATEGORY_NAME_CACHE_KEY)
|
||||
default_category = try_get_default_category()
|
||||
default_category_name = default_category.name if default_category else None
|
||||
cache.put(DEFAULT_CATEGORY_NAME_CACHE_KEY, default_category_name)
|
||||
return default_category_name
|
||||
|
||||
|
||||
def set_default_category(category):
|
||||
assert category
|
||||
old_category = try_get_default_category()
|
||||
old_category = try_get_default_category(lock=True)
|
||||
if old_category:
|
||||
db.session.refresh(old_category)
|
||||
old_category.default = False
|
||||
db.session.refresh(category)
|
||||
category.default = True
|
||||
cache.remove(DEFAULT_CATEGORY_NAME_CACHE_KEY)
|
||||
|
||||
|
||||
def delete_category(category):
|
||||
|
|
|
@ -58,8 +58,7 @@ def _check_name_intersection(names1, names2, case_sensitive):
|
|||
|
||||
|
||||
def sort_tags(tags):
|
||||
default_category = tag_categories.try_get_default_category()
|
||||
default_category_name = default_category.name if default_category else None
|
||||
default_category_name = tag_categories.get_default_category_name()
|
||||
return sorted(
|
||||
tags,
|
||||
key=lambda tag: (
|
||||
|
@ -170,7 +169,7 @@ def get_or_create_tags_by_names(names):
|
|||
names = util.icase_unique(names)
|
||||
existing_tags = get_tags_by_names(names)
|
||||
new_tags = []
|
||||
tag_category_name = tag_categories.get_default_category().name
|
||||
tag_category_name = tag_categories.get_default_category_name()
|
||||
for name in names:
|
||||
found = False
|
||||
for existing_tag in existing_tags:
|
||||
|
|
|
@ -181,16 +181,32 @@ def test_try_get_default_category_when_default(tag_category_factory):
|
|||
assert actual_default_category != category1
|
||||
|
||||
|
||||
def test_try_get_default_category_from_cache(tag_category_factory):
|
||||
def test_get_default_category_name(tag_category_factory):
|
||||
category1 = tag_category_factory()
|
||||
category2 = tag_category_factory(default=True)
|
||||
db.session.add_all([category1, category2])
|
||||
db.session.flush()
|
||||
assert tag_categories.get_default_category_name() == category2.name
|
||||
category2.default = False
|
||||
db.session.flush()
|
||||
cache.purge()
|
||||
assert tag_categories.get_default_category_name() == category1.name
|
||||
db.session.query(db.TagCategory).delete()
|
||||
cache.purge()
|
||||
assert tag_categories.get_default_category_name() is None
|
||||
|
||||
|
||||
def test_get_default_category_name_caching(tag_category_factory):
|
||||
category1 = tag_category_factory()
|
||||
category2 = tag_category_factory()
|
||||
db.session.add_all([category1, category2])
|
||||
db.session.flush()
|
||||
tag_categories.try_get_default_category()
|
||||
db.session.query(db.TagCategory).delete()
|
||||
assert tag_categories.try_get_default_category() == category1
|
||||
tag_categories.get_default_category_name()
|
||||
db.session.delete(category1)
|
||||
db.session.flush()
|
||||
assert tag_categories.get_default_category_name() == category1.name
|
||||
cache.purge()
|
||||
assert tag_categories.try_get_default_category() is None
|
||||
assert tag_categories.get_default_category_name() == category2.name
|
||||
|
||||
|
||||
def test_get_default_category():
|
||||
|
|
Loading…
Reference in a new issue