Skip to content

Commit d41f31d

Browse files
csvirimetacosm
authored andcommitted
feat: feat controller queue size, execution thread count (operator-framework#1649)
1 parent 7734742 commit d41f31d

File tree

8 files changed

+80
-8
lines changed

8 files changed

+80
-8
lines changed

micrometer-support/src/main/java/io/javaoperatorsdk/operator/monitoring/micrometer/MicrometerMetrics.java

+56
Original file line numberDiff line numberDiff line change
@@ -5,28 +5,56 @@
55
import java.util.List;
66
import java.util.Map;
77
import java.util.Optional;
8+
import java.util.concurrent.ConcurrentHashMap;
9+
import java.util.concurrent.atomic.AtomicInteger;
810

911
import io.fabric8.kubernetes.api.model.HasMetadata;
1012
import io.javaoperatorsdk.operator.OperatorException;
1113
import io.javaoperatorsdk.operator.api.monitoring.Metrics;
1214
import io.javaoperatorsdk.operator.api.reconciler.Constants;
1315
import io.javaoperatorsdk.operator.api.reconciler.RetryInfo;
16+
import io.javaoperatorsdk.operator.processing.Controller;
1417
import io.javaoperatorsdk.operator.processing.GroupVersionKind;
1518
import io.javaoperatorsdk.operator.processing.event.Event;
1619
import io.javaoperatorsdk.operator.processing.event.ResourceID;
1720
import io.micrometer.core.instrument.MeterRegistry;
21+
import io.micrometer.core.instrument.Tag;
1822
import io.micrometer.core.instrument.Timer;
1923

24+
import static io.javaoperatorsdk.operator.api.reconciler.Constants.CONTROLLER_NAME;
25+
2026
public class MicrometerMetrics implements Metrics {
2127

2228
private static final String PREFIX = "operator.sdk.";
2329
private static final String RECONCILIATIONS = "reconciliations.";
30+
private static final String RECONCILIATIONS_EXECUTIONS = PREFIX + RECONCILIATIONS + "executions.";
31+
private static final String RECONCILIATIONS_QUEUE_SIZE = PREFIX + RECONCILIATIONS + "queue.size.";
2432
private final MeterRegistry registry;
33+
private final Map<String, AtomicInteger> gauges = new ConcurrentHashMap<>();
2534

2635
public MicrometerMetrics(MeterRegistry registry) {
2736
this.registry = registry;
2837
}
2938

39+
@Override
40+
public void controllerRegistered(Controller<?> controller) {
41+
String executingThreadsName =
42+
RECONCILIATIONS_EXECUTIONS + controller.getConfiguration().getName();
43+
AtomicInteger executingThreads =
44+
registry.gauge(executingThreadsName,
45+
gvkTags(controller.getConfiguration().getResourceClass()),
46+
new AtomicInteger(0));
47+
gauges.put(executingThreadsName, executingThreads);
48+
49+
String controllerQueueName =
50+
RECONCILIATIONS_QUEUE_SIZE + controller.getConfiguration().getName();
51+
AtomicInteger controllerQueueSize =
52+
registry.gauge(controllerQueueName,
53+
gvkTags(controller.getConfiguration().getResourceClass()),
54+
new AtomicInteger(0));
55+
gauges.put(controllerQueueName, controllerQueueSize);
56+
}
57+
3058
public <T> T timeControllerExecution(ControllerExecution<T> execution) {
3159
final var name = execution.controllerName();
3260
final var execName = PREFIX + "controllers.execution." + execution.name();
@@ -94,13 +122,35 @@ public void reconcileCustomResource(HasMetadata resource, RetryInfo retryInfoNul
94122
"" + retryInfo.map(RetryInfo::getAttemptCount).orElse(0),
95123
RECONCILIATIONS + "retries.last",
96124
"" + retryInfo.map(RetryInfo::isLastAttempt).orElse(true));
125+
126+
AtomicInteger controllerQueueSize =
127+
gauges.get(RECONCILIATIONS_QUEUE_SIZE + metadata.get(CONTROLLER_NAME));
128+
controllerQueueSize.incrementAndGet();
97129
}
98130

99131
@Override
100132
public void finishedReconciliation(HasMetadata resource, Map<String, Object> metadata) {
101133
incrementCounter(ResourceID.fromResource(resource), RECONCILIATIONS + "success", metadata);
102134
}
103135

136+
@Override
137+
public void reconciliationExecutionStarted(HasMetadata resource, Map<String, Object> metadata) {
138+
AtomicInteger reconcilerExecutions =
139+
gauges.get(RECONCILIATIONS_EXECUTIONS + metadata.get(CONTROLLER_NAME));
140+
reconcilerExecutions.incrementAndGet();
141+
}
142+
143+
@Override
144+
public void reconciliationExecutionFinished(HasMetadata resource, Map<String, Object> metadata) {
145+
AtomicInteger reconcilerExecutions =
146+
gauges.get(RECONCILIATIONS_EXECUTIONS + metadata.get(CONTROLLER_NAME));
147+
reconcilerExecutions.decrementAndGet();
148+
149+
AtomicInteger controllerQueueSize =
150+
gauges.get(RECONCILIATIONS_QUEUE_SIZE + metadata.get(CONTROLLER_NAME));
151+
controllerQueueSize.decrementAndGet();
152+
}
153+
104154
public void failedReconciliation(HasMetadata resource, Exception exception,
105155
Map<String, Object> metadata) {
106156
var cause = exception.getCause();
@@ -118,6 +168,12 @@ public void failedReconciliation(HasMetadata resource, Exception exception,
118168
return registry.gaugeMapSize(PREFIX + name + ".size", Collections.emptyList(), map);
119169
}
120170

171+
public static List<Tag> gvkTags(Class<? extends HasMetadata> resourceClass) {
172+
final var gvk = GroupVersionKind.gvkFor(resourceClass);
173+
return List.of(Tag.of("group", gvk.group), Tag.of("version", gvk.version),
174+
Tag.of("kind", gvk.kind));
175+
}
176+
121177
private void incrementCounter(ResourceID id, String counterName, Map<String, Object> metadata,
122178
String... additionalTags) {
123179
final var additionalTagsNb =

operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/monitoring/Metrics.java

+12
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
import io.fabric8.kubernetes.api.model.HasMetadata;
77
import io.javaoperatorsdk.operator.api.reconciler.Context;
88
import io.javaoperatorsdk.operator.api.reconciler.RetryInfo;
9+
import io.javaoperatorsdk.operator.processing.Controller;
910
import io.javaoperatorsdk.operator.processing.event.Event;
1011
import io.javaoperatorsdk.operator.processing.event.ResourceID;
1112

@@ -20,6 +21,11 @@ public interface Metrics {
2021
*/
2122
Metrics NOOP = new Metrics() {};
2223

24+
/**
25+
* Do initialization if necessary;
26+
*/
27+
default void controllerRegistered(Controller<?> controller) {}
28+
2329
/**
2430
* Called when an event has been accepted by the SDK from an event source, which would result in
2531
* potentially triggering the associated Reconciler.
@@ -63,6 +69,12 @@ default void failedReconciliation(HasMetadata resource, Exception exception,
6369
failedReconciliation(ResourceID.fromResource(resource), exception, metadata);
6470
}
6571

72+
73+
default void reconciliationExecutionStarted(HasMetadata resource, Map<String, Object> metadata) {}
74+
75+
default void reconciliationExecutionFinished(HasMetadata resource,
76+
Map<String, Object> metadata) {}
77+
6678
/**
6779
*
6880
* @deprecated Use (and implement) {@link #cleanupDoneFor(ResourceID, Map)} instead

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

+1
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ public final class Constants {
2121
public static final String SAME_AS_CONTROLLER = "JOSDK_SAME_AS_CONTROLLER";
2222

2323
public static final String RESOURCE_GVK_KEY = "josdk.resource.gvk";
24+
public static final String CONTROLLER_NAME = "controller.name";
2425

2526
private Constants() {}
2627
}

operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/Controller.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -90,10 +90,10 @@ public Controller(Reconciler<P> reconciler,
9090
eventProcessor = new EventProcessor<>(eventSourceManager);
9191
eventSourceManager.postProcessDefaultEventSourcesAfterProcessorInitializer();
9292
controllerHealthInfo = new ControllerHealthInfo(eventSourceManager);
93-
9493
final var context = new EventSourceContext<>(
9594
eventSourceManager.getControllerResourceEventSource(), configuration, kubernetesClient);
9695
initAndRegisterEventSources(context);
96+
ConfigurationServiceProvider.instance().getMetrics().controllerRegistered(this);
9797
}
9898

9999
@Override

operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/EventProcessor.java

+7-5
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
package io.javaoperatorsdk.operator.processing.event;
22

33
import java.time.Duration;
4-
import java.util.Collections;
4+
import java.util.HashMap;
55
import java.util.Map;
66
import java.util.Optional;
77
import java.util.concurrent.ExecutorService;
@@ -18,7 +18,6 @@
1818
import io.javaoperatorsdk.operator.api.config.ExecutorServiceManager;
1919
import io.javaoperatorsdk.operator.api.monitoring.Metrics;
2020
import io.javaoperatorsdk.operator.api.reconciler.Constants;
21-
import io.javaoperatorsdk.operator.processing.Controller;
2221
import io.javaoperatorsdk.operator.processing.LifecycleAware;
2322
import io.javaoperatorsdk.operator.processing.MDCUtils;
2423
import io.javaoperatorsdk.operator.processing.event.rate.RateLimiter;
@@ -98,9 +97,10 @@ private EventProcessor(
9897
this.rateLimiter = controllerConfiguration.getRateLimiter();
9998

10099
metricsMetadata = Optional.ofNullable(eventSourceManager.getController())
101-
.map(Controller::getAssociatedGroupVersionKind)
102-
.map(gvk -> Map.of(Constants.RESOURCE_GVK_KEY, (Object) gvk))
103-
.orElse(Collections.emptyMap());
100+
.map(c -> Map.of(
101+
Constants.RESOURCE_GVK_KEY, c.getAssociatedGroupVersionKind(),
102+
Constants.CONTROLLER_NAME, controllerConfiguration.getName()))
103+
.orElse(new HashMap<>());
104104
}
105105

106106
@Override
@@ -409,11 +409,13 @@ public void run() {
409409
}
410410
actualResource.ifPresent(executionScope::setResource);
411411
MDCUtils.addResourceInfo(executionScope.getResource());
412+
metrics.reconciliationExecutionStarted(executionScope.getResource(), metricsMetadata);
412413
thread.setName("ReconcilerExecutor-" + controllerName() + "-" + thread.getId());
413414
PostExecutionControl<P> postExecutionControl =
414415
reconciliationDispatcher.handleExecution(executionScope);
415416
eventProcessingFinished(executionScope, postExecutionControl);
416417
} finally {
418+
metrics.reconciliationExecutionFinished(executionScope.getResource(), metricsMetadata);
417419
// restore original name
418420
thread.setName(name);
419421
MDCUtils.removeResourceInfo();

operator-framework-core/src/test/java/io/javaoperatorsdk/operator/api/config/MockControllerConfiguration.java

+1
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ public static <R extends HasMetadata> ControllerConfiguration<R> forResource(
1414
when(configuration.getResourceClass()).thenReturn(resourceType);
1515
when(configuration.getNamespaces()).thenReturn(DEFAULT_NAMESPACES_SET);
1616
when(configuration.getEffectiveNamespaces()).thenCallRealMethod();
17+
when(configuration.getName()).thenReturn(resourceType.getSimpleName());
1718
return configuration;
1819
}
1920
}

operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/event/source/ResourceEventFilterTest.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -135,7 +135,7 @@ public ControllerConfig(String finalizer, boolean generationAware,
135135
ResourceEventFilter<T> eventFilter, Class<T> customResourceClass) {
136136
super(
137137
null,
138-
null,
138+
"testController",
139139
null,
140140
finalizer,
141141
generationAware,

operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/event/source/controller/ControllerResourceEventSourceTest.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -178,7 +178,7 @@ public TestConfiguration(boolean generationAware, OnAddFilter<TestCustomResource
178178
GenericFilter<TestCustomResource> genericFilter) {
179179
super(
180180
null,
181-
null,
181+
"testController",
182182
null,
183183
FINALIZER,
184184
generationAware,

0 commit comments

Comments
 (0)