Skip to content

Commit cca1f7a

Browse files
christophstroblmp911de
authored andcommitted
DATAREDIS-1106 - Remove expiration phantom key on delete via KeyValueAdapter.
Original pull request: spring-projects#521.
1 parent 86b3f45 commit cca1f7a

File tree

5 files changed

+65
-5
lines changed

5 files changed

+65
-5
lines changed

src/main/java/org/springframework/data/redis/core/RedisKeyValueAdapter.java

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -329,8 +329,14 @@ public <T> T delete(Object id, String keyspace, Class<T> type) {
329329

330330
connection.del(keyToDelete);
331331
connection.sRem(binKeyspace, binId);
332-
333332
new IndexWriter(connection, converter).removeKeyFromIndexes(asString(keyspace), binId);
333+
334+
RedisPersistentEntity<?> persistentEntity = converter.getMappingContext().getPersistentEntity(type);
335+
if (persistentEntity != null && persistentEntity.isExpiring()) {
336+
337+
byte[] phantomKey = ByteUtils.concat(keyToDelete, BinaryKeyspaceIdentifier.PHANTOM_SUFFIX);
338+
connection.del(phantomKey);
339+
}
334340
return null;
335341
});
336342
}

src/main/java/org/springframework/data/redis/core/TimeToLiveAccessor.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,4 +31,11 @@ public interface TimeToLiveAccessor {
3131
*/
3232
@Nullable
3333
Long getTimeToLive(Object source);
34+
35+
/**
36+
* @param type must not be {@literal null}.
37+
* @return return {@literal true} if the entity could potentially expire.
38+
* @since ? (depends on backport)
39+
*/
40+
boolean isExpiringEntity(Class<?> type);
3441
}

src/main/java/org/springframework/data/redis/core/mapping/RedisMappingContext.java

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -206,10 +206,6 @@ static class ConfigAwareTimeToLiveAccessor implements TimeToLiveAccessor {
206206
this.mappingContext = mappingContext;
207207
}
208208

209-
/*
210-
* (non-Javadoc)
211-
* @see org.springframework.data.redis.core.TimeToLiveResolver#resolveTimeToLive(java.lang.Object)
212-
*/
213209
@Override
214210
@SuppressWarnings({ "rawtypes" })
215211
public Long getTimeToLive(final Object source) {
@@ -283,6 +279,24 @@ public Long getTimeToLive(final Object source) {
283279
return defaultTimeout;
284280
}
285281

282+
/*
283+
* (non-Javadoc)
284+
* @see org.springframework.data.redis.core.TimeToLiveResolver#isExpiringEntity(java.lang.Class)
285+
*/
286+
@Override
287+
public boolean isExpiringEntity(Class<?> type) {
288+
289+
Long defaultTimeOut = resolveDefaultTimeOut(type);
290+
291+
if (defaultTimeOut != null && defaultTimeOut > 0) {
292+
return true;
293+
}
294+
if (resolveTtlProperty(type) != null) {
295+
return true;
296+
}
297+
return resolveTimeMethod(type) != null;
298+
}
299+
286300
private Long resolveDefaultTimeOut(Class<?> type) {
287301

288302
if (this.defaultTimeouts.containsKey(type)) {

src/main/java/org/springframework/data/redis/core/mapping/RedisPersistentEntity.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,4 +52,11 @@ public interface RedisPersistentEntity<T> extends KeyValuePersistentEntity<T, Re
5252
@Nullable
5353
RedisPersistentProperty getExplicitTimeToLiveProperty();
5454

55+
/**
56+
* @return {@literal true} if the entity could potentially expire.
57+
* @since ? (depends on backport)
58+
*/
59+
default boolean isExpiring() {
60+
return getTimeToLiveAccessor().isExpiringEntity(getType());
61+
}
5562
}

src/test/java/org/springframework/data/redis/core/RedisKeyValueAdapterTests.java

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -297,6 +297,24 @@ public void deleteCleansIndexedDataCorrectly() {
297297
assertThat(template.opsForSet().members("persons:firstname:rand")).doesNotContain("1");
298298
}
299299

300+
@Test // DATAREDIS-1106
301+
public void deleteRemovesExpireHelperStructures() {
302+
303+
WithExpiration source = new WithExpiration();
304+
source.id = "1";
305+
source.value = "vale";
306+
307+
adapter.put("1", source, "withexpiration");
308+
309+
assertThat(template.hasKey("withexpiration:1")).isTrue();
310+
assertThat(template.hasKey("withexpiration:1:phantom")).isTrue();
311+
312+
adapter.delete("1", "withexpiration", WithExpiration.class);
313+
314+
assertThat(template.hasKey("withexpiration:1")).isFalse();
315+
assertThat(template.hasKey("withexpiration:1:phantom")).isFalse();
316+
}
317+
300318
@Test // DATAREDIS-425
301319
public void keyExpiredEventShouldRemoveHelperStructures() throws Exception {
302320

@@ -765,4 +783,12 @@ static class Location {
765783
String name;
766784
}
767785

786+
@KeySpace("withexpiration")
787+
@RedisHash(timeToLive = 30)
788+
static class WithExpiration {
789+
790+
@Id String id;
791+
String value;
792+
}
793+
768794
}

0 commit comments

Comments
 (0)