diff --git a/openmetadata-service/src/main/java/org/openmetadata/service/monitoring/OpenMetadataMetrics.java b/openmetadata-service/src/main/java/org/openmetadata/service/monitoring/OpenMetadataMetrics.java index a8612e80b9bd..b84d21fb4233 100644 --- a/openmetadata-service/src/main/java/org/openmetadata/service/monitoring/OpenMetadataMetrics.java +++ b/openmetadata-service/src/main/java/org/openmetadata/service/monitoring/OpenMetadataMetrics.java @@ -4,6 +4,7 @@ import io.micrometer.core.instrument.Counter; import io.micrometer.core.instrument.DistributionSummary; +import io.micrometer.core.instrument.Gauge; import io.micrometer.core.instrument.MeterRegistry; import io.micrometer.core.instrument.Timer; import java.time.Duration; @@ -19,7 +20,6 @@ public class OpenMetadataMetrics { private final DistributionSummary httpResponseSize; private final Timer jdbiQueryTimer; - private final Counter jdbiConnectionCounter; private final Counter jdbiErrorCounter; private final Counter entityCreatedCounter; @@ -50,10 +50,9 @@ public OpenMetadataMetrics(MeterRegistry meterRegistry) { .sla(LATENCY_SLA_BUCKETS) .register(meterRegistry); - this.jdbiConnectionCounter = - Counter.builder("db.connections.total") - .description("Total database connections created") - .register(meterRegistry); + Gauge.builder("db.pool.connections", () -> poolTotalConnections()) + .description("Total connections in the database connection pool") + .register(meterRegistry); this.jdbiErrorCounter = Counter.builder("db.errors.total") @@ -135,10 +134,6 @@ public void recordDatabaseQuery(String queryType, long durationMs) { .record(Duration.ofMillis(durationMs)); } - public void incrementDatabaseConnections() { - jdbiConnectionCounter.increment(); - } - public void incrementDatabaseErrors(String errorType) { meterRegistry.counter("db.errors.total", "type", errorType).increment(); } @@ -200,7 +195,7 @@ public void recordAuthenticationFailure(String authType, String reason) { // Gauge registration methods public void registerGauge( String name, java.util.function.Supplier supplier, String description) { - io.micrometer.core.instrument.Gauge.builder(name, () -> supplier.get().doubleValue()) + Gauge.builder(name, () -> supplier.get().doubleValue()) .description(description) .register(meterRegistry); } @@ -208,4 +203,12 @@ public void registerGauge( public MeterRegistry getMeterRegistry() { return meterRegistry; } + + private double poolTotalConnections() { + Gauge active = meterRegistry.find("hikaricp.connections.active").gauge(); + Gauge idle = meterRegistry.find("hikaricp.connections.idle").gauge(); + double activeVal = active != null ? active.value() : 0.0; + double idleVal = idle != null ? idle.value() : 0.0; + return activeVal + idleVal; + } } diff --git a/openmetadata-service/src/test/java/org/openmetadata/service/monitoring/OpenMetadataMetricsTest.java b/openmetadata-service/src/test/java/org/openmetadata/service/monitoring/OpenMetadataMetricsTest.java new file mode 100644 index 000000000000..4b821e2ccd46 --- /dev/null +++ b/openmetadata-service/src/test/java/org/openmetadata/service/monitoring/OpenMetadataMetricsTest.java @@ -0,0 +1,80 @@ +package org.openmetadata.service.monitoring; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import io.micrometer.core.instrument.Gauge; +import io.micrometer.core.instrument.simple.SimpleMeterRegistry; +import io.micrometer.prometheusmetrics.PrometheusConfig; +import io.micrometer.prometheusmetrics.PrometheusMeterRegistry; +import java.util.concurrent.atomic.AtomicInteger; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +class OpenMetadataMetricsTest { + + private SimpleMeterRegistry registry; + + @BeforeEach + void setUp() { + registry = new SimpleMeterRegistry(); + } + + @Test + void dbPoolConnectionsGaugeReflectsHikariCPPoolSize() { + AtomicInteger activeConnections = new AtomicInteger(5); + AtomicInteger idleConnections = new AtomicInteger(15); + + Gauge.builder("hikaricp.connections.active", activeConnections, AtomicInteger::doubleValue) + .register(registry); + Gauge.builder("hikaricp.connections.idle", idleConnections, AtomicInteger::doubleValue) + .register(registry); + + new OpenMetadataMetrics(registry); + + Gauge total = registry.find("db.pool.connections").gauge(); + assertNotNull(total, "db.pool.connections gauge should be registered"); + assertEquals(20.0, total.value(), 0.01, "Should equal active + idle"); + + activeConnections.set(10); + idleConnections.set(10); + assertEquals(20.0, total.value(), 0.01, "Should reflect updated pool state"); + + activeConnections.set(0); + idleConnections.set(0); + assertEquals(0.0, total.value(), 0.01, "Should be zero when pool is empty"); + } + + @Test + void dbPoolConnectionsGaugeReturnsZeroWithoutHikariCP() { + new OpenMetadataMetrics(registry); + + Gauge total = registry.find("db.pool.connections").gauge(); + assertNotNull(total, "db.pool.connections gauge should be registered"); + assertEquals(0.0, total.value(), 0.01, "Should be zero when HikariCP metrics are absent"); + } + + @Test + void prometheusScrapeExposesDbPoolConnectionsGauge() { + PrometheusMeterRegistry promRegistry = new PrometheusMeterRegistry(PrometheusConfig.DEFAULT); + + AtomicInteger activeConnections = new AtomicInteger(3); + AtomicInteger idleConnections = new AtomicInteger(7); + + Gauge.builder("hikaricp.connections.active", activeConnections, AtomicInteger::doubleValue) + .register(promRegistry); + Gauge.builder("hikaricp.connections.idle", idleConnections, AtomicInteger::doubleValue) + .register(promRegistry); + + new OpenMetadataMetrics(promRegistry); + + String scrape = promRegistry.scrape(); + assertTrue( + scrape.contains("db_pool_connections"), + "Prometheus scrape should contain db_pool_connections metric"); + assertTrue( + scrape.contains("# TYPE db_pool_connections gauge"), + "db_pool_connections should be exposed as a gauge type"); + } +}