diff options
author | Mike Bayer <classic@zzzcomputing.com> | 2014-06-13 12:53:44 -0400 |
---|---|---|
committer | Mike Bayer <classic@zzzcomputing.com> | 2014-06-13 12:53:44 -0400 |
commit | e4c8e36a2c00129fb141cafc215296e098c1606d (patch) | |
tree | 0a0b8af0341e2d270f8121c3bf557ecbf8c4e7ec | |
parent | 30685d06840e18f28e7c88d1bc251e7e5e8a130d (diff) | |
parent | fa42d806156991e5dae3d87e241e52f0f6a59c0e (diff) | |
download | dogpile-cache-e4c8e36a2c00129fb141cafc215296e098c1606d.tar.gz |
Merged in javex/dogpile.cache (pull request #7)
Add a SQLAlchemy recipe for refreshing the cache
-rw-r--r-- | docs/build/usage.rst | 74 |
1 files changed, 74 insertions, 0 deletions
diff --git a/docs/build/usage.rst b/docs/build/usage.rst index afa792e..0c41554 100644 --- a/docs/build/usage.rst +++ b/docs/build/usage.rst @@ -347,3 +347,77 @@ in order to invalidate everything having to do with that id. print user_fn_two(5) print user_fn_three(7) print user_fn_two(7) + + +Refreshing After Database Update with SQLAlchemy +------------------------------------------------ + +If you use SQLAlchemy and cache query results (for example after they are +processed) you may find yourself in need of invalidating the cache after the +database has been altered to provide the most up-to-date results. For example, +if you have a decorated function like this: + +.. code-block:: python + + @region.cache_on_arguments() + def get_some_data(argument): + # query database to get data + data = Session().query(DBClass).filter(DBClass.argument == argument).all() + return data + +If you change the database and want this function to return fresh data, you +could just call ``get_some_data.invalidate(argument)``. However, this only +leads to the deletion of the old value. A new value is not generated until the +next call. A common usage for a cache is if fetching the data is time-intense, +so every once in a while a client has to wait for his new data. Another +alternative would be to recreate the cache on the function adding new data. For +example, say this function updates the database: + +.. code-block:: python + + def add_new_data(argument): + Session().add(DBClass(argument=argument)) + +Inside this class we could call the ``refresh`` function to generate new data +or the ``invalidate`` function to just remove the old data: + +.. code-block:: python + + def add_new_data(argument): + # ... + # Either refresh the data + get_some_data.refresh(argument) + # Or invalidate it + get_some_data.invalidate(argument) + +However, this leads to a new issue: Now the client adding new data has to wait. +Instead, you can use the folloing recipe to regenerate the value after the data +has been saved to the database. Since you start a new thread, no client has to +wait. And because of dogpile-locking the cache will return fresh data as soon +as it is done and return old data before. + +.. code-block:: python + + def cache_refresh(refresher, *args, **kwargs): + """ + Refresh the functions cache data in a new thread. Starts refreshing only + after the session was committed so all database data is available. + """ + + def do_refresh(session): + t = Thread(target=refresher, args=args, kwargs=kwargs) + t.daemon = True + t.start() + event.listen(Session(), "after_commit", do_refresh) + +And its usage: + +.. code-block:: python + + def add_new_data(argument): + # ... + cache_refresh(get_some_data.refresh, argument) + +This example assumes that you have a threadlocal session factory as is common +with web applications. Otherwise you might need to pass in your session to this +function as well. |