From 975408227bc9e3d0f85cb11758b7049ec1eb46ed Mon Sep 17 00:00:00 2001 From: Ethan Dalool Date: Fri, 4 Jun 2021 17:47:38 -0700 Subject: [PATCH] Improve cached_endpoint behavior with sentinel. --- .../etiquette_flask/backend/decorators.py | 19 +++++++++++++++++-- .../backend/endpoints/album_endpoints.py | 2 +- .../backend/endpoints/tag_endpoints.py | 2 +- 3 files changed, 19 insertions(+), 4 deletions(-) diff --git a/frontends/etiquette_flask/backend/decorators.py b/frontends/etiquette_flask/backend/decorators.py index e25959a..a3963f6 100644 --- a/frontends/etiquette_flask/backend/decorators.py +++ b/frontends/etiquette_flask/backend/decorators.py @@ -4,11 +4,14 @@ import time from voussoirkit import dotdict from voussoirkit import passwordy +from voussoirkit import sentinel import etiquette from . import jsonify +NOT_CACHED = sentinel.Sentinel('not cached', truthyness=False) + def cached_endpoint(max_age): ''' 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 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 precisely up to date every time. ''' + if max_age < 0: + raise ValueError(f'max_age should be positive, not {max_age}.') + state = dotdict.DotDict({ 'max_age': max_age, - 'stored_value': None, + 'stored_value': NOT_CACHED, 'stored_etag': None, 'headers': {'ETag': None, 'Cache-Control': f'max-age={max_age}'}, 'last_run': 0, @@ -42,7 +52,12 @@ def cached_endpoint(max_age): def wrapper(function): 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 value = function(*args, **kwargs) diff --git a/frontends/etiquette_flask/backend/endpoints/album_endpoints.py b/frontends/etiquette_flask/backend/endpoints/album_endpoints.py index d8e54e4..f6d636b 100644 --- a/frontends/etiquette_flask/backend/endpoints/album_endpoints.py +++ b/frontends/etiquette_flask/backend/endpoints/album_endpoints.py @@ -195,7 +195,7 @@ def post_album_show_in_folder(album_id): # Album listings ################################################################################### @site.route('/all_albums.json') -@decorators.cached_endpoint(max_age=0) +@decorators.cached_endpoint(max_age=15) def get_all_album_names(): all_albums = {album.display_name: album.id for album in common.P.get_albums()} response = {'albums': all_albums} diff --git a/frontends/etiquette_flask/backend/endpoints/tag_endpoints.py b/frontends/etiquette_flask/backend/endpoints/tag_endpoints.py index b2d5bfb..988a281 100644 --- a/frontends/etiquette_flask/backend/endpoints/tag_endpoints.py +++ b/frontends/etiquette_flask/backend/endpoints/tag_endpoints.py @@ -99,7 +99,7 @@ def post_tag_remove_synonym(tagname): # Tag listings ##################################################################################### @site.route('/all_tags.json') -@decorators.cached_endpoint(max_age=0) +@decorators.cached_endpoint(max_age=15) def get_all_tag_names(): all_tags = list(common.P.get_all_tag_names()) all_synonyms = common.P.get_all_synonyms()