Skip to content

Commit 0072b5d

Browse files
committed
docs
1 parent 10e11e5 commit 0072b5d

File tree

7 files changed

+70
-16
lines changed

7 files changed

+70
-16
lines changed

caffein-bounded-cache-support/src/main/java/io/javaoperatorsdk/operator/processing/event/source/cache/CaffeinBoundedItemStores.java

Lines changed: 18 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -8,20 +8,28 @@
88
import com.github.benmanes.caffeine.cache.Cache;
99
import com.github.benmanes.caffeine.cache.Caffeine;
1010

11+
/**
12+
* The idea about CaffeinBoundedItemStore-s is that, caffeine will cache the resources which were
13+
* recently used, and will evict resource, which are not used for a while. This is ideal from the
14+
* perspective that on startup controllers reconcile all resources (this is why a maxSize not ideal)
15+
* but after a while it can happen (well depending on the controller and domain) that only some
16+
* resources are actually active, thus related events happen. So in case large amount of custom
17+
* resources only the active once will remain in the cache. Note that if a resource is reconciled
18+
* all the secondary resources are usually reconciled too, in that case all those resources are
19+
* fetched and populated to the cache, and will remain there for some time, for a subsequent
20+
* reconciliations.
21+
*/
1122
public class CaffeinBoundedItemStores {
1223

1324
private CaffeinBoundedItemStores() {}
1425

15-
public static <R extends HasMetadata> BoundedItemStore<R> boundedItemStore(
16-
KubernetesClient client, Class<R> rClass,
17-
Duration accessExpireDuration, long cacheMaxSize) {
18-
Cache<String, R> cache = Caffeine.newBuilder()
19-
.expireAfterAccess(accessExpireDuration)
20-
.maximumSize(cacheMaxSize)
21-
.build();
22-
return boundedItemStore(client, rClass, cache);
23-
}
24-
26+
/**
27+
* @param client Kubernetes Client
28+
* @param rClass resource class
29+
* @param accessExpireDuration the duration after resources is evicted from cache if not accessed.
30+
* @return the ItemStore implementation
31+
* @param <R> resource type
32+
*/
2533
public static <R extends HasMetadata> BoundedItemStore<R> boundedItemStore(
2634
KubernetesClient client, Class<R> rClass,
2735
Duration accessExpireDuration) {

caffein-bounded-cache-support/src/test/java/io/javaoperatorsdk/operator/processing/event/source/cache/BoundedCacheTestBase.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,4 +83,6 @@ private void createTestResources() {
8383

8484
abstract LocallyRunOperatorExtension extension();
8585

86+
87+
8688
}

caffein-bounded-cache-support/src/test/java/io/javaoperatorsdk/operator/processing/event/source/cache/CaffeinBoundedCacheClusterScopeIT.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,14 +11,16 @@
1111
import io.javaoperatorsdk.operator.processing.event.source.cache.sample.clusterscope.BoundedCacheClusterScopeTestReconciler;
1212
import io.javaoperatorsdk.operator.processing.event.source.cache.sample.namespacescope.BoundedCacheTestSpec;
1313

14+
import static io.javaoperatorsdk.operator.processing.event.source.cache.sample.AbstractTestReconciler.boundedItemStore;
15+
1416
public class CaffeinBoundedCacheClusterScopeIT
1517
extends BoundedCacheTestBase<BoundedCacheClusterScopeTestCustomResource> {
1618

1719
@RegisterExtension
1820
LocallyRunOperatorExtension extension =
1921
LocallyRunOperatorExtension.builder()
2022
.withReconciler(new BoundedCacheClusterScopeTestReconciler(), o -> {
21-
o.withItemStore(CaffeinBoundedItemStores.boundedItemStore(
23+
o.withItemStore(boundedItemStore(
2224
new KubernetesClientBuilder().build(),
2325
BoundedCacheClusterScopeTestCustomResource.class,
2426
Duration.ofMinutes(1),

caffein-bounded-cache-support/src/test/java/io/javaoperatorsdk/operator/processing/event/source/cache/CaffeinBoundedCacheNamespacedIT.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,14 @@
1111
import io.javaoperatorsdk.operator.processing.event.source.cache.sample.namespacescope.BoundedCacheTestReconciler;
1212
import io.javaoperatorsdk.operator.processing.event.source.cache.sample.namespacescope.BoundedCacheTestSpec;
1313

14+
import static io.javaoperatorsdk.operator.processing.event.source.cache.sample.AbstractTestReconciler.boundedItemStore;
15+
1416
class CaffeinBoundedCacheNamespacedIT extends BoundedCacheTestBase<BoundedCacheTestCustomResource> {
1517

1618
@RegisterExtension
1719
LocallyRunOperatorExtension extension =
1820
LocallyRunOperatorExtension.builder().withReconciler(new BoundedCacheTestReconciler(), o -> {
19-
o.withItemStore(CaffeinBoundedItemStores.boundedItemStore(
21+
o.withItemStore(boundedItemStore(
2022
new KubernetesClientBuilder().build(), BoundedCacheTestCustomResource.class,
2123
Duration.ofMinutes(1),
2224
1));

caffein-bounded-cache-support/src/test/java/io/javaoperatorsdk/operator/processing/event/source/cache/sample/AbstractTestReconciler.java

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,13 +15,17 @@
1515
import io.javaoperatorsdk.operator.api.reconciler.Context;
1616
import io.javaoperatorsdk.operator.junit.KubernetesClientAware;
1717
import io.javaoperatorsdk.operator.processing.event.source.EventSource;
18+
import io.javaoperatorsdk.operator.processing.event.source.cache.BoundedItemStore;
1819
import io.javaoperatorsdk.operator.processing.event.source.cache.CaffeinBoundedItemStores;
1920
import io.javaoperatorsdk.operator.processing.event.source.cache.sample.clusterscope.BoundedCacheClusterScopeTestReconciler;
2021
import io.javaoperatorsdk.operator.processing.event.source.cache.sample.namespacescope.BoundedCacheTestSpec;
2122
import io.javaoperatorsdk.operator.processing.event.source.cache.sample.namespacescope.BoundedCacheTestStatus;
2223
import io.javaoperatorsdk.operator.processing.event.source.informer.InformerEventSource;
2324
import io.javaoperatorsdk.operator.processing.event.source.informer.Mappers;
2425

26+
import com.github.benmanes.caffeine.cache.Cache;
27+
import com.github.benmanes.caffeine.cache.Caffeine;
28+
2529
public abstract class AbstractTestReconciler<P extends CustomResource<BoundedCacheTestSpec, BoundedCacheTestStatus>>
2630
implements KubernetesClientAware, Reconciler<P>,
2731
EventSourceInitializer<P> {
@@ -82,9 +86,8 @@ public Map<String, EventSource> prepareEventSources(
8286
EventSourceContext<P> context) {
8387

8488
var boundedItemStore =
85-
CaffeinBoundedItemStores.boundedItemStore(new KubernetesClientBuilder().build(),
86-
ConfigMap.class, Duration.ofMinutes(1),
87-
1);
89+
boundedItemStore(new KubernetesClientBuilder().build(),
90+
ConfigMap.class, Duration.ofMinutes(1), 1); // setting max size for testing purposes
8891

8992
var es = new InformerEventSource<>(InformerConfiguration.from(ConfigMap.class, context)
9093
.withItemStore(boundedItemStore)
@@ -101,4 +104,15 @@ private void ensureStatus(P resource) {
101104
}
102105
}
103106

107+
public static <R extends HasMetadata> BoundedItemStore<R> boundedItemStore(
108+
KubernetesClient client, Class<R> rClass,
109+
Duration accessExpireDuration,
110+
// max size is only for testing purposes
111+
long cacheMaxSize) {
112+
Cache<String, R> cache = Caffeine.newBuilder()
113+
.expireAfterAccess(accessExpireDuration)
114+
.maximumSize(cacheMaxSize)
115+
.build();
116+
return CaffeinBoundedItemStores.boundedItemStore(client, rClass, cache);
117+
}
104118
}

docs/documentation/features.md

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -739,3 +739,29 @@ with a `mycrs` plural form will result in 2 files:
739739
**NOTE:**
740740
> Quarkus users using the `quarkus-operator-sdk` extension do not need to add any extra dependency
741741
> to get their CRD generated as this is handled by the extension itself.
742+
743+
## Optimizing Caches
744+
745+
One of the ideas around operator pattern, is that all the relevant resources are cached, thus reconciliation is usually
746+
very fast (especially if it does not need to update resources) since it's mostly working with in memory state.
747+
However or large clusters, caching huge amount of primary and secondary resources might consume lots of memory.
748+
There are some semi-experimental (experimental in terms that it works, but we need feedback from real production usage)
749+
features to optimize memory usage of controllers.
750+
751+
### Bounded Caches for Informers
752+
753+
Limiting caches for informers - thus for Kubernetes resources, both controllers primary resource - is supported for now.
754+
The idea with the implementation that is provided, is that resources are in the cache for a limited time.
755+
So for use cases, when a resource is only frequently reconciled when it is created, and later no or
756+
occasionally reconciled, will be evicted from the cache, since the resources are not accessed.
757+
If a resource accessed in the future but not in the cache, the bounded cache implementation will fetch it from
758+
the service if needed.
759+
Note that on start of a controller all the resources are reconciled, for this reason explicitly setting a maximal
760+
size of a cache might not be ideal. In other words it is desired to have all the resources in the cache at startup,
761+
but not later if not accessed.
762+
763+
See usage of the related implementation using Caffein cache in integration tests for [primary resource](https://github.com/java-operator-sdk/java-operator-sdk/blob/10e11e587447667ef0da1ddb29e0ba15fcd24ada/caffein-bounded-cache-support/src/test/java/io/javaoperatorsdk/operator/processing/event/source/cache/CaffeinBoundedCacheNamespacedIT.java#L19-L19) and for an [informer](https://github.com/java-operator-sdk/java-operator-sdk/blob/10e11e587447667ef0da1ddb29e0ba15fcd24ada/caffein-bounded-cache-support/src/test/java/io/javaoperatorsdk/operator/processing/event/source/cache/sample/AbstractTestReconciler.java#L84-L93).
764+
765+
See also [CaffeinBoundedItemStores](https://github.com/java-operator-sdk/java-operator-sdk/blob/10e11e587447667ef0da1ddb29e0ba15fcd24ada/caffein-bounded-cache-support/src/main/java/io/javaoperatorsdk/operator/processing/event/source/cache/CaffeinBoundedItemStores.java#L22-L22)
766+
767+

operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/cache/BoundedItemStore.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,7 @@ public static <R extends HasMetadata> Function<R, String> namespaceKeyFunc() {
114114
}
115115

116116
protected R refreshMissingStateFromServer(String key) {
117-
log.debug("Fetching resource from server");
117+
log.debug("Fetching resource from server for key: {}", key);
118118
var newRes = resourceFetcher.fetchResource(key);
119119
synchronized (this) {
120120
log.debug("Fetched resource: {}", newRes);

0 commit comments

Comments
 (0)