I have a big issue when using multiple threads with spring-redis-data and it's so easy to reproduce that I think I've been missing something trivial.
If I query a CrudRepository while doing save operations, it sometimes (up to 60%) doesn't find the record on Redis.
Despite the full code could be found in the link above, this are the main components:
@Repository
public interface MyEntityRepository extends CrudRepository<MyEntity, Integer> {
}
@RedisHash("my-entity")
public class MyEntity implements Serializable {
@Id
private int id1;
private double attribute1;
private String attribute2;
private String attribute3;
@GetMapping( "/my-endpoint")
public ResponseEntity<?> myEndpoint () {
MyEntity myEntity = new MyEntity();
myEntity.setAttribute1(0.7);
myEntity.setAttribute2("attr2");
myEntity.setAttribute3("attr3");
myEntity.setId1(1);
myEntityRepository.save(myEntity);//create it in redis
logger.info("STARTED");
for (int i = 0; i < 100; i++){
new Thread(){
@Override
public void run() {
super.run();
myEntity.setAttribute1(Math.random());
myEntityRepository.save(myEntity); //updating the entity
Optional<MyEntity> optionalMyEntity = myEntityRepository.findById(1);
if (optionalMyEntity.isPresent()) {
logger.info("found");
}else{
logger.warning("NOT FOUND");
}
}
}.start();
}
return ResponseEntity.noContent().build();
}
2020-05-26 07:52:53.769 INFO 30655 --- [nio-8080-exec-2] my-controller-logger : STARTED
2020-05-26 07:52:53.795 INFO 30655 --- [ Thread-168] my-controller-logger : found
2020-05-26 07:52:53.798 WARN 30655 --- [ Thread-174] my-controller-logger : NOT FOUND
2020-05-26 07:52:53.798 WARN 30655 --- [ Thread-173] my-controller-logger : NOT FOUND
2020-05-26 07:52:53.806 INFO 30655 --- [ Thread-170] my-controller-logger : found
2020-05-26 07:52:53.806 WARN 30655 --- [ Thread-172] my-controller-logger : NOT FOUND
2020-05-26 07:52:53.812 WARN 30655 --- [ Thread-175] my-controller-logger : NOT FOUND
2020-05-26 07:52:53.814 WARN 30655 --- [ Thread-176] my-controller-logger : NOT FOUND
2020-05-26 07:52:53.819 WARN 30655 --- [ Thread-169] my-controller-logger : NOT FOUND
2020-05-26 07:52:53.826 INFO 30655 --- [ Thread-171] my-controller-logger : found
2020-05-26 07:52:53.829 INFO 30655 --- [ Thread-177] my-controller-logger : found
So simply with 10 threads, 6 of them are not finding the result in db.
As mentioned here replacing in redis with spring data redis contains at least, 9 operations.
So, as to replace a value in redis, it has to remove the hash, the indexes and then adding the new hash and the new indexes again, maybe a thread is in the middle of doing this operations while other thread tries to find the value by index and this index has not beeing added yet.
I think it's nearly impossible that spring data with data-redis has such a bug, so I'm wondering what I'm not understanding of data-redis or redis. As redis has concurrency I think something different may be happening, but with the provided example it seems like that...
Thank you in advance to all of you
This ticket raises the same problem.
The behavior was chosen deliberately to avoid lingering hash entries. Deleting the hash ensures a consistent state and avoids additional entries that should no longer be part of the hash.
Redis Repository operations are not atomic.
So it is intended to be not atomic.
And suggested in the ticket, the solution will be using PartialUpdate
.
Below is a snippet for example
@Autowired
private RedisKeyValueTemplate redisKVTemplate;
...
// id is the @Id value of the entity
private void update(Integer id) {
PartialUpdate update = new PartialUpdate<MyEntity>(id, MyEntity.class)
.set("attribute1", Math.random());
redisKVTemplate.update(update);
}
References:
Update entity in redis with spring-data-redis
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With