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
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
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)
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)
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)
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 store() 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
Read
We can read a value from the database with the retrieve() method, which given the 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'
Delete
To delete a record from the database, we can use the remove method. This method accepts the context and a 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
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
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)
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)
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)
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);
err = lock.AcquireLock(context.Background(), "key1", 3000, 1000)
locked = lock.try_acquire_lock(my_context, "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 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)
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")
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")
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.