Distributed Locks for Redis Template - redis

I am trying to figure how distributed locks are used/implemented in Redis using Redis Template. I have race condition scenario so cant use Optimistic Locking with Multi and Exec.
I see RedisLockService implmentations which implements org.springframework.cloud.cluster.lock.LockService but that has been deprecated. Is there something new that has replaced it.

Why not use Redisson to implement a redis lock. there is a complete set of different distributed lock implementation in Redisson.

Related

Syncronize multiple instances of Spring Cache with a Redis lock

I'm building a Spring Boot application that uses Spring Cache with a Redis backing store and needs to synchronize the updates made to the cache.
The caching is not made on the fly, but by an scheduled process that updates the cache periodically.
The algorithm I came up with is:
periodically the instances will check if the Redis cache is older than some predetermined time
if that's the case, the instance will try to acquire a lock on some Redis key
if the instance successfully locks the key, it will then proceed with the update
if some other instance already locked the key, move on
all instances can still read the cache
Everything is more or less already built, all I need is to implement the locking/releasing mechanism.
Spring Cache is using Lettuce to interact with Redis, what is the best way to get an connection to Redis and manage the locking mechanism?
As you may already be aware, Spring's Cache Abstraction provides simple coordination amongst multiple Threads in a single Spring [Boot] application process using the sync attribute on the #Cacheable annotation (see ref doc).
NOTE: Despite the comment ("... use the sync attribute to instruct the underlying cache provider to lock the cache entry while the value is being computed. As a result, only one thread is busy computing the value, while the others are blocked until the entry is updated in the cache.") in the documentation, the locking mechanics is handled by the core framework itself, and in most cases, not the provider. Anyway...
However, this "coordination" is only per-process and will not work for multiple Spring [Boot] application instances, or (OS) JVM processes. In this case, you need some form of distributed locking across your multiple Spring [Boot] application instances to coordinates access to shared cache entries stored in the single Redis server (cluster) shared by your Spring [Boot] application instances.
I am no Redis expert (I am still learning), but I am familiar with similar NoSQL stores (Apache Geode/VMware GemFire, Hazelcast, etc) and distributed locking mechanisms. I see that distributed locking is possible to achieve with Redis as well. In a quick search, I found "Distributed Locking" in Redis, and specifically, "Building a lock in Redis". This is probably the best way to go.
In addition, if you want to make this distributed locking automatically/transparently available through Spring's Cache Abstraction, then you could possibly create a custom AOP Aspect and weave this Aspect together with the framework provided Caching Aspect (Interceptor), being conscious of ordering, as 1 idea.
Alternatively, you could implement wrapper implementations for the Spring Cache and CacheManager SPI interfaces that implement distributed locking on top of the core Redis Cache and CacheManager provider implementations provided by Spring Boot/Spring Data Redis.
Of course, there are multiple ways to go about this. Just tossing out more ideas, but have a look at the distributed locking information in the book.

Redisson: Locking with Spring cache

Redisson provides support for locking backed by Redis. It also provides implementation for working with spring cache framework. But based on what I saw locking is not called by default when try to update a key in a cache using spring cache framework. Redisson has separate APIs for locking a particular key. Is that correct?
Also the locking APIs seem to take key as an input so I am not clear how locking works. For locking I am assuming you need both cache name and key.
I am new to redis so any help in throwing some light on this is really appreciated. Thanks
Firstly, locking in Redisson is implement by Redis, but not only used for Redis updating.
For example if you want to implement an atomic operation like this:
Get key value from Redis
Calculate a new value based on some logic
Save the new value to Redis and Mysql
You can use Redisson lock to make the operation atomically.
Secondly, in Redis, set/update command is atomic and you don't need to lock the key if you only update the value.
And for locking API, Redisson implement lock by Redis key/value, so you only need to provide lock-key, which generally contains a resource id and resource type(like "lock:user:31352")

Redis multi behavior in cluster mode

When working with a single redis instance I can be sure that commands inside MULTI will be processed as a single atomic operation.
What happens when redis operates in cluster mode?
Can I be sure slaves won't get intermediate result of MULTI but only the whole/none of commands that were send as a MULTI(transaction)?
added: all commands inside MULTI operating on the same slot and keys are tagged with {tagName}
Thanks!
Redis' replication between master and slave(s) is designed to honor MULTI's assurances, so yeah, you can be sure. Put differently, the replication stream that the slave gets is a built out of the write operations that the master performs. These are sent in order, and since MULTI guarantees atomicity on the master, it follows that the same applies to the slaves.

optimistic locking in ServiceStack's Redis Client

We are trying to implement a pattern where we update the Redis in 2 cases
1. from the db every 5-10 minutes.
2. on specific use cases we update the current Redis data according to the use case (from time to time may need to ask the DB for the data again)
Question: What is the way to implement optimistic locking in ServiceStack's Redis?
I'm new to Redis but have recently been reading a lot about issues like this.
I think you want WATCH with MULTI/EXEC.
The redis docs have the reference for these commands, but they're pretty minimal:
http://redis.io/commands/watch
http://redis.io/commands/multi
http://redis.io/commands/exec
The "transactions" page ties them together a lot better:
http://redis.io/topics/transactions
I also recommend the "little Redis book":
http://openmymind.net/2012/1/23/The-Little-Redis-Book/

Acquiring Locks when updating a Redis key/value

I'm using AcquireLock method from ServiceStack Redis when updating and getting the key/value like this:
public virtual void Set(string key, T entity)
{
using (var client = ClientManager.GetClient())
{
using (client.AcquireLock(key + ":locked", DefaultLockingTimeout, DefaultLockExpire))
{
client.Set(key, entity);
}
}
}
I've extended AcqurieLock method to accept extra parameter for expiration of the lock key. So I'm wondering that if I need AcquireLock at all or not? My class uses AcquireLock in every operation like Get<>, GetAll<>, ExpireAt, SetAll<>, etc..
But this approach doesn't work everytime. For example, if the operating in the lock throws an exception, then the key remains locked. For this situation I've added DefaultLockExpire parameter to AcquireLock method to expire the "locked" key.
Is there any better solution, or when do we need acquiring locks like "lock" blocks in multi-thread programming.
As The Real Bill answer has said, you don't need locks for Redis itself. What the ServiceStack client offers in terms of locking is not for Redis, but for your application. In a C# application, you can lock things locally with lock(obj) so that something cannot happen concurrently (only one thread can access the locked section at a time), but that only works if you have one webserver. If you want to prevent something happening concurrently, you need a locking mechanism living outside of the webserver. Redis is a good fit for this.
We have a case where it is checked if a customer has a shopping cart already and if not, create it. Between checking and creating it, there's a time where another request could have also found out that cart doesn't exist and might also proceed to create one. That's a classical case for locking but a simple lock wouldn't work here as the request may have arrived from an entirely different web-server. So for this, we use the ServiceStack Redis client (with some abstraction) to lock using Redis and only allow one request at a time to enter the "create a cart" section.
So to answer your actual question: no, you don't need a lock for getting/setting values to Redis.
I wouldn't use locks for get/set operations. Redis will do those actions atomically, so there is no chance of it getting "changed underneath you" when setting or getting. I've built systems where hundreds of clients are updating/operating on values concurrently and never needed a lock to do those operations (especially an expire).
I don't know how Service Stack redis implements the locking it has so I can't say why it is failing. However, I'm not sure I'd trust it given there is no true locking needed on the Redis side for data operations. Redis is single-threaded so locking there doesn't make sense.
If you are doing complex operations where you get a value, operate on things based on it, then update it after a while and can't have the value change in the meantime I'd recommend reading and groking http://redis.io/topics/transactions to see if what you want is what Redis is good for, whether your code needs refactored to eliminate the problem, or at the least find a better way to do it.
For example, SETNX may be the route you need to get what you want, but without details I can't say it will work.
As #JulianR says, the locking in ServiceStack.Redis is only for application-level distributed locks (i.e. to replace using a DB or an empty .lock file on a distributed file system) and it only works against other ServiceStack.Redis clients in other process using the same key/API to acquire the lock.
You would never need to do this for normal Redis operations since they're all atomic. If you want to ensure a combination of redis operations happen atomically than you would combine them within a Redis Transaction or alternatively you can execute them within a server-side Lua script - both allow atomic execution of batch operations.