Skip to content

Commit ca62b53

Browse files
committed
feat: share InformerManager when configuration is equal
Fixes #942
1 parent 73dc445 commit ca62b53

File tree

4 files changed

+126
-2
lines changed

4 files changed

+126
-2
lines changed

operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/informer/InformerManager.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -141,4 +141,8 @@ public void put(ResourceID key, T resource) {
141141
getSource(key.getNamespace().orElse(ANY_NAMESPACE_MAP_KEY))
142142
.ifPresent(c -> c.put(key, resource));
143143
}
144+
145+
int numberOfSources() {
146+
return sources.size();
147+
}
144148
}

operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/informer/InformerWrapper.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ class InformerWrapper<T extends HasMetadata>
1818
private final SharedIndexInformer<T> informer;
1919
private final InformerResourceCache<T> cache;
2020

21-
public InformerWrapper(SharedIndexInformer<T> informer) {
21+
InformerWrapper(SharedIndexInformer<T> informer) {
2222
this.informer = informer;
2323
this.cache = new InformerResourceCache<>(informer);
2424
}

operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/informer/ManagedInformerEventSource.java

Lines changed: 68 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,10 @@
11
package io.javaoperatorsdk.operator.processing.event.source.informer;
22

3+
import java.util.Objects;
4+
import java.util.Set;
5+
import java.util.concurrent.ConcurrentHashMap;
6+
import java.util.concurrent.ConcurrentMap;
7+
38
import io.fabric8.kubernetes.api.model.HasMetadata;
49
import io.fabric8.kubernetes.api.model.KubernetesResourceList;
510
import io.fabric8.kubernetes.client.dsl.MixedOperation;
@@ -13,15 +18,31 @@ public abstract class ManagedInformerEventSource<R extends HasMetadata, P extend
1318
extends CachingEventSource<R, P>
1419
implements ResourceEventHandler<R> {
1520

21+
@SuppressWarnings("rawtypes")
22+
private static final ConcurrentMap<ResourceConfigurationAsKey, InformerManager> managedInformers =
23+
new ConcurrentHashMap<>(17);
24+
1625
protected ManagedInformerEventSource(
1726
MixedOperation<R, KubernetesResourceList<R>, Resource<R>> client, C configuration) {
1827
super(configuration.getResourceClass());
28+
initCache(configuration);
1929
manager().initSources(client, configuration, this);
2030
}
2131

32+
@SuppressWarnings("unchecked")
33+
private void initCache(C configuration) {
34+
final var key = new ResourceConfigurationAsKey(configuration);
35+
var existing = managedInformers.get(key);
36+
if (existing == null) {
37+
existing = new InformerManager<>();
38+
managedInformers.put(key, existing);
39+
}
40+
cache = existing;
41+
}
42+
2243
@Override
2344
protected UpdatableCache<R> initCache() {
24-
return new InformerManager<>();
45+
return null; // cache needs the configuration to be properly initialized
2546
}
2647

2748
protected InformerManager<R, C> manager() {
@@ -39,4 +60,50 @@ public void stop() {
3960
super.stop();
4061
manager().stop();
4162
}
63+
64+
@SuppressWarnings("rawtypes")
65+
private static class ResourceConfigurationAsKey {
66+
private final ResourceConfiguration configuration;
67+
68+
private ResourceConfigurationAsKey(ResourceConfiguration configuration) {
69+
this.configuration = configuration;
70+
}
71+
72+
@Override
73+
public boolean equals(Object o) {
74+
if (this == o) {
75+
return true;
76+
}
77+
if (o == null || getClass() != o.getClass()) {
78+
return false;
79+
}
80+
81+
final var that = (ResourceConfigurationAsKey) o;
82+
if (configuration == that.configuration) {
83+
return true;
84+
}
85+
86+
return Objects.equals(getLabelSelector(), that.getLabelSelector())
87+
&& Objects.equals(getResourceClass(), that.getResourceClass())
88+
&& Objects.equals(getNamespaces(), that.getNamespaces());
89+
}
90+
91+
@Override
92+
public int hashCode() {
93+
return Objects.hash(getLabelSelector(), getResourceClass(), getNamespaces());
94+
}
95+
96+
public String getLabelSelector() {
97+
return configuration.getLabelSelector();
98+
}
99+
100+
public Class getResourceClass() {
101+
return configuration.getResourceClass();
102+
}
103+
104+
@SuppressWarnings("unchecked")
105+
public Set<String> getNamespaces() {
106+
return configuration.getNamespaces();
107+
}
108+
}
42109
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
package io.javaoperatorsdk.operator.processing.event.source.informer;
2+
3+
import org.junit.jupiter.api.Test;
4+
5+
import io.fabric8.kubernetes.api.model.HasMetadata;
6+
import io.javaoperatorsdk.operator.MockKubernetesClient;
7+
import io.javaoperatorsdk.operator.api.config.ConfigurationService;
8+
import io.javaoperatorsdk.operator.api.config.informer.InformerConfiguration;
9+
10+
import static org.junit.jupiter.api.Assertions.assertEquals;
11+
import static org.junit.jupiter.api.Assertions.assertNotEquals;
12+
import static org.mockito.Mockito.mock;
13+
14+
@SuppressWarnings({"rawtypes", "unchecked"})
15+
public class ManagedInformerEventSourceTest {
16+
@Test
17+
void sourcesWithSameConfigurationShouldShareInformer() {
18+
final var namespaces = new String[] {"foo", "bar"};
19+
final var config = InformerConfiguration.from(mock(ConfigurationService.class),
20+
HasMetadata.class)
21+
.withLabelSelector("label=value").withNamespaces(namespaces)
22+
.build();
23+
24+
final var informer1 =
25+
new InformerEventSource<>(config, MockKubernetesClient.client(HasMetadata.class));
26+
final var informer2 =
27+
new InformerEventSource<>(config, MockKubernetesClient.client(HasMetadata.class));
28+
29+
final var manager = informer1.manager();
30+
assertEquals(manager, informer2.manager());
31+
assertEquals(namespaces.length, manager.numberOfSources());
32+
}
33+
34+
@Test
35+
void sourcesWithDifferentConfigurationsShouldNotShareInformer() {
36+
final var config1 = InformerConfiguration.from(mock(ConfigurationService.class),
37+
HasMetadata.class)
38+
.withLabelSelector("label=value").withNamespaces("foo", "bar")
39+
.build();
40+
41+
final var config2 = InformerConfiguration.from(config1)
42+
.withLabelSelector("label=otherValue").withNamespaces("baz")
43+
.build();
44+
45+
final var informer1 =
46+
new InformerEventSource<>(config1, MockKubernetesClient.client(HasMetadata.class));
47+
final var informer2 =
48+
new InformerEventSource<>(config2, MockKubernetesClient.client(HasMetadata.class));
49+
50+
assertNotEquals(informer1.manager(), informer2.manager());
51+
assertEquals(1, informer2.manager().numberOfSources());
52+
}
53+
}

0 commit comments

Comments
 (0)