From 55378ae9825a4af0135aa3159ed553bd0d1e35fe Mon Sep 17 00:00:00 2001 From: michaldo Date: Wed, 4 Jun 2025 23:32:49 +0200 Subject: [PATCH 1/2] Do not instrument "hello" When MongoObservationCommandListener is enabled according to documentation, there is a race in metrics registration: > The meter (MeterId{name='spring.data.mongodb.command.active', tags=[tag(db.name=test),tag(db.operation=hello),tag(db.system=mongodb),tag(net.peer.name=localhost),tag(net.peer.port=27017),tag(net.transport=IP.TCP),tag(spring.data.mongodb.cluster_id=6840b93dabcd29b2f0526362)]}) registration has failed: ... Prometheus does not allow register metric with different tags. The problem is that healthcheck calls Mongo command "hello" which does not refer to any collection. Consequently, tag "db.mongodb.collection" is absent for "hello" but present in regular commands. Event if regular commands always wins the race (and metrics always has tag "collection"), the race must be removed. My proposal is skip "hello" instrumentation, like "admin" commands Signed-off-by: michaldo --- .../observability/MongoObservationCommandListener.java | 4 ++++ .../MongoObservationCommandListenerTests.java | 10 ++++++++++ 2 files changed, 14 insertions(+) diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/observability/MongoObservationCommandListener.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/observability/MongoObservationCommandListener.java index 914396ab96..7eccd6335f 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/observability/MongoObservationCommandListener.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/observability/MongoObservationCommandListener.java @@ -105,6 +105,10 @@ public void commandStarted(CommandStartedEvent event) { return; // don't instrument commands like "endSessions" } + if ("hello".equals(event.getCommandName())) { + return; // don't instrument healthcheck + } + RequestContext requestContext = event.getRequestContext(); if (requestContext == null) { diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/observability/MongoObservationCommandListenerTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/observability/MongoObservationCommandListenerTests.java index 5c2cb0b701..c1b02eabba 100644 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/observability/MongoObservationCommandListenerTests.java +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/observability/MongoObservationCommandListenerTests.java @@ -98,6 +98,16 @@ void commandStartedShouldNotInstrumentWhenNoParentSampleInRequestContext() { assertThat(meterRegistry).hasMeterWithName("spring.data.mongodb.command.active"); } + @Test + void commandStartedShouldNotInstrumentWhenHello() { + + // when + listener.commandStarted(new CommandStartedEvent(new MapRequestContext(), 0, 0, null, "some name", "hello", null)); + + // then + assertThat(meterRegistry).hasNoMetrics(); + } + @Test void successfullyCompletedCommandShouldCreateTimerWhenParentSampleInRequestContext() { From a6bc1172ffb63435f9229abaa6fcd34d0968be31 Mon Sep 17 00:00:00 2001 From: michaldo Date: Thu, 5 Jun 2025 23:31:15 +0200 Subject: [PATCH 2/2] Use default collection name is missing Signed-off-by: michaldo --- .../DefaultMongoHandlerObservationConvention.java | 3 +++ .../observability/MongoObservationCommandListener.java | 4 ---- .../MongoObservationCommandListenerTests.java | 9 +++++++-- 3 files changed, 10 insertions(+), 6 deletions(-) diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/observability/DefaultMongoHandlerObservationConvention.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/observability/DefaultMongoHandlerObservationConvention.java index 550a71b301..ec3a8838cf 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/observability/DefaultMongoHandlerObservationConvention.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/observability/DefaultMongoHandlerObservationConvention.java @@ -15,6 +15,7 @@ */ package org.springframework.data.mongodb.observability; +import io.micrometer.common.KeyValue; import io.micrometer.common.KeyValues; import org.springframework.data.mongodb.observability.MongoObservation.LowCardinalityCommandKeyNames; @@ -63,6 +64,8 @@ public KeyValues getLowCardinalityKeyValues(MongoHandlerContext context) { if (!ObjectUtils.isEmpty(context.getCollectionName())) { keyValues = keyValues .and(LowCardinalityCommandKeyNames.MONGODB_COLLECTION.withValue(context.getCollectionName())); + } else { + keyValues = keyValues.and(LowCardinalityCommandKeyNames.MONGODB_COLLECTION.withValue(KeyValue.NONE_VALUE)); } if(context.getCommandStartedEvent() == null) { diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/observability/MongoObservationCommandListener.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/observability/MongoObservationCommandListener.java index 7eccd6335f..914396ab96 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/observability/MongoObservationCommandListener.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/observability/MongoObservationCommandListener.java @@ -105,10 +105,6 @@ public void commandStarted(CommandStartedEvent event) { return; // don't instrument commands like "endSessions" } - if ("hello".equals(event.getCommandName())) { - return; // don't instrument healthcheck - } - RequestContext requestContext = event.getRequestContext(); if (requestContext == null) { diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/observability/MongoObservationCommandListenerTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/observability/MongoObservationCommandListenerTests.java index c1b02eabba..981e48a8e1 100644 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/observability/MongoObservationCommandListenerTests.java +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/observability/MongoObservationCommandListenerTests.java @@ -20,6 +20,7 @@ import io.micrometer.common.KeyValues; import io.micrometer.core.instrument.MeterRegistry; +import io.micrometer.core.instrument.Tags; import io.micrometer.core.instrument.observation.DefaultMeterObservationHandler; import io.micrometer.core.instrument.simple.SimpleMeterRegistry; import io.micrometer.observation.Observation; @@ -99,13 +100,17 @@ void commandStartedShouldNotInstrumentWhenNoParentSampleInRequestContext() { } @Test - void commandStartedShouldNotInstrumentWhenHello() { + void commandStartedShouldIncludeCollectionIfMissing() { // when listener.commandStarted(new CommandStartedEvent(new MapRequestContext(), 0, 0, null, "some name", "hello", null)); // then - assertThat(meterRegistry).hasNoMetrics(); + // although command 'hello' is collection-less, metric must have tag "db.mongodb.collection" + assertThat(meterRegistry).hasMeterWithNameAndTags( + "spring.data.mongodb.command.active", + Tags.of("db.mongodb.collection", "none")); + } @Test