summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMike Bayer <classic@zzzcomputing.com>2014-06-13 12:53:44 -0400
committerMike Bayer <classic@zzzcomputing.com>2014-06-13 12:53:44 -0400
commite4c8e36a2c00129fb141cafc215296e098c1606d (patch)
tree0a0b8af0341e2d270f8121c3bf557ecbf8c4e7ec
parent30685d06840e18f28e7c88d1bc251e7e5e8a130d (diff)
parentfa42d806156991e5dae3d87e241e52f0f6a59c0e (diff)
downloaddogpile-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.rst74
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.