summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorjavex <florian.ruechel@gmail.com>2013-10-25 21:58:03 +0200
committerjavex <florian.ruechel@gmail.com>2013-10-25 21:58:03 +0200
commitfa42d806156991e5dae3d87e241e52f0f6a59c0e (patch)
treec6d915336aedcf48fd69ad1da304c7c064bf59af
parentc4ea9e7c6082ffb55519c4489df488f09af6cc1e (diff)
downloaddogpile-cache-fa42d806156991e5dae3d87e241e52f0f6a59c0e.tar.gz
Add a SQLAlchemy recipe for refreshing the cache
-rw-r--r--docs/build/usage.rst74
1 files changed, 74 insertions, 0 deletions
diff --git a/docs/build/usage.rst b/docs/build/usage.rst
index 74f634c..15db6b5 100644
--- a/docs/build/usage.rst
+++ b/docs/build/usage.rst
@@ -345,3 +345,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.