Skip to content

Commit e2a2837

Browse files
committed
feat: bounded cache (variant 2)
1 parent 559afa9 commit e2a2837

File tree

7 files changed

+217
-1
lines changed

7 files changed

+217
-1
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
package io.javaoperatorsdk.operator.processing.event.source.cache;
2+
3+
import java.util.Set;
4+
5+
// todo: rename to cache?
6+
public interface BoundedCache<K, R> {
7+
8+
R get(K key);
9+
10+
R remove(K key);
11+
12+
R put(K key, R object);
13+
14+
Set<K> keys();
15+
16+
Set<R> values();
17+
18+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
package io.javaoperatorsdk.operator.processing.event.source.cache;
2+
3+
import java.util.HashSet;
4+
import java.util.function.Function;
5+
import java.util.stream.Stream;
6+
7+
import io.fabric8.kubernetes.api.model.HasMetadata;
8+
import io.fabric8.kubernetes.client.informers.cache.ItemStore;
9+
10+
public class BoundedItemStore<R extends HasMetadata> extends BoundedStore<String, R>
11+
implements ItemStore<R> {
12+
13+
private final Function<R, String> keyFunction;
14+
15+
public BoundedItemStore(KubernetesResourceFetcher<R> resourceFetcher,
16+
BoundedCache<String, R> cache, Function<R, String> keyFunction) {
17+
super(resourceFetcher, cache);
18+
this.keyFunction = keyFunction;
19+
}
20+
21+
@Override
22+
public String getKey(R obj) {
23+
return keyFunction.apply(obj);
24+
}
25+
26+
@Override
27+
public Stream<String> keySet() {
28+
return super.keys();
29+
}
30+
31+
/** This is very inefficient but should not be called by the Informer or just */
32+
@Override
33+
public Stream<R> values() {
34+
var keys = cache.keys();
35+
var values = cache.values();
36+
var notPresentValueKeys = new HashSet<>(existingResources);
37+
notPresentValueKeys.retainAll(keys);
38+
var fetchedValues = notPresentValueKeys.stream().map(k -> fetchAndCacheResource(k));
39+
return Stream.concat(values.stream(), fetchedValues);
40+
}
41+
42+
@Override
43+
public int size() {
44+
return existingResources.size();
45+
}
46+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
package io.javaoperatorsdk.operator.processing.event.source.cache;
2+
3+
import java.util.Collections;
4+
import java.util.HashSet;
5+
import java.util.Set;
6+
import java.util.stream.Stream;
7+
8+
public class BoundedStore<K, R> {
9+
10+
protected final ResourceFetcher<K, R> resourceFetcher;
11+
protected final BoundedCache<K, R> cache;
12+
protected Set<K> existingResources = Collections.synchronizedSet(new HashSet<>());;
13+
14+
public BoundedStore(ResourceFetcher<K, R> resourceFetcher, BoundedCache<K, R> cache) {
15+
this.resourceFetcher = resourceFetcher;
16+
this.cache = cache;
17+
}
18+
19+
public R get(K key) {
20+
var res = cache.get(key);
21+
if (res != null) {
22+
return res;
23+
}
24+
if (!existingResources.contains(key)) {
25+
return null;
26+
} else {
27+
return fetchAndCacheResource(key);
28+
}
29+
}
30+
31+
public R remove(K key) {
32+
existingResources.remove(key);
33+
return cache.remove(key);
34+
}
35+
36+
public R put(K key, R object) {
37+
var res = cache.put(key, object);
38+
existingResources.add(key);
39+
return res;
40+
}
41+
42+
public Stream<K> keys() {
43+
return existingResources.stream();
44+
}
45+
46+
protected R fetchAndCacheResource(K key) {
47+
var newRes = resourceFetcher.fetchResource(key);
48+
cache.put(key, newRes);
49+
return newRes;
50+
}
51+
52+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
package io.javaoperatorsdk.operator.processing.event.source.cache;
2+
3+
import java.util.function.Function;
4+
5+
import io.fabric8.kubernetes.api.model.HasMetadata;
6+
import io.fabric8.kubernetes.client.KubernetesClient;
7+
import io.javaoperatorsdk.operator.processing.event.ResourceID;
8+
9+
public class KubernetesResourceFetcher<R extends HasMetadata>
10+
implements ResourceFetcher<String, R> {
11+
12+
private final Class<R> rClass;
13+
private final KubernetesClient client;
14+
private final Function<String, ResourceID> resourceIDFunction;
15+
16+
public KubernetesResourceFetcher(Class<R> rClass,
17+
KubernetesClient client,
18+
Function<String, ResourceID> resourceIDFunction) {
19+
this.rClass = rClass;
20+
this.client = client;
21+
this.resourceIDFunction = resourceIDFunction;
22+
}
23+
24+
@Override
25+
public R fetchResource(String key) {
26+
var resourceId = resourceIDFunction.apply(key);
27+
return resourceId.getNamespace().map(ns -> client.resources(rClass).inNamespace(ns)
28+
.withName(resourceId.getName()).get())
29+
.orElse(client.resources(rClass).withName(resourceId.getName()).get());
30+
31+
}
32+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
package io.javaoperatorsdk.operator.processing.event.source.cache;
2+
3+
public interface ResourceFetcher<K, R> {
4+
5+
R fetchResource(K key);
6+
7+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
package io.javaoperatorsdk.operator.processing.event.source.cache;
2+
3+
import org.junit.jupiter.api.Test;
4+
5+
import io.fabric8.kubernetes.api.model.ConfigMap;
6+
import io.fabric8.kubernetes.api.model.ObjectMetaBuilder;
7+
import io.javaoperatorsdk.operator.processing.event.ResourceID;
8+
9+
import static org.junit.jupiter.api.Assertions.*;
10+
import static org.mockito.ArgumentMatchers.any;
11+
import static org.mockito.Mockito.*;
12+
13+
class BoundedStoreTest {
14+
15+
ResourceFetcher<ResourceID, ConfigMap> resourceFetcher = mock(ResourceFetcher.class);
16+
BoundedCache<ResourceID, ConfigMap> boundedCache = mock(BoundedCache.class);
17+
18+
BoundedStore<ResourceID, ConfigMap> boundedStore =
19+
new BoundedStore<>(resourceFetcher, boundedCache);
20+
21+
@Test
22+
void storesValue() {
23+
boundedStore.put(ResourceID.fromResource(testValue1()), testValue1());
24+
25+
verify(boundedCache, times(1)).put(any(), any());
26+
}
27+
28+
29+
@Test
30+
void fetchesResourceIfNotPresentInCache() {
31+
32+
}
33+
34+
@Test
35+
void removesValueFromCache() {
36+
37+
}
38+
39+
@Test
40+
void listKeys() {
41+
42+
}
43+
44+
45+
ConfigMap testValue1() {
46+
return testValue("test1");
47+
}
48+
49+
ConfigMap testValue2() {
50+
return testValue("test2");
51+
}
52+
53+
ConfigMap testValue(String name) {
54+
var cm = new ConfigMap();
55+
cm.setMetadata(new ObjectMetaBuilder()
56+
.withName(name)
57+
.withNamespace("default")
58+
.build());
59+
return cm;
60+
}
61+
}
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
import static org.mockito.Mockito.mock;
1515
import static org.mockito.Mockito.when;
1616

17-
class TemporaryResourceCacheTest {
17+
class TemporaryResourceBoundedCacheTest {
1818

1919
public static final String RESOURCE_VERSION = "1";
2020
@SuppressWarnings("unchecked")

0 commit comments

Comments
 (0)