如何解决OpenTelemetry Agent数据库连接指标重复值告警?
db.client.connections.idle.min重复指标告警问题 问题原因
该告警源于OpenTelemetry的HikariCP instrumentation在收集db.client.connections.idle.min异步指标时,同一个pool.name关联了多个HikariDataSource实例,导致每秒指标上报时同一属性集下出现多条记录,触发SDK的重复值校验告警。
从你的代码来看,db2DataSource()和postgresDataSource()每次调用都会创建新的HikariDataSource实例(例如db2DataSource()直接返回HikariDataSource(db2HikariConfig())),而所有实例的pool.name是固定值(基于naisAppName生成),这就造成了同一连接池名称对应多个数据源实例的情况。
解决方案(保留监控的前提下)
1. 确保连接池实例单例化
修改数据库配置代码,让每个连接池只创建一次实例,避免同一pool.name对应多个数据源:
object DatabaseConfig { // 单例初始化DB2数据源 private val db2DataSource by lazy { HikariDataSource(db2HikariConfig()) } // 单例初始化默认角色的PostgreSQL数据源 private val defaultPostgresDataSource by lazy { postgresDataSource(postgresHikariConfig()) } private fun db2HikariConfig(): HikariConfig { // 原代码逻辑保持不变 val db2Properties: PropertiesConfig.Db2Properties = PropertiesConfig.Db2Properties() return HikariConfig().apply { maximumPoolSize = 10 minimumIdle = 1 poolName = "HikariPool-${PropertiesConfig.Configuration().naisAppName}-DB2" connectionTestQuery = "select 1 from sysibm.sysdummy1" dataSource = DB2SimpleDataSource().apply { driverType = 4 enableNamedParameterMarkers = DB2BaseDataSource.YES databaseName = db2Properties.name serverName = db2Properties.host portNumber = db2Properties.port.toInt() currentSchema = db2Properties.schema connectionTimeout = 1000 commandTimeout = 10000 user = db2Properties.username setPassword(db2Properties.password) } } } private fun postgresHikariConfig(): HikariConfig { // 原代码逻辑保持不变 val postgresProperties: PropertiesConfig.PostgresProperties = PropertiesConfig.PostgresProperties() return HikariConfig().apply { poolName = "HikariPool-${PropertiesConfig.Configuration().naisAppName}-POSTGRES" maximumPoolSize = 5 minimumIdle = 1 dataSource = PGSimpleDataSource().apply { if (PropertiesConfig.isLocal()) { user = postgresProperties.adminUsername password = postgresProperties.adminPassword } serverNames = arrayOf(postgresProperties.host) databaseName = postgresProperties.databaseName portNumbers = intArrayOf(postgresProperties.port.toInt()) connectionTimeout = Duration.ofSeconds(10).toMillis() maxLifetime = Duration.ofMinutes(30).toMillis() initializationFailTimeout = Duration.ofMinutes(30).toMillis() } } } fun db2DataSource(): HikariDataSource = db2DataSource fun postgresDataSource( hikariConfig: HikariConfig = postgresHikariConfig(), role: String = PropertiesConfig.PostgresProperties().user, ): HikariDataSource { return when { PropertiesConfig.isLocal() -> HikariDataSource(hikariConfig) else -> HikariCPVaultUtil.createHikariDataSourceWithVaultIntegration( hikariConfig, PropertiesConfig.PostgresProperties().vaultMountPath, role, ) } } // 对外提供默认单例的PostgreSQL数据源 fun defaultPostgresDataSource(): HikariDataSource = defaultPostgresDataSource fun postgresMigrate(dataSource: HikariDataSource = defaultPostgresDataSource()) { // 原代码逻辑保持不变 Flyway.configure() .dataSource(dataSource) .initSql("""SET ROLE "${PropertiesConfig.PostgresProperties().adminUser}"""") .lockRetryCount(-1) .load() .migrate() .migrationsExecuted logger.info { "Migration finished" } } }
通过lazy初始化保证每个连接池名称仅对应一个数据源实例,从根源上避免重复指标上报。
2. 升级OpenTelemetry Agent版本
OpenTelemetry Agent 2.5.0的HikariCP instrumentation存在已知的重复指标上报问题,升级到2.6.0及以上稳定版本,官方后续版本修复了异步指标的重复值处理逻辑。
3. 调整OTel指标聚合策略(临时 workaround)
如果无法立即升级或修改代码,可通过JVM参数配置,将db.client.connections.idle.min指标的聚合策略设置为last_value,让SDK忽略同一属性的重复值,仅保留最新记录:
-Dotel.sdk.metrics.export.aggregation.db.client.connections.idle.min=last_value
注:该配置在2.5.0版本中部分支持,更高版本的兼容性和稳定性更佳。
验证
修改后重启应用,观察日志是否不再出现目标WARN告警,同时通过OTel的metrics端点(如/metrics)确认db.client.connections.idle.min指标仍正常上报,且每个pool.name对应唯一的指标条目。
内容的提问来源于stack exchange,提问作者mudasar

