Redis

How to create a cache and a lock using Redis.

Key takeaways

RedisCache Distributed cache that stores values in a Redis in-memory database.
RedisLock Distributed lock that is implemented based on a Redis in-memory database.

Introduction

In this tutorial, you will see how to use two components related to the Redis database. The first is the RedisCache class, which can be used to create distributed caches that store values in Redis. The second is RedisLock, a component that allows us to create a distributed lock based on the Redis database.

Pre-requisites

Before using this library, we need to install the Redis module. This can be done with the following command:

 npm install pip-services4-redis-node --save

go get -u github.com/pip-services4/pip-services4-go/pip-services4-redis-go@latest

pip install pip-services4-redis
Not available

RedisCache

This component provides a way to create a distributed cache that stores values in a Redis database. The sections below explain how to create this cache and perform basic CRUD operations with it.

Pre-requisites

In order to use the RedisCache component, we need to import it first. The command to do this is:

import { RedisCache } from "pip-services4-redis-node";

import (
    rcache "github.com/pip-services4/pip-services4-go/pip-services4-redis-go/cache"
)

from pip_services4_redis.cache import RedisCache
Not available

Component creation

To be able to interact with a Redis database, we need to create an instance of the RedisCache component and configure it. The following example shows how to do this.

import { RedisCache } from "pip-services4-redis-node";
import { ConfigParams } from 'pip-services4-components-node';

let cache = new RedisCache();
cache.configure(ConfigParams.fromTuples(
    "connection.host", "localhost",
    "connection.port", 6379
));

import (
	"context"

	conf "github.com/pip-services4/pip-services4-go/pip-services4-components-go/config"
	rcache "github.com/pip-services4/pip-services4-go/pip-services4-redis-go/cache"
)

cache := rcache.NewRedisCache[string]()
cache.Configure(context.Background(), conf.NewConfigParamsFromTuples(
	"connection.host", "localhost",
	"connection.port", 6379,
))

from pip_services4_components.config import ConfigParams
from pip_services4_components.context import Context

cache = RedisCache()
cache.configure(ConfigParams.from_tuples(
    "connection.host", "localhost",
    "connection.port", 6379
))

context_data = {
  "traceId": "123",
}

my_context = Context(context_data)
Not available

Once our component has been configured, we can connect it to the database via the open() method:

await cache.open(ctx);


err := cache.Open(context.Background())

cache.open(my_context)
Not available

Once our task has been completed, we can free used resources by closing this component with the close() method, which takes the context as its only input parameter.

await cache.close(ctx);


err = cache.Close(context.Background())

cache.close(my_context)
Not available

CRUD operations

The RedisCache component offers the necessary methods to manage the interaction with a Redis database and perform all the CRUD operations. They are:

Create and update

We can use the create() method to create a new entry. This method accepts the context, key, value, and expiration time as inputs. It returns True if the operation was successful and False otherwise. If the key already exists in the database, it updates the old value with the new value. An example of its usage is.

let result = await cache.store(ctx, "key1", "ABC", 10000); // Returns "OK" if successful 


result, err := cache.Store(context.Background(), "key", "ABC", 10000) // Returns saved value and err or nil

result = cache.store(my_context, "key1", "ABC", None)  # Returns True if successful and False otherwise 
Not available
Read

We can read a value from the database with the retrieve() method, which given a context and a key, returns the corresponding value.

result = await cache.retrieve(ctx, "key1");  // Returns "ABC"


res, err := cache.Retrieve(context.Background(), "key1") // Returns "ABC"

result = cache.retrieve(my_context, 'key1')  # Returns b'ABC'
Not available
Delete

To delete a record from the database, we can use the remove method. This method accepts the context and the key as inputs and returns 1 if the removal was successful and 0 otherwise.

await cache.remove(ctx, "key1");


err := cache.Remove(context.Background(), "key1")

result = cache.remove(my_context,'key1') # Returns 1 if successful and 0 otherwise
Not available

RedisLock

This component can be used to create a distributed lock that is implemented using a Redis database.

Pre-requisites

In order to use this lock, we need to import the RedisLock class first. This can be done with the following command:

import { RedisLock } from "pip-services4-redis-node";

import (
    rlock "github.com/pip-services4/pip-services4-go/pip-services4-redis-go/lock"
)

from pip_services4_redis.lock import RedisLock
Not available

Component creation

Now, we can create our lock by defining an instance of the class and configuring it. For this, it is convenient to use the ConfigParams component, which creates a key-value map with the configuration parameters and their values. The example below shows how to do this.

import { RedisLock } from "pip-services4-redis-node";
import { ConfigParams } from 'pip-services4-components-node';

let lock = new RedisLock();
lock.configure(ConfigParams.fromTuples(
    "connection.host", "localhost",
    "connection.port", 6379
));

import (
	"context"

	conf "github.com/pip-services4/pip-services4-go/pip-services4-components-go/config"
	rlock "github.com/pip-services4/pip-services4-go/pip-services4-redis-go/lock"
)


lock := rlock.NewRedisLock()
lock.Configure(context.Background(), conf.NewConfigParamsFromTuples(
	"connection.host", "localhost",
	"connection.port", 6379,
))

from pip_services4_components.config import ConfigParams
from pip_services4_components.context import Context

lock = RedisLock()
lock.configure(ConfigParams.from_tuples(
    "connection.host", "localhost",
    "connection.port", 6379
))

context_data = {
  "traceId": "123",
}

my_context = Context(context_data)
Not available

Once our component has been defined, we can connect it to the Redis database with the open() method, which accepts the context as an input parameter.

await lock.open(ctx);


err := lock.Open(context.Background())

lock.open(my_context)
Not available

Later on, once we have completed our task, we can close the lock with the close() method to free used resources.

await lock.close(ctx);


err = lock.Close(context.Background())

lock.close(my_context)
Not available

Managing the lock

Once our lock is ready, we need to manage it. For this, we have two main operations: acquire and release, which can be executed with a set of methods available in this class.

Acquire

Here, we have two different methods. They are as follows:

tryAcquireLock()

To acquire our lock, we can use the try_acquire_lock() method. This method makes a single attempt to acquire the lock and returns a Boolean value indicating success or failure. It accepts the context, the key, and the timeout in milliseconds as input parameters. The following code explains its usage.

let locked = await lock.tryAcquireLock(ctx, "key1", 3300);


locked, err := lock.TryAcquireLock(context.Background(), "key1", 3300)

locked = lock.try_acquire_lock(my_context, "key1", 3300) # returns bool
Not available
acquireLock()

Alternatively, we could use the acquireLock() method, which makes multiple attempts to acquire the lock. This class inherits this method from the Lock class, which it extends.

This method accepts the context, key, timeout, and acquisition timeout as input parameters. The following example shows how to use it.

await lock.acquireLock(ctx, "key1", 3000, 1000);


err = lock.AcquireLock(context.Background(), "key1", 3000, 1000)

lock.acquire_lock(my_context, "key1", 3000, 1000)
Not available
Release

Once the lock has completed its function, it needs to be released. This can be done with the releaseLock() method, which takes the context and the key as input parameters. The following example shows how to release a lock.

await lock.releaseLock(ctx, "key1");


err = lock.ReleaseLock(context.Background(), "key1")

lock.release_lock(my_context, "key1")
Not available

Example

The example below summarizes what we learned in the previous sections. In it, a lock is created and configured. Then, it is connected to the database. Once connected, it is acquired, some task processing is done, and after this task is completed, the lock is released.

However, an important point to consider is that if the task was not completed before the timeout expires, the lock is released anyway.

import { RedisLock  } from "pip-services4-redis-node";
import { ConfigParams } from 'pip-services4-components-node';

let lock = new RedisLock();
lock.configure(ConfigParams.fromTuples(
    "connection.host", "localhost",
    "connection.port", 6379
));

await lock.open(ctx);
await lock.acquireLock(ctx, "key1", 3000, 1000);

try {
    // Processing...
} finally {
    await lock.releaseLock(ctx, "key1");
}

import (
	"context"

	conf "github.com/pip-services4/pip-services4-go/pip-services4-components-go/config"
	rlock "github.com/pip-services4/pip-services4-go/pip-services4-redis-go/lock"
)

func main() {
	lock := rlock.NewRedisLock()
	lock.Configure(context.Background(), conf.NewConfigParamsFromTuples(
		"connection.host", "localhost",
		"connection.port", 6379,
	))
	err := lock.Open(context.Background())

	err = lock.AcquireLock(context.Background(), "key1", 3000, 1000)

	defer lock.ReleaseLock(context.Background(), "key1")

	// Processing...
}

from pip_services4_redis.lock import RedisLock
from pip_services4_components.config import ConfigParams
from pip_services4_components.context import Context

lock = RedisLock()
lock.configure(ConfigParams.from_tuples(
    "connection.host", "localhost",
    "connection.port", 6379
))

context_data = {
  "traceId": "123",
}

my_context = Context(context_data)

lock.open(my_context)
lock.acquire_lock(my_context, "key1", 3000, 1000)

try:
    # Processing...
    pass
finally:
    lock.release_lock(my_context, "key1")
Not available

Wrapping up

In this tutorial, we have learned how to use the RedisCache and RedisLock components. The first is used to create a distributed cache that stores values in Redis. The second helps us create distributed locks based on a Redis database.

For the RedisCache component, we saw how to perform the different CRUD operations and, for the RedisLock component, we understood how to manage it in practical situations.