The built-in cache
Store data that is immediately available to every service on every server.
Every service has access to a built-in cache via self.cache.
It is backed by Redis, so everything Redis offers is available to your services as well, and the same self.cache has an alias of self.redis as well.
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 |