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)
INFO - my-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)
INFO - b'abc123'

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'])
INFO - joe
INFO - ['admin', 'editor']

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)
INFO - None

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')
INFO - Key is present
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)
INFO - Key not found, fetching from source ..
INFO - value-from-source

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

FeatureWhat it does
RedisLists, hashmaps and counters behind the same object
Distributed locksCoordinate work across servers beyond the nx pattern
Response cachingCache whole channel responses with no code

Learn more