Improve cached_endpoint behavior with sentinel.

This commit is contained in:
voussoir 2021-06-04 17:47:38 -07:00
parent 56ab6636cc
commit 975408227b
No known key found for this signature in database
GPG key ID: 5F7554F8C26DACCB
3 changed files with 19 additions and 4 deletions

View file

@ -4,11 +4,14 @@ import time
from voussoirkit import dotdict from voussoirkit import dotdict
from voussoirkit import passwordy from voussoirkit import passwordy
from voussoirkit import sentinel
import etiquette import etiquette
from . import jsonify from . import jsonify
NOT_CACHED = sentinel.Sentinel('not cached', truthyness=False)
def cached_endpoint(max_age): def cached_endpoint(max_age):
''' '''
The cached_endpoint decorator can be used on slow endpoints that don't need The cached_endpoint decorator can be used on slow endpoints that don't need
@ -29,12 +32,19 @@ def cached_endpoint(max_age):
the previous return value (still using 200 or 304 as appropriate for the the previous return value (still using 200 or 304 as appropriate for the
client's provided etag). client's provided etag).
With max_age=0, the function will be run every time to check if the value
has changed, but if it hasn't changed then we can still send a 304 response,
saving bandwidth.
An example use case would be large-sized data dumps that don't need to be An example use case would be large-sized data dumps that don't need to be
precisely up to date every time. precisely up to date every time.
''' '''
if max_age < 0:
raise ValueError(f'max_age should be positive, not {max_age}.')
state = dotdict.DotDict({ state = dotdict.DotDict({
'max_age': max_age, 'max_age': max_age,
'stored_value': None, 'stored_value': NOT_CACHED,
'stored_etag': None, 'stored_etag': None,
'headers': {'ETag': None, 'Cache-Control': f'max-age={max_age}'}, 'headers': {'ETag': None, 'Cache-Control': f'max-age={max_age}'},
'last_run': 0, 'last_run': 0,
@ -42,7 +52,12 @@ def cached_endpoint(max_age):
def wrapper(function): def wrapper(function):
def get_value(*args, **kwargs): def get_value(*args, **kwargs):
if state.max_age and (time.time() - state.last_run) > state.max_age: can_bail = (
state.stored_value is not NOT_CACHED and
state.max_age != 0 and
(time.time() - state.last_run) < state.max_age
)
if can_bail:
return state.stored_value return state.stored_value
value = function(*args, **kwargs) value = function(*args, **kwargs)

View file

@ -195,7 +195,7 @@ def post_album_show_in_folder(album_id):
# Album listings ################################################################################### # Album listings ###################################################################################
@site.route('/all_albums.json') @site.route('/all_albums.json')
@decorators.cached_endpoint(max_age=0) @decorators.cached_endpoint(max_age=15)
def get_all_album_names(): def get_all_album_names():
all_albums = {album.display_name: album.id for album in common.P.get_albums()} all_albums = {album.display_name: album.id for album in common.P.get_albums()}
response = {'albums': all_albums} response = {'albums': all_albums}

View file

@ -99,7 +99,7 @@ def post_tag_remove_synonym(tagname):
# Tag listings ##################################################################################### # Tag listings #####################################################################################
@site.route('/all_tags.json') @site.route('/all_tags.json')
@decorators.cached_endpoint(max_age=0) @decorators.cached_endpoint(max_age=15)
def get_all_tag_names(): def get_all_tag_names():
all_tags = list(common.P.get_all_tag_names()) all_tags = list(common.P.get_all_tag_names())
all_synonyms = common.P.get_all_synonyms() all_synonyms = common.P.get_all_synonyms()