Skip to content

Commit 7ee8a12

Browse files
Added 'monitor' and 'report' methods for database monitoring through document store (#75)
1 parent 9a48be3 commit 7ee8a12

File tree

5 files changed

+193
-0
lines changed

5 files changed

+193
-0
lines changed

docstore-metrics/build.gradle.kts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
plugins {
2+
`java-library`
3+
jacoco
4+
id("org.hypertrace.publish-plugin")
5+
id("org.hypertrace.jacoco-report-plugin")
6+
}
7+
8+
dependencies {
9+
annotationProcessor(libs.lombok)
10+
compileOnly(libs.lombok)
11+
12+
api(libs.hypertrace.documentStore)
13+
api(project(":service-framework-spi"))
14+
implementation(project(":platform-metrics"))
15+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
package org.hypertrace.core.serviceframework.docstore.metrics;
2+
3+
import java.time.Duration;
4+
import lombok.Builder;
5+
import lombok.Value;
6+
import lombok.experimental.Accessors;
7+
import org.hypertrace.core.documentstore.model.config.CustomMetricConfig;
8+
9+
@Value
10+
@Builder
11+
@Accessors(fluent = true)
12+
public class DocStoreCustomMetricReportingConfig {
13+
CustomMetricConfig config;
14+
Duration reportingInterval;
15+
}
Lines changed: 159 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,159 @@
1+
package org.hypertrace.core.serviceframework.docstore.metrics;
2+
3+
import static java.util.Collections.emptyList;
4+
import static java.util.concurrent.TimeUnit.MINUTES;
5+
import static java.util.concurrent.TimeUnit.SECONDS;
6+
7+
import io.micrometer.common.lang.Nullable;
8+
import java.time.Duration;
9+
import java.util.List;
10+
import java.util.concurrent.Executors;
11+
import java.util.concurrent.ScheduledExecutorService;
12+
import java.util.concurrent.atomic.AtomicLong;
13+
import lombok.NonNull;
14+
import org.hypertrace.core.documentstore.Datastore;
15+
import org.hypertrace.core.documentstore.metric.DocStoreMetric;
16+
import org.hypertrace.core.documentstore.metric.DocStoreMetricProvider;
17+
import org.hypertrace.core.documentstore.model.config.CustomMetricConfig;
18+
import org.hypertrace.core.serviceframework.metrics.PlatformMetricsRegistry;
19+
import org.hypertrace.core.serviceframework.spi.PlatformServiceLifecycle;
20+
21+
@SuppressWarnings("unused")
22+
public class DocStoreMetricsRegistry {
23+
private static final long INITIAL_DELAY_SECONDS = MINUTES.toSeconds(5);
24+
25+
private final DocStoreMetricProvider metricProvider;
26+
@Nullable private PlatformServiceLifecycle platformLifecycle;
27+
private int threadPoolSize;
28+
private List<DocStoreCustomMetricReportingConfig> customMetricConfigs;
29+
private Duration standardMetricsReportingInterval;
30+
private ScheduledExecutorService executor;
31+
32+
public DocStoreMetricsRegistry(@NonNull final Datastore datastore) {
33+
metricProvider = datastore.getDocStoreMetricProvider();
34+
platformLifecycle = null;
35+
threadPoolSize = 1;
36+
customMetricConfigs = emptyList();
37+
standardMetricsReportingInterval = Duration.ofMinutes(30);
38+
}
39+
40+
/**
41+
* Supply the platform lifecycle to stop monitoring the datastore when the service is shutting
42+
* down
43+
*/
44+
public DocStoreMetricsRegistry withPlatformLifecycle(
45+
final PlatformServiceLifecycle platformLifecycle) {
46+
this.platformLifecycle = platformLifecycle;
47+
return this;
48+
}
49+
50+
/** Define the custom metrics to be reported */
51+
public DocStoreMetricsRegistry withCustomMetrics(
52+
@NonNull final List<DocStoreCustomMetricReportingConfig> customMetricConfigs) {
53+
this.customMetricConfigs = customMetricConfigs;
54+
return this;
55+
}
56+
57+
/**
58+
* Override the number of threads dedicated for datastore metric reporting. The default value is
59+
* 1.
60+
*/
61+
public DocStoreMetricsRegistry withThreadPoolSize(final int threadPoolSize) {
62+
this.threadPoolSize = threadPoolSize;
63+
return this;
64+
}
65+
66+
/**
67+
* Override the reporting interval of the standard datastore metrics. The default value is 30
68+
* minutes.
69+
*/
70+
public DocStoreMetricsRegistry withStandardMetricsReportingInterval(
71+
@NonNull final Duration standardMetricsReportingInterval) {
72+
this.standardMetricsReportingInterval = standardMetricsReportingInterval;
73+
return this;
74+
}
75+
76+
/**
77+
* Continuously monitor a database and periodically report (standard and custom) metrics.
78+
*
79+
* <p>The standard metrics (like the database connection count this service is holding) are
80+
* reported immediately after this method is invoked and subsequently reported at the standard
81+
* metrics reporting interval (configurable using {@link #withStandardMetricsReportingInterval})
82+
*
83+
* <p>The custom metrics, provided through {@link #withCustomMetrics}, are reported immediately
84+
* after this method is invoked and subsequently reported at the interval scheduled per metric.
85+
*/
86+
public void monitor() {
87+
shutdown();
88+
executor = Executors.newScheduledThreadPool(threadPoolSize);
89+
90+
addShutdownHook();
91+
92+
new StandardDocStoreMetricsRegistry().monitor();
93+
monitorCustomMetrics();
94+
}
95+
96+
/** Instantly query the datastore and report the custom metric once */
97+
public void report(final CustomMetricConfig customMetricConfig) {
98+
metricProvider.getCustomMetrics(customMetricConfig).forEach(this::report);
99+
}
100+
101+
/** Stop monitoring the database */
102+
public void shutdown() {
103+
if (executor != null) {
104+
executor.shutdown();
105+
}
106+
}
107+
108+
private void addShutdownHook() {
109+
if (platformLifecycle != null) {
110+
platformLifecycle.shutdownComplete().thenRun(this::shutdown);
111+
}
112+
}
113+
114+
private void monitorCustomMetrics() {
115+
customMetricConfigs.forEach(
116+
reportingConfig ->
117+
executor.scheduleAtFixedRate(
118+
() -> report(reportingConfig.config()),
119+
INITIAL_DELAY_SECONDS,
120+
reportingConfig.reportingInterval().toSeconds(),
121+
SECONDS));
122+
}
123+
124+
private void report(final DocStoreMetric metric) {
125+
PlatformMetricsRegistry.registerGauge(metric.name(), metric.labels(), metric.value());
126+
}
127+
128+
private class StandardDocStoreMetricsRegistry {
129+
private final AtomicLong connectionCount;
130+
131+
public StandardDocStoreMetricsRegistry() {
132+
this.connectionCount = registerConnectionCountMetric();
133+
}
134+
135+
private void monitor() {
136+
executor.scheduleAtFixedRate(
137+
this::queryDocStoreAndSetMetricValues,
138+
INITIAL_DELAY_SECONDS,
139+
standardMetricsReportingInterval.toSeconds(),
140+
SECONDS);
141+
}
142+
143+
private AtomicLong registerConnectionCountMetric() {
144+
final DocStoreMetric docStoreMetric = metricProvider.getConnectionCountMetric();
145+
return PlatformMetricsRegistry.registerGauge(
146+
docStoreMetric.name(),
147+
docStoreMetric.labels(),
148+
new AtomicLong(castToLong(docStoreMetric.value())));
149+
}
150+
151+
private void queryDocStoreAndSetMetricValues() {
152+
connectionCount.set(castToLong(metricProvider.getConnectionCountMetric().value()));
153+
}
154+
155+
private long castToLong(final double value) {
156+
return Double.valueOf(value).longValue();
157+
}
158+
}
159+
}

gradle/libs.versions.toml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
[libraries]
2+
lombok = { module = "org.projectlombok:lombok", version = "1.18.28" }
3+
hypertrace-documentStore = { module = "org.hypertrace.core.documentstore:document-store", version = "0.7.37" }

settings.gradle.kts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,5 +16,6 @@ include(":platform-grpc-service-framework")
1616
include(":platform-http-service-framework")
1717
include(":platform-service-framework")
1818
include(":platform-metrics")
19+
include(":docstore-metrics")
1920
include(":integrationtest-service-framework")
2021
include(":service-framework-spi")

0 commit comments

Comments
 (0)