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-services-redis-nodex --save
dotnet add package PipServices3.Redis
go get -u github.com/pip-services3-gox/pip-services3-redis-gox@latest
dart pub add pip_services3_redis
pip install pip-services3-redis
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-services3-redis-nodex';
using PipServices3.Redis.Cache;
import (
rcache "github.com/pip-services3-gox/pip-services3-redis-gox/cache"
)
import 'package:pip_services3_redis/pip_services3_redis.dart';
from pip_services3_redis.cache import RedisCache
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 { ConfigParams } from 'pip-services3-commons-nodex';
let cache = new RedisCache();
cache.configure(ConfigParams.fromTuples(
"connection.host", "localhost",
"connection.port", 6379
));
using PipServices3.Commons.Config;
var cache = new RedisCache();
cache.Configure(ConfigParams.FromTuples(
"connection.host", "localhost",
"connection.port", 6379
));
import (
conf "github.com/pip-services3-gox/pip-services3-commons-gox/config"
rcache "github.com/pip-services3-gox/pip-services3-redis-gox/cache"
)
cache := rcache.NewRedisCache()
cache.Configure(context.Background(), conf.NewConfigParamsFromTuples(
"connection.host", "localhost",
"connection.port", 6379,
))
import 'package:pip_services3_commons/pip_services3_commons.dart';
import 'package:pip_services3_redis/pip_services3_redis.dart';
var cache = RedisCache();
cache.configure(ConfigParams.fromTuples(
['connection.host', 'localhost', 'connection.port', 6379]));
from pip_services3_commons.config import ConfigParams
cache = RedisCache()
cache.configure(ConfigParams.from_tuples(
"connection.host", "localhost",
"connection.port", 6379
))
Once our component has been configured, we can connect it to the database via the open() method:
await cache.open(null);
await cache.OpenAsync(null);
err := cache.Open(context.Background(), "123")
await cache.open(null);
cache.open("123")
Once our task has been completed, we can free used resources by closing this component with the close() method, which takes the correlationId as its only input parameter.
await cache.close(null);
await cache.CloseAsync(null);
err = cache.Close(context.Background(), "123")
await cache.close(null);
cache.close('123')
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 correlationId, 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(null, "key1", "ABC", 10000); // Returns "OK" if successful
var result = await cache.StoreAsync(null, "key1", "ABC", 10000); // Returns True if successful and False otherwise
result, err := cache.Store(context.Background(), "123", "key1", "ABC", 10000) // Returns saved value and err or nil
var result = await cache.store(null, 'key1', 'ABC', 10000); // Returns "OK" if successful
result = cache.store("123", "key1", "ABC", None) # Returns True if successful and False otherwise
Read
We can read a value from the database with the retrieve() method, which given a correlationId and a key, returns the corresponding value.
result = await cache.retrieve(null, "key1"); // Returns "ABC"
result = await cache.RetrieveAsync<string>(null, "key1"); // Returns "ABC"
res, err := cache.Retrieve(context.Background(), "123", "key1") // Returns "ABC"
result = await cache.retrieve(null, 'key1'); // Returns "ABC"
result = cache.retrieve('123', 'key1') # Returns b'ABC'
Delete
To delete a record from the database, we can use the remove method. This method accepts the correlationId and the key as inputs and returns 1 if the removal was successful and 0 otherwise.
await cache.remove(null, "key1");
await cache.RemoveAsync(null, "key1");
err := cache.Remove(context.Background(), "123", "key1")
await cache.remove(null, 'key1');
result = cache.remove('123','key1') # Returns 1 if successful and 0 otherwise
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-services3-redis-nodex';
using PipServices3.Redis.Lock;
import (
rlock "github.com/pip-services3-gox/pip-services3-redis-gox/lock"
)
import 'package:pip_services3_redis/pip_services3_redis.dart';
from pip_services3_redis.lock import RedisLock
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 { ConfigParams } from 'pip-services3-commons-nodex';
let lock = new RedisLock();
lock.configure(ConfigParams.fromTuples(
"connection.host", "localhost",
"connection.port", 6379
));
using PipServices3.Commons.Config;
var rLock = new RedisLock();
rLock.Configure(ConfigParams.FromTuples(
"connection.host", "localhost",
"connection.port", 6379
));
import (
conf "github.com/pip-services3-gox/pip-services3-commons-gox/config"
rlock "github.com/pip-services3-gox/pip-services3-redis-gox/lock"
)
lock := rlock.NewRedisLock()
lock.Configure(context.Background(), conf.NewConfigParamsFromTuples(
"connection.host", "localhost",
"connection.port", 6379,
))
import 'package:pip_services3_commons/pip_services3_commons.dart';
import 'package:pip_services3_redis/pip_services3_redis.dart';
var lock = RedisLock();
lock.configure(ConfigParams.fromTuples(
['connection.host', 'localhost', 'connection.port', 6379]));
from pip_services3_commons.config import ConfigParams
lock = RedisLock()
lock.configure(ConfigParams.from_tuples(
"connection.host", "localhost",
"connection.port", 6379
))
Once our component has been defined, we can connect it to the Redis database with the open() method, which accepts the correlationId as an input parameter.
await lock.open(null);
await rLock.OpenAsync(null);
err := lock.Open(context.Background(), "123")
await lock.open(null);
lock.open("123")
Later on, once we have completed our task, we can close the lock with the close() method to free used resources.
await lock.close(null);
await rLock.CloseAsync(null);
err = lock.Close(context.Background(), "123")
await lock.close(null);
lock.close('123')
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 correlationId, the key, and the timeout in milliseconds as input parameters. The following code explains its usage.
let locked = await lock.tryAcquireLock(null, "key1", 3300);
var locked = rLock.TryAcquireLock(null, "key1", 3300);
locked, err := lock.TryAcquireLock(context.Background(), "123", "key1", 3300)
var locked = await lock.tryAcquireLock(null, 'key1', 3300);
locked = lock.try_acquire_lock("123", "key1", 3300) # returns bool
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 correlationId, key, timeout, and acquisition timeout as input parameters. The following example shows how to use it.
await lock.acquireLock(null, "key1", 3000, 1000);
rLock.AcquireLock(null, "key1", 3000, 1000);
err = lock.AcquireLock(context.Background(), "123", "key1", 3000, 1000)
await lock.acquireLock(null, 'key1', 3000, 1000);
lock.acquire_lock("123", "key1", 3000, 1000)
Release
Once the lock has completed its function, it needs to be released. This can be done with the releaseLock() method, which takes the correlationId and the key as input parameters. The following example shows how to release a lock.
await lock.releaseLock(null, "key1");
rLock.ReleaseLock(null, "key1");
err = lock.ReleaseLock(context.Background(), "123", "key1")
await lock.releaseLock(null, 'key1');
lock.release_lock("123", "key1")
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-services3-redis-nodex';
import { ConfigParams } from 'pip-services3-commons-nodex';
let lock = new RedisLock();
lock.configure(ConfigParams.fromTuples(
"connection.host", "localhost",
"connection.port", 6379
));
await lock.open(null);
await lock.acquireLock(null, "key1", 3000, 1000);
try {
// Processing...
} finally {
await lock.releaseLock(null, "key1");
}
using PipServices3.Redis.Lock;
using PipServices3.Commons.Config;
var rLock = new RedisLock();
rLock.Configure(ConfigParams.FromTuples(
"connection.host", "localhost",
"connection.port", 6379
));
await rLock.OpenAsync(null);
rLock.AcquireLock(null, "key1", 3000, 1000);
try
{
// Processing...
} finally
{
rLock.ReleaseLock(null, "key1");
}
import (
conf "github.com/pip-services3-gox/pip-services3-commons-gox/config"
rlock "github.com/pip-services3-gox/pip-services3-redis-gox/lock"
)
lock := rlock.NewRedisLock()
lock.Configure(context.Background(), conf.NewConfigParamsFromTuples(
"connection.host", "localhost",
"connection.port", 6379,
))
err := lock.Open(context.Background(), "123")
err = lock.AcquireLock(context.Background(), "123", "key1", 3000, 1000)
defer lock.ReleaseLock(context.Background(), "123", "key1")
// Processing...
import 'package:pip_services3_commons/pip_services3_commons.dart';
import 'package:pip_services3_redis/pip_services3_redis.dart';
var lock = RedisLock();
lock.configure(ConfigParams.fromTuples(
['connection.host', 'localhost', 'connection.port', 6379]));
await lock.open(null);
await lock.acquireLock(null, 'key1', 3000, 1000);
try {
// Processing...
} finally {
await lock.releaseLock(null, 'key1');
}
from pip_services3_redis.lock import RedisLock
from pip_services3_commons.config import ConfigParams
lock = RedisLock()
lock.configure(ConfigParams.from_tuples(
"connection.host", "localhost",
"connection.port", 6379
))
lock.open("123")
lock.acquire_lock("123", "key1", 3000, 1000)
try:
# Processing...
pass
finally:
lock.release_lock("123", "key1")
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.