Skip to content

Commit 33f3305

Browse files
csvirimetacosm
andauthored
feat: cache object pruning (#1630)
Co-authored-by: Chris Laprun <[email protected]>
1 parent 1ea9e68 commit 33f3305

27 files changed

+611
-53
lines changed

docs/documentation/features.md

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -711,7 +711,29 @@ setting, where this flag usually needs to be set to false, in order to control t
711711
See also an example implementation in the
712712
[WebPage sample](https://github.com/java-operator-sdk/java-operator-sdk/blob/3e2e7c4c834ef1c409d636156b988125744ca911/sample-operators/webpage/src/main/java/io/javaoperatorsdk/operator/sample/WebPageOperator.java#L38-L43)
713713

714-
## Monitoring with Micrometer
714+
## Optimization of Caches
715+
716+
** Cache pruning is an experimental feature. Might a subject of change or even removal in the future. **
717+
718+
Operators using informers will initially cache the data for all known resources when starting up
719+
so that access to resources can be performed quickly. Consequently, the memory required for the
720+
operator to run and startup time will both increase quite dramatically when dealing with large
721+
clusters with numerous resources.
722+
723+
It is thus possible to configure the operator to cache only pruned versions of the resources to
724+
alleviate the memory usage of the primary and secondary caches. This setup, however, has
725+
implications on how reconcilers deal with resources since they will only work with partial
726+
objects. As a consequence, resources need to be updated using PATCH operations only, sending
727+
only required changes.
728+
729+
To see how to use, and how to handle related caveats regarding how to deal with pruned objects
730+
that leverage
731+
[server side apply](https://kubernetes.io/docs/reference/using-api/server-side-apply/) patches,
732+
please check the provided
733+
[integration test](https://github.com/java-operator-sdk/java-operator-sdk/blob/c688524e64205690ba15587e7ed96a64dc231430/operator-framework/src/test/java/io/javaoperatorsdk/operator/CachePruneIT.java)
734+
and associates reconciler.
735+
736+
Pruned caches are currently not supported with the Dependent Resources feature.
715737

716738
## Automatic Generation of CRDs
717739

operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/AnnotationControllerConfiguration.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
import java.util.Optional;
99
import java.util.Set;
1010
import java.util.function.Function;
11+
import java.util.function.UnaryOperator;
1112
import java.util.stream.Collectors;
1213

1314
import io.fabric8.kubernetes.api.model.HasMetadata;
@@ -83,6 +84,14 @@ public Set<String> getNamespaces() {
8384
DEFAULT_NAMESPACES_SET.toArray(String[]::new)));
8485
}
8586

87+
@Override
88+
@SuppressWarnings("unchecked")
89+
public Optional<UnaryOperator<P>> cachePruneFunction() {
90+
return Optional.ofNullable(
91+
Utils.instantiate(annotation.cachePruneFunction(), UnaryOperator.class,
92+
Utils.contextFor(this, null, null)));
93+
}
94+
8695
@Override
8796
@SuppressWarnings("unchecked")
8897
public Class<P> getResourceClass() {

operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/ControllerConfigurationOverrider.java

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
import java.util.List;
77
import java.util.Optional;
88
import java.util.Set;
9+
import java.util.function.UnaryOperator;
910
import java.util.stream.Collectors;
1011

1112
import io.fabric8.kubernetes.api.model.HasMetadata;
@@ -38,6 +39,7 @@ public class ControllerConfigurationOverrider<R extends HasMetadata> {
3839
private OnUpdateFilter<R> onUpdateFilter;
3940
private GenericFilter<R> genericFilter;
4041
private RateLimiter rateLimiter;
42+
private UnaryOperator<R> cachePruneFunction;
4143

4244
private ControllerConfigurationOverrider(ControllerConfiguration<R> original) {
4345
finalizer = original.getFinalizerName();
@@ -56,6 +58,7 @@ private ControllerConfigurationOverrider(ControllerConfiguration<R> original) {
5658
dependentResources.forEach(drs -> namedDependentResourceSpecs.put(drs.getName(), drs));
5759
this.original = original;
5860
this.rateLimiter = original.getRateLimiter();
61+
this.cachePruneFunction = original.cachePruneFunction().orElse(null);
5962
}
6063

6164
public ControllerConfigurationOverrider<R> withFinalizer(String finalizer) {
@@ -158,6 +161,12 @@ public ControllerConfigurationOverrider<R> withGenericFilter(GenericFilter<R> ge
158161
return this;
159162
}
160163

164+
public ControllerConfigurationOverrider<R> withCachePruneFunction(
165+
UnaryOperator<R> cachePruneFunction) {
166+
this.cachePruneFunction = cachePruneFunction;
167+
return this;
168+
}
169+
161170
@SuppressWarnings("unchecked")
162171
public ControllerConfigurationOverrider<R> replacingNamedDependentResourceConfig(String name,
163172
Object dependentResourceConfig) {
@@ -208,7 +217,7 @@ public ControllerConfiguration<R> build() {
208217
onUpdateFilter,
209218
genericFilter,
210219
rateLimiter,
211-
newDependentSpecs);
220+
newDependentSpecs, cachePruneFunction);
212221
}
213222

214223
public static <R extends HasMetadata> ControllerConfigurationOverrider<R> override(

operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/DefaultControllerConfiguration.java

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import java.util.List;
66
import java.util.Optional;
77
import java.util.Set;
8+
import java.util.function.UnaryOperator;
89

910
import io.fabric8.kubernetes.api.model.HasMetadata;
1011
import io.javaoperatorsdk.operator.api.config.dependent.DependentResourceSpec;
@@ -49,8 +50,10 @@ public DefaultControllerConfiguration(
4950
OnUpdateFilter<R> onUpdateFilter,
5051
GenericFilter<R> genericFilter,
5152
RateLimiter rateLimiter,
52-
List<DependentResourceSpec> dependents) {
53-
super(labelSelector, resourceClass, onAddFilter, onUpdateFilter, genericFilter, namespaces);
53+
List<DependentResourceSpec> dependents,
54+
UnaryOperator<R> cachePruneFunction) {
55+
super(labelSelector, resourceClass, onAddFilter, onUpdateFilter, genericFilter, namespaces,
56+
cachePruneFunction);
5457
this.associatedControllerClassName = associatedControllerClassName;
5558
this.name = name;
5659
this.crdName = crdName;
@@ -116,4 +119,5 @@ public Optional<Duration> maxReconciliationInterval() {
116119
public RateLimiter getRateLimiter() {
117120
return rateLimiter;
118121
}
122+
119123
}

operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/DefaultResourceConfiguration.java

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
import java.util.Optional;
44
import java.util.Set;
5+
import java.util.function.UnaryOperator;
56

67
import io.fabric8.kubernetes.api.model.HasMetadata;
78
import io.javaoperatorsdk.operator.processing.event.source.filter.GenericFilter;
@@ -19,18 +20,23 @@ public class DefaultResourceConfiguration<R extends HasMetadata>
1920
private final OnAddFilter<R> onAddFilter;
2021
private final OnUpdateFilter<R> onUpdateFilter;
2122
private final GenericFilter<R> genericFilter;
23+
private final UnaryOperator<R> cachePruneFunction;
2224

2325
public DefaultResourceConfiguration(String labelSelector, Class<R> resourceClass,
2426
OnAddFilter<R> onAddFilter,
2527
OnUpdateFilter<R> onUpdateFilter, GenericFilter<R> genericFilter, String... namespaces) {
2628
this(labelSelector, resourceClass, onAddFilter, onUpdateFilter, genericFilter,
2729
namespaces == null || namespaces.length == 0 ? DEFAULT_NAMESPACES_SET
28-
: Set.of(namespaces));
30+
: Set.of(namespaces),
31+
null);
2932
}
3033

3134
public DefaultResourceConfiguration(String labelSelector, Class<R> resourceClass,
3235
OnAddFilter<R> onAddFilter,
33-
OnUpdateFilter<R> onUpdateFilter, GenericFilter<R> genericFilter, Set<String> namespaces) {
36+
OnUpdateFilter<R> onUpdateFilter,
37+
GenericFilter<R> genericFilter,
38+
Set<String> namespaces,
39+
UnaryOperator<R> cachePruneFunction) {
3440
this.labelSelector = labelSelector;
3541
this.resourceClass = resourceClass;
3642
this.onAddFilter = onAddFilter;
@@ -39,6 +45,7 @@ public DefaultResourceConfiguration(String labelSelector, Class<R> resourceClass
3945
this.namespaces =
4046
namespaces == null || namespaces.isEmpty() ? DEFAULT_NAMESPACES_SET
4147
: namespaces;
48+
this.cachePruneFunction = cachePruneFunction;
4249
}
4350

4451
@Override
@@ -56,6 +63,11 @@ public Set<String> getNamespaces() {
5663
return namespaces;
5764
}
5865

66+
@Override
67+
public Optional<UnaryOperator<R>> cachePruneFunction() {
68+
return Optional.ofNullable(this.cachePruneFunction);
69+
}
70+
5971
@Override
6072
public Class<R> getResourceClass() {
6173
return resourceClass;

operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/ResourceConfiguration.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,13 @@
33
import java.util.Collections;
44
import java.util.Optional;
55
import java.util.Set;
6+
import java.util.function.UnaryOperator;
67

78
import io.fabric8.kubernetes.api.model.HasMetadata;
89
import io.javaoperatorsdk.operator.OperatorException;
910
import io.javaoperatorsdk.operator.ReconcilerUtils;
1011
import io.javaoperatorsdk.operator.api.reconciler.Constants;
12+
import io.javaoperatorsdk.operator.api.reconciler.ControllerConfiguration;
1113
import io.javaoperatorsdk.operator.processing.event.source.filter.GenericFilter;
1214
import io.javaoperatorsdk.operator.processing.event.source.filter.OnAddFilter;
1315
import io.javaoperatorsdk.operator.processing.event.source.filter.OnUpdateFilter;
@@ -108,4 +110,11 @@ default Set<String> getEffectiveNamespaces() {
108110
}
109111
return targetNamespaces;
110112
}
113+
114+
/**
115+
* See {@link ControllerConfiguration#cachePruneFunction()} for details.
116+
*/
117+
default Optional<UnaryOperator<R>> cachePruneFunction() {
118+
return Optional.empty();
119+
}
111120
}

operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/informer/InformerConfiguration.java

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import java.util.Objects;
44
import java.util.Optional;
55
import java.util.Set;
6+
import java.util.function.UnaryOperator;
67

78
import io.fabric8.kubernetes.api.model.HasMetadata;
89
import io.javaoperatorsdk.operator.api.config.DefaultResourceConfiguration;
@@ -29,6 +30,7 @@ class DefaultInformerConfiguration<R extends HasMetadata> extends
2930
private final SecondaryToPrimaryMapper<R> secondaryToPrimaryMapper;
3031
private final boolean followControllerNamespaceChanges;
3132
private final OnDeleteFilter<R> onDeleteFilter;
33+
private final UnaryOperator<R> cachePruneFunction;
3234

3335
protected DefaultInformerConfiguration(String labelSelector,
3436
Class<R> resourceClass,
@@ -38,15 +40,18 @@ protected DefaultInformerConfiguration(String labelSelector,
3840
OnAddFilter<R> onAddFilter,
3941
OnUpdateFilter<R> onUpdateFilter,
4042
OnDeleteFilter<R> onDeleteFilter,
41-
GenericFilter<R> genericFilter) {
42-
super(labelSelector, resourceClass, onAddFilter, onUpdateFilter, genericFilter, namespaces);
43+
GenericFilter<R> genericFilter,
44+
UnaryOperator<R> cachePruneFunction) {
45+
super(labelSelector, resourceClass, onAddFilter, onUpdateFilter, genericFilter, namespaces,
46+
cachePruneFunction);
4347
this.followControllerNamespaceChanges = followControllerNamespaceChanges;
4448

4549
this.primaryToSecondaryMapper = primaryToSecondaryMapper;
4650
this.secondaryToPrimaryMapper =
4751
Objects.requireNonNullElse(secondaryToPrimaryMapper,
4852
Mappers.fromOwnerReference());
4953
this.onDeleteFilter = onDeleteFilter;
54+
this.cachePruneFunction = cachePruneFunction;
5055
}
5156

5257
@Override
@@ -67,6 +72,11 @@ public Optional<OnDeleteFilter<R>> onDeleteFilter() {
6772
public <P extends HasMetadata> PrimaryToSecondaryMapper<P> getPrimaryToSecondaryMapper() {
6873
return (PrimaryToSecondaryMapper<P>) primaryToSecondaryMapper;
6974
}
75+
76+
@Override
77+
public Optional<UnaryOperator<R>> cachePruneFunction() {
78+
return Optional.ofNullable(this.cachePruneFunction);
79+
}
7080
}
7181

7282
/**
@@ -102,6 +112,7 @@ class InformerConfigurationBuilder<R extends HasMetadata> {
102112
private OnDeleteFilter<R> onDeleteFilter;
103113
private GenericFilter<R> genericFilter;
104114
private boolean inheritControllerNamespacesOnChange = false;
115+
private UnaryOperator<R> cachePruneFunction;
105116

106117
private InformerConfigurationBuilder(Class<R> resourceClass) {
107118
this.resourceClass = resourceClass;
@@ -202,12 +213,18 @@ public InformerConfigurationBuilder<R> withGenericFilter(GenericFilter<R> generi
202213
return this;
203214
}
204215

216+
public InformerConfigurationBuilder<R> withCachePruneFunction(
217+
UnaryOperator<R> cachePruneFunction) {
218+
this.cachePruneFunction = cachePruneFunction;
219+
return this;
220+
}
221+
205222
public InformerConfiguration<R> build() {
206223
return new DefaultInformerConfiguration<>(labelSelector, resourceClass,
207224
primaryToSecondaryMapper,
208225
secondaryToPrimaryMapper,
209226
namespaces, inheritControllerNamespacesOnChange, onAddFilter, onUpdateFilter,
210-
onDeleteFilter, genericFilter);
227+
onDeleteFilter, genericFilter, cachePruneFunction);
211228
}
212229
}
213230

operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/ControllerConfiguration.java

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import java.lang.annotation.Retention;
66
import java.lang.annotation.RetentionPolicy;
77
import java.lang.annotation.Target;
8+
import java.util.function.UnaryOperator;
89

910
import io.javaoperatorsdk.operator.api.reconciler.dependent.Dependent;
1011
import io.javaoperatorsdk.operator.processing.event.rate.LinearRateLimiter;
@@ -118,4 +119,25 @@ MaxReconciliationInterval maxReconciliationInterval() default @MaxReconciliation
118119
* accessible no-arg constructor.
119120
*/
120121
Class<? extends RateLimiter> rateLimiter() default LinearRateLimiter.class;
122+
123+
/**
124+
* <p>
125+
* <b>This is an experimental feature, might be a subject of change and even removal in the
126+
* future.</b>
127+
* </p>
128+
* <p>
129+
* In order to optimize cache, thus set null on some attributes, this function can be set. Note
130+
* that this has subtle implications how updates on the resources should be handled. Notably only
131+
* patching of the resource can be used from that point, since update would remove not cached
132+
* parts of the resource.
133+
* </p>
134+
* <p>
135+
* Note that this feature does not work with Dependent Resources.
136+
* </p>
137+
*
138+
*
139+
*
140+
* @return function to remove parts of the resource.
141+
*/
142+
Class<? extends UnaryOperator> cachePruneFunction() default UnaryOperator.class;
121143
}

0 commit comments

Comments
 (0)