Skip to content

improve: dependent resource can be re-initialized #2026

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 6 commits into from
Aug 24, 2023
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -365,7 +365,7 @@ public void setConfigurationService(ConfigurationService configurationService) {
super.setConfigurationService(configurationService);

cache.addIndexers(indexerBuffer);
indexerBuffer = null;
indexerBuffer = new HashMap<>(0);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think that initializing the buffer to a zero size is a good idea since it will immediately need to get resized the first time something is added to it. Can't we initialize it to some default, reasonable size if we want to avoid overprovisioning for nothing? I'm not sure what's the typical use pattern here, like how many indexers might we expect to have?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We mostly shouldn't worry about initializing to specific sizes. The value is used lazily - so using zero or one is effectively the same. If not specified it will default to 16, but you're only holding that memory ephemerally based upon the usage pattern here.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess 0 leads to the internal table being initialized to size 1… which is reasonable if we expect a low number of indexer. No idea on the usage pattern.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Usually there is index just for one case (at least this is what we see now) when there is many-to-one / many-to-many relationship between resource, see: https://javaoperatorsdk.io/docs/features#managing-relation-between-primary-and-secondary-resources. But yeah, maybe not necessary to optimizations like this, can be even confusing to read. will remove this number if not objections and have the default (as when initialized).

}

public void addIndexers(Map<String, Function<R, List<String>>> indexers) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package io.javaoperatorsdk.operator;

import org.junit.jupiter.api.Test;

import io.fabric8.kubernetes.client.KubernetesClient;
import io.fabric8.kubernetes.client.KubernetesClientBuilder;
import io.javaoperatorsdk.operator.junit.LocallyRunOperatorExtension;
import io.javaoperatorsdk.operator.sample.dependentreinitialization.ConfigMapDependentResource;
import io.javaoperatorsdk.operator.sample.dependentreinitialization.DependentReInitializationCustomResource;
import io.javaoperatorsdk.operator.sample.dependentreinitialization.DependentReInitializationReconciler;

class DependentReInitializationIT {

/**
* In case dependent resource is managed by CDI (like in Quarkus) can be handy that the instance
* is reused in tests.
*/
@Test
void dependentCanDeReInitialized() {
var client = new KubernetesClientBuilder().build();
LocallyRunOperatorExtension.applyCrd(DependentReInitializationCustomResource.class, client);

var dependent = new ConfigMapDependentResource();

startEndStopOperator(client, dependent);
startEndStopOperator(client, dependent);
}

private static void startEndStopOperator(KubernetesClient client,
ConfigMapDependentResource dependent) {
Operator o1 = new Operator(o -> o
.withCloseClientOnStop(false)
.withKubernetesClient(client));
o1.register(new DependentReInitializationReconciler(dependent, client));
o1.start();
o1.stop();
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package io.javaoperatorsdk.operator.sample.dependentreinitialization;

import java.util.Map;

import io.fabric8.kubernetes.api.model.ConfigMap;
import io.fabric8.kubernetes.api.model.ConfigMapBuilder;
import io.fabric8.kubernetes.api.model.ObjectMetaBuilder;
import io.javaoperatorsdk.operator.api.reconciler.Context;
import io.javaoperatorsdk.operator.processing.dependent.kubernetes.CRUDKubernetesDependentResource;

public class ConfigMapDependentResource
extends CRUDKubernetesDependentResource<ConfigMap, DependentReInitializationCustomResource> {

public ConfigMapDependentResource() {
super(ConfigMap.class);
}

@Override
protected ConfigMap desired(DependentReInitializationCustomResource primary,
Context<DependentReInitializationCustomResource> context) {
return new ConfigMapBuilder()
.withMetadata(new ObjectMetaBuilder()
.withName(primary.getMetadata().getName())
.withNamespace(primary.getMetadata().getNamespace())
.build())
.withData(Map.of("key", "val"))
.build();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package io.javaoperatorsdk.operator.sample.dependentreinitialization;

import io.fabric8.kubernetes.api.model.Namespaced;
import io.fabric8.kubernetes.client.CustomResource;
import io.fabric8.kubernetes.model.annotation.Group;
import io.fabric8.kubernetes.model.annotation.Version;

@Group("sample.javaoperatorsdk")
@Version("v1")
public class DependentReInitializationCustomResource
extends CustomResource<Void, Void>
implements Namespaced {

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package io.javaoperatorsdk.operator.sample.dependentreinitialization;

import java.util.Map;

import io.fabric8.kubernetes.client.KubernetesClient;
import io.javaoperatorsdk.operator.api.reconciler.*;
import io.javaoperatorsdk.operator.processing.event.source.EventSource;

@ControllerConfiguration
public class DependentReInitializationReconciler
implements Reconciler<DependentReInitializationCustomResource>,
EventSourceInitializer<DependentReInitializationCustomResource> {

private final ConfigMapDependentResource configMapDependentResource;

public DependentReInitializationReconciler(ConfigMapDependentResource dependentResource,
KubernetesClient client) {
this.configMapDependentResource = dependentResource;
this.configMapDependentResource.setKubernetesClient(client);
}

@Override
public UpdateControl<DependentReInitializationCustomResource> reconcile(
DependentReInitializationCustomResource resource,
Context<DependentReInitializationCustomResource> context) throws Exception {
configMapDependentResource.reconcile(resource, context);
return UpdateControl.noUpdate();
}

@Override
public Map<String, EventSource> prepareEventSources(
EventSourceContext<DependentReInitializationCustomResource> context) {
return EventSourceInitializer.nameEventSourcesFromDependentResource(context,
configMapDependentResource);
}


}