diff --git a/pom.xml b/pom.xml index a5d3a47ec6..8b64370059 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ org.springframework.data spring-data-redis - 2.5.0-SNAPSHOT + 2.5.0-DATAREDIS-1955-SNAPSHOT Spring Data Redis diff --git a/src/main/java/org/springframework/data/redis/core/RedisKeyValueAdapter.java b/src/main/java/org/springframework/data/redis/core/RedisKeyValueAdapter.java index 832231c667..28d9c8e204 100644 --- a/src/main/java/org/springframework/data/redis/core/RedisKeyValueAdapter.java +++ b/src/main/java/org/springframework/data/redis/core/RedisKeyValueAdapter.java @@ -99,6 +99,7 @@ * * @author Christoph Strobl * @author Mark Paluch + * @author Andrey Muchnik * @since 1.7 */ public class RedisKeyValueAdapter extends AbstractKeyValueAdapter @@ -248,6 +249,11 @@ public Object put(Object id, Object item, String keyspace) { } } + boolean isNoExpire = rdo.getTimeToLive() == null || rdo.getTimeToLive() != null && rdo.getTimeToLive() < 0; + if (isNoExpire && !isNew && keepShadowCopy()){ + connection.del(ByteUtils.concat(objectKey, BinaryKeyspaceIdentifier.PHANTOM_SUFFIX)); + } + connection.sAdd(toBytes(rdo.getKeyspace()), key); IndexWriter indexWriter = new IndexWriter(connection, converter); @@ -492,7 +498,7 @@ public void update(PartialUpdate update) { } else { connection.persist(redisKey); - connection.persist(ByteUtils.concat(redisKey, BinaryKeyspaceIdentifier.PHANTOM_SUFFIX)); + connection.del(ByteUtils.concat(redisKey, BinaryKeyspaceIdentifier.PHANTOM_SUFFIX)); } } diff --git a/src/test/java/org/springframework/data/redis/core/RedisKeyValueAdapterTests.java b/src/test/java/org/springframework/data/redis/core/RedisKeyValueAdapterTests.java index 48330cd237..60903098b6 100644 --- a/src/test/java/org/springframework/data/redis/core/RedisKeyValueAdapterTests.java +++ b/src/test/java/org/springframework/data/redis/core/RedisKeyValueAdapterTests.java @@ -54,6 +54,7 @@ * * @author Christoph Strobl * @author Mark Paluch + * @author Andrey Muchnik */ @ExtendWith(LettuceConnectionFactoryExtension.class) public class RedisKeyValueAdapterTests { @@ -705,6 +706,41 @@ void phantomKeyInsertedOnPutWhenShadowCopyIsInDefaultAndKeyspaceNotificationEnab assertThat(template.hasKey("persons:1:phantom")).isTrue(); } + @Test // DATAREDIS-1955 + void phantomKeyIsDeletedWhenPutWithNegativeTimeToLiveAndOldEntryTimeToLiveWasPositiveAndWhenShadowCopyIsTurnedOn() { + ExpiringPerson rand = new ExpiringPerson(); + rand.id = "1"; + rand.ttl = 3000L; + + adapter.put("1", rand, "persons"); + + assertThat(template.getExpire("persons:1:phantom")).isPositive(); + + rand.ttl = -1L; + + adapter.put("1", rand, "persons"); + + assertThat(template.hasKey("persons:1:phantom")).isFalse(); + } + + @Test // DATAREDIS-1955 + void updateWithRefreshTtlAndWithoutPositiveTtlShouldDeletePhantomKey() { + ExpiringPerson person = new ExpiringPerson(); + person.id = "1"; + person.ttl = 100L; + + adapter.put("1", person, "persons"); + + assertThat(template.getExpire("persons:1:phantom")).isPositive(); + + PartialUpdate update = new PartialUpdate<>("1", ExpiringPerson.class) // + .refreshTtl(true); + + adapter.update(update); + + assertThat(template.hasKey("persons:1:phantom")).isFalse(); + } + /** * Wait up to 5 seconds until {@code key} is no longer available in Redis. *