Reading keys in Redis while they are being written produces a read-fail as if the key were not present in the cache. Is there any connection configuration in spring/jedis that allow clients to avoid the read fail returning the old data of a key while a new data it is being written?
I saw that to avoid this issue some people uses a local cache, but I am looking for a simpler solution. In my case reading the old value would not be a concern.
Artifacts:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
</dependency>
Spring configuration class:
@Configuration
@EntityScan(basePackages = { "com.entities.redis" })
@EnableRedisRepositories("com.repository.redis")
public class RedisConfig {
@Value("${redis.server}")
private String server;
@Value("${redis.port}")
private Integer port;
@Value("${redis.pool-size}")
private Integer poolSize;
@Value("${redis.database-index}")
private Integer databaseIndex;
@Bean
public JedisPoolConfig jedisPoolConfig() {
JedisPoolConfig jedisPoolConfig = new JedisPoolConfig();
jedisPoolConfig.setMaxTotal(poolSize);
return jedisPoolConfig;
}
@Bean
public JedisClientConfiguration jedisClientConfiguration(JedisPoolConfig jedisPoolConfig) {
return JedisClientConfiguration
.builder()
.usePooling()
.poolConfig(jedisPoolConfig)
.build();
}
@Bean
public JedisConnectionFactory jedisConnectionFactory(JedisClientConfiguration jedisClientConfiguration) {
RedisStandaloneConfiguration config = new RedisStandaloneConfiguration(server, port);
config.setDatabase(databaseIndex);
return new JedisConnectionFactory(config, jedisClientConfiguration);
}
@Bean
public RedisTemplate<?, ?> redisTemplate(JedisConnectionFactory jedisConnectionFactory) {
RedisTemplate<byte[], byte[]> template = new RedisTemplate<byte[], byte[]>();
template.setConnectionFactory(jedisConnectionFactory);
return template;
}
}
Repository class
@Repository
public interface DataBeanRepository extends CrudRepository<DataBean, String> {
}
Entity class
@RedisHash("DataBean")
public class DataBean {
@Id
private String data;
public DataBean(String data) {
this.data = data;
}
public String getData() {
return data;
}
}
Test class
@SpringBootTest(webEnvironment = WebEnvironment.MOCK)
public class RedisTest {
private static Logger LOGGER = LoggerFactory.getLogger(RedisTest.class);
@Autowired
private DataBeanRepository repository;
@Test
public void testPutAndGet() throws InterruptedException {
final Duration TIME_TO_RUN = Duration.of(1, ChronoUnit.MINUTES);
final int threadInternalTimeMilli = 200;
final List<String> dataStrings = List.of("TEST0001", "TEST0002");
// Start the consumer thread
getConsumerThread(dataStrings, threadInternalTimeMilli).start();
// Producer thread
getProducerThread(dataStrings, threadInternalTimeMilli).start();
// Wait until finishing...
Instant endTime = Instant.now().plusSeconds(TIME_TO_RUN.getSeconds());
while (Instant.now().isBefore(endTime)) {
Thread.sleep(100);
}
}
private Thread getConsumerThread(List<String> serials, int threadInternalTimeMilli) {
return new Thread(() -> {
LOGGER.info("Starting consumer thread...");
while (true) {
List<String> foundStrings = StreamSupport.stream(repository.findAllById(serials).spliterator(), false).map(DataBean::getData).collect(Collectors.toList());
List<String> notFoundStrings = serials.stream().filter(sn -> !foundStrings.contains(sn)).collect(Collectors.toList());
if (CollectionUtils.nonEmpty(notFoundStrings)) {
LOGGER.error("Reading ERROR > Not found strings ({})", notFoundStrings.stream().collect(Collectors.joining(",")));
} else {
LOGGER.info("Reading OK (All were found)");
}
// Thread sleep time
try {
Thread.sleep(threadInternalTimeMilli);
} catch (InterruptedException e) {
throw new IllegalStateException();
}
}
});
}
private Thread getProducerThread(List<String> serials, int threadInternalTimeMilli) {
return new Thread(() -> {
LOGGER.info("Starting producer thread...");
while (true) {
serials.stream().forEach(data -> {
LOGGER.info("Saving data: '{}'", data);
repository.save(new DataBean(data));
});
// Thread sleep time
try {
Thread.sleep(threadInternalTimeMilli);
} catch (InterruptedException e) {
throw new IllegalStateException();
}
}
});
}
}
When I run the program I get:
2021-09-10 11:31:33.595 INFO 61439 --- [ Thread-2] : Starting consumer thread...
2021-09-10 11:31:33.596 INFO 61439 --- [ Thread-3] : Starting producer thread...
2021-09-10 11:31:33.596 INFO 61439 --- [ Thread-3] : Saving data: 'TEST0001'
2021-09-10 11:31:34.505 INFO 61439 --- [ Thread-3] : Saving data: 'TEST0002'
2021-09-10 11:31:34.506 INFO 61439 --- [ Thread-2] : Reading OK (All were found)
2021-09-10 11:31:35.295 INFO 61439 --- [ Thread-3] : Saving data: 'TEST0001'
2021-09-10 11:31:35.297 INFO 61439 --- [ Thread-2] : Reading OK (All were found)
2021-09-10 11:31:35.884 INFO 61439 --- [ Thread-3] : Saving data: 'TEST0002'
2021-09-10 11:31:36.088 ERROR 61439 --- [ Thread-2] : Reading ERROR > Not found strings (TEST0001)
2021-09-10 11:31:36.681 INFO 61439 --- [ Thread-3] : Saving data: 'TEST0001'
2021-09-10 11:31:36.877 INFO 61439 --- [ Thread-2] : Reading OK (All were found)
2021-09-10 11:31:37.274 INFO 61439 --- [ Thread-3] : Saving data: 'TEST0002'
2021-09-10 11:31:37.660 ERROR 61439 --- [ Thread-2] : Reading ERROR > Not found strings (TEST0002)
2021-09-10 11:31:38.061 INFO 61439 --- [ Thread-3] : Saving data: 'TEST0001'
2021-09-10 11:31:38.447 INFO 61439 --- [ Thread-2] : Reading OK (All were found)
2021-09-10 11:31:38.649 INFO 61439 --- [ Thread-3] : Saving data: 'TEST0002'
2021-09-10 11:31:39.233 INFO 61439 --- [ Thread-2] : Reading OK (All were found)
2021-09-10 11:31:39.433 INFO 61439 --- [ Thread-3] : Saving data: 'TEST0001'
2021-09-10 11:31:40.035 INFO 61439 --- [ Thread-3] : Saving data: 'TEST0002'
2021-09-10 11:31:40.037 ERROR 61439 --- [ Thread-2] : Reading ERROR > Not found strings (TEST0001)
Thank you, Regards