The built-in cache
Store data that every service on every server sees immediately.
Every service has access to a built-in cache via self.cache. It is the server-wide Redis connection - self.cache, self.redis and self.kvdb are three names for the same object, so everything Redis offers is available and the examples below cover the caching patterns specifically.
There is one cache, shared by all services and all servers of an environment, which is what makes it useful for coordination - a value stored by one service is immediately visible to every other one, on every server.
Set and get a value
# -*- coding: utf-8 -*-
# Zato
from zato.server.service import Service
class MyService(Service):
def handle(self):
# Store a value
self.cache.set('my-key', 'my-value')
# Read it back - values come back as bytes
value = self.cache.get('my-key')
value = value.decode('utf8')
self.logger.info(value)
Set a value with expiry
The ex parameter is the expiry in seconds - after that, the key is gone and get returns None.
# -*- coding: utf-8 -*-
# Zato
from zato.server.service import Service
class MyService(Service):
def handle(self):
# Store a value that expires in 60 seconds
self.cache.set('session-token', 'abc123', ex=60)
# Read it back - returns the value if not expired, None otherwise
value = self.cache.get('session-token')
self.logger.info(value)
Cache a dictionary
Dictionaries and lists are serialized to JSON on the way in and parsed on the way out.
# -*- coding: utf-8 -*-
# stdlib
from json import dumps, loads
# Zato
from zato.server.service import Service
class MyService(Service):
def handle(self):
user_data = {
'username': 'joe',
'email': 'joe@example.com',
'roles': ['admin', 'editor'],
}
# Store a dict with a 5-minute expiry
self.cache.set('user:123', dumps(user_data), ex=300)
# Read it back as a Python dict
cached = self.cache.get('user:123')
cached = loads(cached)
self.logger.info(cached['username'])
self.logger.info(cached['roles'])
Delete a key
# -*- coding: utf-8 -*-
# Zato
from zato.server.service import Service
class MyService(Service):
def handle(self):
self.cache.set('temp-key', 'temp-value')
# Delete the key
self.cache.delete('temp-key')
# Returns None because the key was deleted
value = self.cache.get('temp-key')
self.logger.info(value)
Check whether a key exists
# -*- coding: utf-8 -*-
# Zato
from zato.server.service import Service
class MyService(Service):
def handle(self):
self.cache.set('flag-key', '1', ex=300)
if self.cache.exists('flag-key'):
self.logger.info('Key is present')
self.cache.delete('flag-key')
if not self.cache.exists('flag-key'):
self.logger.info('Key is gone')
Cache miss
# -*- coding: utf-8 -*-
# Zato
from zato.server.service import Service
class MyService(Service):
def handle(self):
# Returns None when the key does not exist
value = self.cache.get('my-key')
if value is None:
self.logger.info('Key not found, fetching from source ..')
value = 'value-from-source'
self.cache.set('my-key', value, ex=120)
self.logger.info(value)
Set only if absent
With nx=True, the value is stored only when the key does not exist yet, and the call tells you whether it was this call that stored it. Because the cache is shared, this is a single-winner primitive across all services and servers - only one of any number of concurrent callers gets True.
# -*- coding: utf-8 -*-
# Zato
from zato.server.service import Service
class MyService(Service):
def handle(self):
# Only one concurrent caller will store the value and see True
is_winner = self.cache.set('daily-report-lock', '1', ex=3600, nx=True)
if is_winner:
self.logger.info('This invocation runs the report')
else:
self.logger.info('Another invocation is already running it')
Beyond caching
Since self.cache is a full Redis client, lists, hashmaps, counters and transactions are all available too - the Redis examples page shows them, and for locking across servers the platform has purpose-built distributed locks. Whole channel responses can also be cached declaratively, without any code, with response caching.
See also
| Feature | What it does |
|---|---|
| Redis | Lists, hashmaps and counters behind the same object |
| Distributed locks | Coordinate work across servers beyond the nx pattern |
| Response caching | Cache whole channel responses with no code |