flask-caching icon indicating copy to clipboard operation
flask-caching copied to clipboard

Delete memoized versions based on specific parameter only

Open instanceofmel opened this issue 2 years ago • 0 comments

I have built a REST interface with flask and I'm caching most of the db queries using this extension to minimize db hits during a request. I've done this by outsourcing the db queries into functions and decorating those functions with memoize(). To invalidate the cache once a db row has been added, modified or deleted, I'm using sqlalchemy events, which call delete_memoized(...) and delete the memoized versions.

The app has multi tenant capability and all of the tables holding user data have a tenant_id column. Most of the memoized functions accept a tenant_id parameter in order to restrict each query to a specific tenant. That has worked very well so far, but in some cases the REST interface must return paginated data and the page parameter is set beforehand by the user. Following my previous approach, I have created a function which accepts the tenant_id and page parameters and decorated it with memoize():

@cache.memoize()
def get_tenant_documents(tenant_id, page=1, per_page=20):
    query = Document.query.filter(Document.tenant_id == tenant_id)
    pagination = query.paginate(page=page, per_page=per_page, max_per_page=50)

    return pagination

Unfortunately I can't use this approach, because it's not possible to invalidate the cache on a per tenant basis using the value of tenant_id. I can only delete the cache for all versions of the function (by not passing any parameters to cache.delete_memoized), but this would clear the cache for all users of course.

It would be nice to have the possibility to delete the memoized versions by a selected argument, which would look like this in my case:

@event.listens_for(Document, "after_insert")
@event.listens_for(Document, "after_update")
@event.listens_for(Document, "after_delete")
def clear_document_cache(
    mapper,
    connection,
    document_obj,
):
    cache.delete_memoized(get_tenant_documents, document_obj.tenant_id)  # Ignore page, per_page params and delete all versions with that tenant_id

I have looked through the source code a bit but could not find a way to accomplish this. I'm not even sure that this feature can be added to this extension. The only way I can think of is to use the Redis SCAN command and * wildcard to find keys matching a specific pattern and deleting them afterwards. Would that be a possible solution? I'm happy to help!

BR

instanceofmel avatar Sep 19 '22 15:09 instanceofmel