Skip to content

Commit

Permalink
INTERNAL: add builder and transaction for ArcusCacheManager
Browse files Browse the repository at this point in the history
  • Loading branch information
oliviarla committed Aug 23, 2024
1 parent 1d0d5ec commit dd073da
Show file tree
Hide file tree
Showing 3 changed files with 189 additions and 2 deletions.
18 changes: 18 additions & 0 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,24 @@
</exclusions>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-support</artifactId>
<version>${spring.version}</version>
<exclusions>
<!-- Exclude Commons Logging in favor of SLF4j -->
<exclusion>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
</exclusion>
</exclusions>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,22 +19,27 @@

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;

import net.spy.memcached.ArcusClient;
import net.spy.memcached.ArcusClientPool;
import net.spy.memcached.ConnectionFactoryBuilder;

import org.springframework.beans.factory.DisposableBean;
import org.springframework.cache.Cache;
import org.springframework.cache.support.AbstractCacheManager;
import org.springframework.cache.transaction.AbstractTransactionSupportingCacheManager;
import org.springframework.util.Assert;

/**
* 스프링 CacheManager의 Arcus 구현체.
* 미리 정의하지 않은 이름의 캐시에 대해 get 요청을 받으면 (SimpleCacheManager와 다르게) 기본 설정으로 새 캐시를 생성하고 저장합니다.
*/
public class ArcusCacheManager extends AbstractCacheManager implements DisposableBean {
public class ArcusCacheManager extends AbstractTransactionSupportingCacheManager implements DisposableBean {
private final ArcusClientPool client;
protected ArcusCacheConfiguration defaultConfiguration;
protected Map<String, ArcusCacheConfiguration> initialCacheConfigs;
Expand Down Expand Up @@ -85,6 +90,17 @@ public ArcusCacheManager(
this.internalClient = true;
}

public static ArcusCacheManagerBuilder builder(ArcusClientPool arcusClientPool) {
return new ArcusCacheManagerBuilder(arcusClientPool);
}

public static ArcusCacheManagerBuilder builder(String adminAddress,
String serviceCode,
ConnectionFactoryBuilder connectionFactoryBuilder,
int poolSize) {
return new ArcusCacheManagerBuilder(adminAddress, serviceCode, connectionFactoryBuilder, poolSize);
}

@Override
protected Collection<? extends Cache> loadCaches() {
List<Cache> caches = new ArrayList<Cache>(initialCacheConfigs.size());
Expand Down Expand Up @@ -117,4 +133,82 @@ public void destroy() {
client.shutdown();
}
}

public static class ArcusCacheManagerBuilder {
private final ArcusClientPool arcusClientPool;
private final boolean internalClient;
private final Map<String, ArcusCacheConfiguration> initialCaches = new LinkedHashMap<>();
private boolean enableTransactions;
private ArcusCacheConfiguration defaultConfiguration = new ArcusCacheConfiguration();

private ArcusCacheManagerBuilder(ArcusClientPool arcusClientPool) {
this.arcusClientPool = arcusClientPool;
this.internalClient = false;
}

private ArcusCacheManagerBuilder(String adminAddress,
String serviceCode,
ConnectionFactoryBuilder connectionFactoryBuilder,
int poolSize) {
this.arcusClientPool = ArcusClient.createArcusClientPool(
adminAddress, serviceCode, connectionFactoryBuilder, poolSize);
this.internalClient = true;
}

public ArcusCacheManagerBuilder cacheDefaults(ArcusCacheConfiguration defaultCacheConfiguration) {
this.defaultConfiguration = defaultCacheConfiguration;
return this;
}

public ArcusCacheManagerBuilder initialCacheNames(Set<String> cacheNames) {
Assert.notNull(cacheNames, "Cache names must not be null");

cacheNames.forEach(cacheName -> initialCaches.put(cacheName, defaultConfiguration));
return this;
}

public ArcusCacheManagerBuilder transactionAware() {
this.enableTransactions = true;
return this;
}

public ArcusCacheManagerBuilder withCacheConfiguration(String cacheName, ArcusCacheConfiguration cacheConfiguration) {
Assert.notNull(cacheName, "Cache name must not be null");
Assert.notNull(cacheConfiguration, "Cache configuration must not be null");

this.initialCaches.put(cacheName, cacheConfiguration);
return this;
}

public ArcusCacheManagerBuilder withInitialCacheConfigurations(Map<String, ArcusCacheConfiguration> cacheConfigurations) {
Assert.notNull(cacheConfigurations, "Cache configurations must not be null");

this.initialCaches.putAll(cacheConfigurations);
return this;
}

public Optional<ArcusCacheConfiguration> getCacheConfigurationFor(String cacheName) {
return Optional.ofNullable(this.initialCaches.get(cacheName));
}

public Set<String> getConfiguredCaches() {
return Collections.unmodifiableSet(this.initialCaches.keySet());
}

/**
* Create new instance of {@link ArcusCacheManager} with configuration options applied.
*
* @return new instance of {@link ArcusCacheManager}.
*/
public ArcusCacheManager build() {
Assert.state(arcusClientPool != null, "ArcusClient must not be null");

ArcusCacheManager cacheManager = new ArcusCacheManager(arcusClientPool, defaultConfiguration, initialCaches);
cacheManager.internalClient = this.internalClient;
cacheManager.setTransactionAware(this.enableTransactions);

return cacheManager;
}

}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
package com.navercorp.arcus.spring.cache;

import java.util.Collections;

import net.spy.memcached.ArcusClient;
import net.spy.memcached.ArcusClientPool;

import org.junit.jupiter.api.Test;

import org.springframework.cache.Cache;
import org.springframework.cache.transaction.TransactionAwareCacheDecorator;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertInstanceOf;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.assertThrows;

class ArcusCacheManagerBuilderTest {

private final ArcusClientPool arcusClientPool = ArcusClient.createArcusClientPool("localhost:2181", "test", 4);

@Test
void testMissingCacheMadeByDefaultCacheConfig() {
ArcusCacheConfiguration configuration = new ArcusCacheConfiguration();
configuration.setServiceId("TEST-");
ArcusCacheManager cm = ArcusCacheManager.builder(arcusClientPool).cacheDefaults(configuration).build();
cm.afterPropertiesSet();

ArcusCache missingCache = (ArcusCache) cm.getMissingCache("new-cache");
assertNotNull(missingCache);
assertEquals(configuration.getServiceId(), missingCache.getServiceId());
assertEquals(configuration.getPrefix(), missingCache.getPrefix());
assertEquals(configuration.getExpireSeconds(), missingCache.getExpireSeconds());
}

@Test
void testSettingDifferentDefaultCacheConfiguration() {
ArcusCacheConfiguration withPrefix = new ArcusCacheConfiguration();
withPrefix.setPrefix("prefix");
ArcusCacheConfiguration withoutPrefix = new ArcusCacheConfiguration();

ArcusCacheManager cm = ArcusCacheManager.builder(arcusClientPool)
.cacheDefaults(withPrefix)
.initialCacheNames(Collections.singleton("first-cache"))
.cacheDefaults(withoutPrefix)
.initialCacheNames(Collections.singleton("second-cache"))
.build();

cm.afterPropertiesSet();

ArcusCache firstCache = (ArcusCache) cm.getCache("first-cache");
assertNotNull(firstCache);
assertEquals(withPrefix.getPrefix(), firstCache.getPrefix());
ArcusCache secondCache = (ArcusCache) cm.getCache("second-cache");
assertNotNull(secondCache);
assertNull(withoutPrefix.getPrefix());
assertEquals(withoutPrefix.getPrefix(), secondCache.getPrefix());
}

@Test
void testTransactionAwareCacheManager() {
Cache cache = ArcusCacheManager.builder(arcusClientPool)
.transactionAware()
.build()
.getCache("decorated-cache");

assertInstanceOf(TransactionAwareCacheDecorator.class, cache);
}

@Test
void testArcusClientNull() {
assertThrows(IllegalStateException.class, () -> ArcusCacheManager.builder(null).build());
}
}

0 comments on commit dd073da

Please sign in to comment.