Many developers who tuned values like maximumPoolSize using the HikariCP tuning guide have probably run into this problem: you can’t tell whether that value is actually sufficient for production traffic at the moment you set it. This article explains how to visualize the metrics HikariCP exposes to Micrometer using Actuator/Prometheus, and how to detect signs of pool exhaustion with thresholds and alerts before errors actually occur.

Logs alone won’t catch the warning signs of exhaustion

Error logs like Failed to validate connection only appear after the pool has actually been exhausted. By the time you notice, the impact has already happened.

Moreover, while the pool is approaching exhaustion, the number of waiting threads increases and responses simply get slower — often without throwing any exception at all. If you rely on error logs alone, you’ll miss this early warning stage entirely.

Mapping key metrics to tuning values

HikariCP exposes just under 10 metrics to Micrometer. Let’s look at them in their Prometheus-format names (where dots become underscores), alongside how they correspond to the settings you decided on in the tuning guide.

  • hikaricp_connections_active is the number of connections currently in use
  • hikaricp_connections_idle is the number of idle, available connections, corresponding to the current value of minimumIdle. If it keeps trending toward zero, that’s a sign of approaching exhaustion
  • hikaricp_connections_pending is the number of threads waiting because they couldn’t acquire a connection — this is the most important indicator for detecting exhaustion
  • hikaricp_connections_max is simply the current value of maximumPoolSize; its ratio with active gives you the utilization rate
  • hikaricp_connections_acquire_seconds is the wait time to acquire a connection (since it’s a Timer type, it decomposes into multiple series)
  • hikaricp_connections_timeout_total is the count of acquisition failures that exceeded connectionTimeout

Configuration values represent “intent,” while metrics represent “actual state.” Only by comparing both can you tell whether your tuning is actually working.

Enabling the Prometheus endpoint in application.yml

First, add the dependencies.

implementation("org.springframework.boot:spring-boot-starter-actuator")
implementation("io.micrometer:micrometer-registry-prometheus")

Next, expose the endpoint. management.metrics.enable.hikaricp defaults to true, so no additional configuration is needed there.

management:
  endpoints:
    web:
      exposure:
        include: health,metrics,prometheus

After starting the application, verify it:

curl -s localhost:8080/actuator/prometheus | grep hikaricp_connections

If you see output with the label pool="HikariPool-1", you’re good to go. This is the auto-generated name used when pool-name isn’t configured, so if you’ve set it explicitly, substitute that value in the queries that follow.

Visualizing signs of exhaustion with Prometheus

Judging based on instantaneous values alone increases false positives, so the key is to look at sustained duration. You can get the utilization rate with this query:

hikaricp_connections_active{pool="HikariPool-1"}
  / hikaricp_connections_max{pool="HikariPool-1"}

If it stays at 80% or higher for 5 minutes or more, that’s a danger zone. Use min_over_time to judge sustained pending counts.

min_over_time(hikaricp_connections_pending{pool="HikariPool-1"}[5m]) > 0

Since pending should normally be zero, even a one-minute stretch above zero deserves attention. In Grafana, placing a utilization gauge next to a pending time series makes the trend immediately visible. For the setup itself, refer to the Spring Boot Observability setup guide.

Designing alert rules in Prometheus

What we’re defining here are Prometheus-side “alert rules” (rule_files). The syntax differs from the Alertmanager configuration file (alertmanager.yml), so pasting this directly into the wrong file will cause a parse error.

groups:
  - name: hikaricp
    rules:
      - alert: HikariPoolPendingThreads
        expr: hikaricp_connections_pending{pool="HikariPool-1"} > 0
        for: 1m
        labels:
          severity: critical
        annotations:
          summary: 'HikariCP connection waiting is occurring'

For a utilization-based rule, rewrite the expr and add severity: warning to set up a two-tier operation. The for clause is essential to avoid triggering on a momentary spike.

Notification is Alertmanager’s job. By splitting routing destinations based on severity, you can send critical alerts immediately.

route:
  receiver: default-mail
  routes:
    - matchers: [severity = "critical"]
      receiver: slack-critical
receivers:
  - name: default-mail
    email_configs: [{to: '[email protected]'}]
  - name: slack-critical
    slack_configs: [{channel: '#alerts-critical'}]

When a critical alert fires, don’t just reconsider the pool size — also suspect slow queries or connection leaks.

Summary

Tuning alone doesn’t tell you whether it’s actually sufficient in production. By mapping configuration values to metrics — like maximumPoolSize to hikaricp_connections_max — and setting up alerts for pending occurrences or sustained high utilization, you can respond before errors actually occur.

If you haven’t tuned yet, we recommend reading the HikariCP tuning guide first; if you’re unsure about Actuator, check out Getting Started with Spring Boot Actuator. If you’re already seeing errors, the guide to handling Failed to validate connection can help, and if your database’s max_connections is the bottleneck, see the article on Kubernetes replica scaling.