Skip to content

Commit

Permalink
use hash code of key class when its object empty, add tests
Browse files Browse the repository at this point in the history
  • Loading branch information
agrgr committed Jun 13, 2024
1 parent 3e301e6 commit c8c970c
Show file tree
Hide file tree
Showing 2 changed files with 120 additions and 4 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
import com.aerospike.client.policy.RecordExistsAction;
import com.aerospike.client.policy.WritePolicy;
import org.springframework.cache.Cache;
import org.springframework.cache.interceptor.SimpleKey;
import org.springframework.cache.support.SimpleValueWrapper;
import org.springframework.data.aerospike.convert.AerospikeConverter;
import org.springframework.data.aerospike.convert.AerospikeReadData;
Expand Down Expand Up @@ -196,7 +197,10 @@ public ValueWrapper putIfAbsent(Object key, Object value) {
}

private Key getKey(Object key) {
return new Key(cacheConfiguration.getNamespace(), cacheConfiguration.getSet(), key.hashCode());
int userKey = key.hashCode();
// when no arguments are given return hash code of key's class (hash code of key itself can be equal to 1)
if (key instanceof SimpleKey && key.equals(SimpleKey.EMPTY)) userKey = key.getClass().hashCode();
return new Key(cacheConfiguration.getNamespace(), cacheConfiguration.getSet(), userKey);
}

private void serializeAndPut(WritePolicy writePolicy, Object key, Object value) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
import java.util.Map;

import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
import static org.springframework.data.aerospike.util.AwaitilityUtils.awaitTenSecondsUntil;

public class AerospikeCacheManagerIntegrationTests extends BaseBlockingIntegrationTests {
Expand All @@ -54,18 +55,22 @@ public class AerospikeCacheManagerIntegrationTests extends BaseBlockingIntegrati
AerospikeCacheManager aerospikeCacheManager;

@BeforeEach
public void setup() {
public void setup() throws NoSuchMethodException {
cachingComponent.reset();
deleteRecords();
}

private void deleteRecords() {
private void deleteRecords() throws NoSuchMethodException {
List<Integer> hashCodes = List.of(
STRING_PARAM.hashCode(),
STRING_PARAM_THAT_MATCHES_CONDITION.hashCode(),
Long.hashCode(NUMERIC_PARAM),
MAP_PARAM.hashCode(),
new SimpleKey(STRING_PARAM, NUMERIC_PARAM, MAP_PARAM).hashCode());
new SimpleKey(STRING_PARAM, NUMERIC_PARAM, MAP_PARAM).hashCode(),
SimpleKey.class.hashCode(),
CachingComponent.class.hashCode(),
CachingComponent.class.getMethod("cacheableMethodWithMethodNameKey").hashCode()
);
for (int hash : hashCodes) {
client.delete(null, new Key(getNameSpace(), DEFAULT_SET_NAME, hash));
}
Expand Down Expand Up @@ -126,6 +131,73 @@ public void shouldCacheWithMultipleParams() {
assertThat(cachingComponent.getNoOfCalls()).isEqualTo(1);
}

@Test
public void shouldCacheWithNthParam() {
assertThat(aerospikeOperations.count(DEFAULT_SET_NAME)).isEqualTo(0);
CachedObject response1 = cachingComponent.cacheableMethodWithNthParam(STRING_PARAM, NUMERIC_PARAM,
MAP_PARAM);
CachedObject response2 = cachingComponent.cacheableMethodWithNthParam(STRING_PARAM, NUMERIC_PARAM,
MAP_PARAM);

assertThat(response1).isNotNull();
assertThat(response1.getValue()).isEqualTo(VALUE);
assertThat(response2).isNotNull();
assertThat(response2.getValue()).isEqualTo(VALUE);
assertThat(cachingComponent.getNoOfCalls()).isEqualTo(1);
}

@Test
public void shouldCacheWithNoParams() {
assertThat(aerospikeOperations.count(DEFAULT_SET_NAME)).isEqualTo(0);
CachedObject response1 = cachingComponent.cacheableMethodWithNoParams();
CachedObject response2 = cachingComponent.cacheableMethodWithNoParams();

assertThat(response1).isNotNull();
assertThat(response1.getValue()).isEqualTo(VALUE);
assertThat(response2).isNotNull();
assertThat(response2.getValue()).isEqualTo(VALUE);
assertThat(cachingComponent.getNoOfCalls()).isEqualTo(1);
}

@Test
public void shouldNotCacheDifferentMethodsWithNoParamsByDefault_NegativeTest() {
assertThat(aerospikeOperations.count(DEFAULT_SET_NAME)).isEqualTo(0);
CachedObject response1 = cachingComponent.cacheableMethodWithNoParams();
assertThat(cachingComponent.getNoOfCalls()).isEqualTo(1);

// any two methods with no arguments will have the same cache key by default,
// to change it set the necessary Cacheable annotation parameters (e.g. "key")
assertThatThrownBy(() -> cachingComponent.anotherMethodWithNoParams())
.isInstanceOf(ClassCastException.class)
.hasMessageMatching(".+CachedObject cannot be cast to class .+AnotherCachedObject.*");
}

@Test
public void shouldCacheWithTargetClassKey() {
assertThat(aerospikeOperations.count(DEFAULT_SET_NAME)).isEqualTo(0);
CachedObject response1 = cachingComponent.cacheableMethodWithTargetClassKey();
CachedObject response2 = cachingComponent.cacheableMethodWithTargetClassKey();

assertThat(response1).isNotNull();
assertThat(response1.getValue()).isEqualTo(VALUE);
assertThat(response2).isNotNull();
assertThat(response2.getValue()).isEqualTo(VALUE);
assertThat(cachingComponent.getNoOfCalls()).isEqualTo(1);
}

@Test
public void shouldCacheWithMethodNameKey() {
assertThat(aerospikeOperations.count(DEFAULT_SET_NAME)).isEqualTo(0);
CachedObject response1 = cachingComponent.cacheableMethodWithMethodNameKey();
CachedObject response2 = cachingComponent.cacheableMethodWithMethodNameKey();

assertThat(response1).isNotNull();
assertThat(response1.getValue()).isEqualTo(VALUE);
assertThat(response2).isNotNull();
assertThat(response2.getValue()).isEqualTo(VALUE);
assertThat(cachingComponent.getNoOfCalls()).isEqualTo(1);
}

@Test
public void shouldCacheUsingDefaultSet() {
assertThat(aerospikeOperations.count(DEFAULT_SET_NAME)).isEqualTo(0);
Expand Down Expand Up @@ -317,6 +389,36 @@ public CachedObject cacheableMethodWithMultipleParams(String param1, long param2
return new CachedObject(VALUE);
}

@Cacheable(value = "TEST", key = "#root.args[1]")
public CachedObject cacheableMethodWithNthParam(String param1, long param2, Map<String, String> param3) {
noOfCalls++;
return new CachedObject(VALUE);
}

@Cacheable("TEST")
public CachedObject cacheableMethodWithNoParams() {
noOfCalls++;
return new CachedObject(VALUE);
}

@Cacheable("TEST")
public AnotherCachedObject anotherMethodWithNoParams() {
noOfCalls++;
return new AnotherCachedObject(NUMERIC_PARAM);
}

@Cacheable(value = "TEST", key = "#root.targetClass")
public CachedObject cacheableMethodWithTargetClassKey() {
noOfCalls++;
return new CachedObject(VALUE);
}

@Cacheable(value = "TEST", key = "#root.method")
public CachedObject cacheableMethodWithMethodNameKey() {
noOfCalls++;
return new CachedObject(VALUE);
}

@Cacheable("TEST12345ABC") // Cache name not pre-configured in AerospikeCacheManager, so it goes to default set
public CachedObject cacheableMethodDefaultCache(String param) {
noOfCalls++;
Expand Down Expand Up @@ -371,4 +473,14 @@ public Object getValue() {
return value;
}
}

@AllArgsConstructor
public static class AnotherCachedObject {

private final long number;

public long getNumber() {
return number;
}
}
}

0 comments on commit c8c970c

Please sign in to comment.