Smache is a Python library that allows you to cache the results of a function and keep the values updated such that when the result is cached once, you will never have to wait for the result to be computed since Smache will automatically update your cached value when the underlying data updates.
Smache uses Redis to store metadata about the cached functions, which means it requires a connection to Redis. If you have Redis installed on you machine, Smache will automatically use a default localhost connection.
With Redis installed, you have to install smache, e.g. using pip
:
pip install smache
(Need documentation about the configuration possibilities)
Smache is implemented using decorators, which means you don't need to change the function in order to cache it. You only need to specify the dependencies of your cached function. At the current moment Smache only works with MongoDB.
We will use an example to demonstrate the usage. The code for the examples are
also located in the example
-folder.
Before using smache, we have an application in the app.py
module with the
following code:
import time
from mongoengine import Document, StringField, connect
connect('smache_test_db')
class User(Document):
name = StringField()
def computed_value(user):
time.sleep(2) # To simulate slow computation
return user.name
Since the function computed_value
takes 2 seconds to compute, we would like
to cache it using smache.
We will therefore change our application, by instantiating a configured Smache
object and add a decorator to our function as following:
import time
from mongoengine import Document, StringField, connect
from smache import Smache
connect('smache_test_db')
class User(Document):
name = StringField()
mysmachecache = Smache()
@mysmachecache.computed(User)
def computed_value(user):
time.sleep(2) # To simulate slow computation
return user.name
Smache updates cached values asynchronously, which means you need to start a worker. To manage the background jobs, Smache uses rq.
We need to run a worker in the same context as our application. As a simple example,
we can add the following code into a module called worker.py
:
import app
from rq import Connection, Worker, Queue
if __name__ == '__main__':
with Connection():
worker = Worker(Queue('smache'))
worker.work()
To run the example, open up a terminal and run the worker:
python worker.py
To test that our app caches our value and updates it in the background, we can run the following test (e.g. in a python terminal):
import time
from app import User, computed_value
User(name='John Doe').save()
user = User.objects.order_by('-_id').first()
# This first time would take 4 seconds to compute
print computed_value(user)
user.name = 'John Dee'
user.save()
# At this point Smache has noticed that a cached value needs to be updated. We will wait a couple of seconds for it to finish.
time.sleep(4)
# This is computed instantly since it retreives the value from the cache
print computed_value(user)
To setup development, run:
python setup.py develop
To run the tests, run:
python setup.py test
brew install terminal-notifier
python setup.py develop
pip install -rrequirements_dev.txt
./bin/watchtests