diff --git a/service/src/main/java/org/whispersystems/textsecuregcm/WhisperServerService.java b/service/src/main/java/org/whispersystems/textsecuregcm/WhisperServerService.java index 9d2c5cfae..4f9a76a29 100644 --- a/service/src/main/java/org/whispersystems/textsecuregcm/WhisperServerService.java +++ b/service/src/main/java/org/whispersystems/textsecuregcm/WhisperServerService.java @@ -312,7 +312,13 @@ public void run(WhisperServerConfiguration config, Environment environment) thro UncaughtExceptionHandler.register(); - MetricsUtil.configureRegistries(config, environment); + DynamicConfigurationManager dynamicConfigurationManager = + new DynamicConfigurationManager<>(config.getAppConfig().getApplication(), + config.getAppConfig().getEnvironment(), + config.getAppConfig().getConfigurationName(), + DynamicConfiguration.class); + + MetricsUtil.configureRegistries(config, environment, dynamicConfigurationManager); final boolean useRemoteAddress = Optional.ofNullable( System.getenv("SIGNAL_USE_REMOTE_ADDRESS")) @@ -342,12 +348,6 @@ public void run(WhisperServerConfiguration config, Environment environment) thro DynamoDbClient dynamoDbClient = DynamoDbFromConfig.client(config.getDynamoDbClientConfiguration(), AWSSDK_CREDENTIALS_PROVIDER); - DynamicConfigurationManager dynamicConfigurationManager = - new DynamicConfigurationManager<>(config.getAppConfig().getApplication(), - config.getAppConfig().getEnvironment(), - config.getAppConfig().getConfigurationName(), - DynamicConfiguration.class); - BlockingQueue messageDeletionQueue = new LinkedBlockingQueue<>(); Metrics.gaugeCollectionSize(name(getClass(), "messageDeletionQueueSize"), Collections.emptyList(), messageDeletionQueue); diff --git a/service/src/main/java/org/whispersystems/textsecuregcm/configuration/dynamic/DynamicConfiguration.java b/service/src/main/java/org/whispersystems/textsecuregcm/configuration/dynamic/DynamicConfiguration.java index 658051454..2fd02f927 100644 --- a/service/src/main/java/org/whispersystems/textsecuregcm/configuration/dynamic/DynamicConfiguration.java +++ b/service/src/main/java/org/whispersystems/textsecuregcm/configuration/dynamic/DynamicConfiguration.java @@ -63,6 +63,10 @@ public class DynamicConfiguration { @Valid DynamicVirtualThreadConfiguration virtualThreads = new DynamicVirtualThreadConfiguration(Collections.emptySet()); + @JsonProperty + @Valid + DynamicMetricsConfiguration metricsConfiguration = new DynamicMetricsConfiguration(false); + public Optional getExperimentEnrollmentConfiguration( final String experimentName) { return Optional.ofNullable(experiments.get(experimentName)); @@ -113,4 +117,8 @@ public DynamicVirtualThreadConfiguration getVirtualThreads() { return virtualThreads; } + public DynamicMetricsConfiguration getMetricsConfiguration() { + return metricsConfiguration; + } + } diff --git a/service/src/main/java/org/whispersystems/textsecuregcm/configuration/dynamic/DynamicMetricsConfiguration.java b/service/src/main/java/org/whispersystems/textsecuregcm/configuration/dynamic/DynamicMetricsConfiguration.java new file mode 100644 index 000000000..170763eef --- /dev/null +++ b/service/src/main/java/org/whispersystems/textsecuregcm/configuration/dynamic/DynamicMetricsConfiguration.java @@ -0,0 +1,13 @@ +/* + * Copyright 2024 Signal Messenger, LLC + * SPDX-License-Identifier: AGPL-3.0-only + */ + +package org.whispersystems.textsecuregcm.configuration.dynamic; + +/** + * @param enableLettuceRemoteTag - whether the `remote` tag should be added. Note: although this is dynamic, meters are + * cached after creation, so changes will only affect servers launched after the change. + */ +public record DynamicMetricsConfiguration(boolean enableLettuceRemoteTag) { +} diff --git a/service/src/main/java/org/whispersystems/textsecuregcm/metrics/MetricsUtil.java b/service/src/main/java/org/whispersystems/textsecuregcm/metrics/MetricsUtil.java index 193c1901f..a7e6cffa8 100644 --- a/service/src/main/java/org/whispersystems/textsecuregcm/metrics/MetricsUtil.java +++ b/service/src/main/java/org/whispersystems/textsecuregcm/metrics/MetricsUtil.java @@ -18,7 +18,9 @@ import java.util.concurrent.TimeUnit; import org.whispersystems.textsecuregcm.WhisperServerConfiguration; import org.whispersystems.textsecuregcm.WhisperServerVersion; +import org.whispersystems.textsecuregcm.configuration.dynamic.DynamicConfiguration; import org.whispersystems.textsecuregcm.push.PushLatencyManager; +import org.whispersystems.textsecuregcm.storage.DynamicConfigurationManager; import org.whispersystems.textsecuregcm.util.Constants; public class MetricsUtil { @@ -41,7 +43,8 @@ private static String name(String name, String... parts) { return sb.toString(); } - public static void configureRegistries(final WhisperServerConfiguration config, final Environment environment) { + public static void configureRegistries(final WhisperServerConfiguration config, final Environment environment, + DynamicConfigurationManager dynamicConfigurationManager) { SharedMetricRegistries.add(Constants.METRICS_NAME, environment.metrics()); { @@ -54,7 +57,7 @@ public static void configureRegistries(final WhisperServerConfiguration config, "version", WhisperServerVersion.getServerVersion(), "env", config.getDatadogConfiguration().getEnvironment())); - configureMeterFilters(dogstatsdMeterRegistry.config()); + configureMeterFilters(dogstatsdMeterRegistry.config(), dynamicConfigurationManager); Metrics.addRegistry(dogstatsdMeterRegistry); } @@ -64,7 +67,8 @@ public static void configureRegistries(final WhisperServerConfiguration config, } @VisibleForTesting - static MeterRegistry.Config configureMeterFilters(MeterRegistry.Config config) { + static MeterRegistry.Config configureMeterFilters(MeterRegistry.Config config, + final DynamicConfigurationManager dynamicConfigurationManager) { final DistributionStatisticConfig defaultDistributionStatisticConfig = DistributionStatisticConfig.builder() .percentiles(.75, .95, .99, .999) .build(); @@ -84,6 +88,8 @@ public Meter.Id map(final Meter.Id id) { return id.withName(PREFIX + "." + id.getName()) .replaceTags(id.getTags().stream() .filter(tag -> !"command".equals(tag.getKey())) + .filter(tag -> dynamicConfigurationManager.getConfiguration().getMetricsConfiguration(). + enableLettuceRemoteTag() || !"remote".equals(tag.getKey())) .toList()); } diff --git a/service/src/main/java/org/whispersystems/textsecuregcm/workers/AbstractSinglePassCrawlAccountsCommand.java b/service/src/main/java/org/whispersystems/textsecuregcm/workers/AbstractSinglePassCrawlAccountsCommand.java index 7e8549514..c2015aa90 100644 --- a/service/src/main/java/org/whispersystems/textsecuregcm/workers/AbstractSinglePassCrawlAccountsCommand.java +++ b/service/src/main/java/org/whispersystems/textsecuregcm/workers/AbstractSinglePassCrawlAccountsCommand.java @@ -63,11 +63,13 @@ protected void run(final Environment environment, final Namespace namespace, final WhisperServerConfiguration configuration) throws Exception { UncaughtExceptionHandler.register(); - MetricsUtil.configureRegistries(configuration, environment); this.namespace = namespace; this.commandDependencies = CommandDependencies.build(getName(), environment, configuration); + MetricsUtil.configureRegistries(configuration, environment, commandDependencies.dynamicConfigurationManager()); + + final int segments = Objects.requireNonNull(namespace.getInt(SEGMENT_COUNT)); logger.info("Crawling accounts with {} segments and {} processors", diff --git a/service/src/main/java/org/whispersystems/textsecuregcm/workers/CommandDependencies.java b/service/src/main/java/org/whispersystems/textsecuregcm/workers/CommandDependencies.java index 62842ec93..2a47bcc48 100644 --- a/service/src/main/java/org/whispersystems/textsecuregcm/workers/CommandDependencies.java +++ b/service/src/main/java/org/whispersystems/textsecuregcm/workers/CommandDependencies.java @@ -60,7 +60,8 @@ record CommandDependencies( ClientPresenceManager clientPresenceManager, KeysManager keysManager, FaultTolerantRedisCluster cacheCluster, - ClientResources redisClusterClientResources) { + ClientResources redisClusterClientResources, + DynamicConfigurationManager dynamicConfigurationManager) { static CommandDependencies build( final String name, @@ -196,7 +197,8 @@ static CommandDependencies build( clientPresenceManager, keys, cacheCluster, - redisClusterClientResources + redisClusterClientResources, + dynamicConfigurationManager ); } diff --git a/service/src/main/java/org/whispersystems/textsecuregcm/workers/MessagePersisterServiceCommand.java b/service/src/main/java/org/whispersystems/textsecuregcm/workers/MessagePersisterServiceCommand.java index c21c6b737..ad8ca787d 100644 --- a/service/src/main/java/org/whispersystems/textsecuregcm/workers/MessagePersisterServiceCommand.java +++ b/service/src/main/java/org/whispersystems/textsecuregcm/workers/MessagePersisterServiceCommand.java @@ -50,7 +50,9 @@ protected void run(Environment environment, Namespace namespace, WhisperServerCo UncaughtExceptionHandler.register(); - MetricsUtil.configureRegistries(configuration, environment); + final CommandDependencies deps = CommandDependencies.build("message-persister-service", environment, configuration); + + MetricsUtil.configureRegistries(configuration, environment, deps.dynamicConfigurationManager()); if (configuration.getServerFactory() instanceof DefaultServerFactory defaultServerFactory) { defaultServerFactory.getApplicationConnectors() @@ -61,7 +63,6 @@ protected void run(Environment environment, Namespace namespace, WhisperServerCo }); } - final CommandDependencies deps = CommandDependencies.build("message-persister-service", environment, configuration); final DynamicConfigurationManager dynamicConfigurationManager = new DynamicConfigurationManager<>( configuration.getAppConfig().getApplication(), diff --git a/service/src/main/java/org/whispersystems/textsecuregcm/workers/ScheduledApnPushNotificationSenderServiceCommand.java b/service/src/main/java/org/whispersystems/textsecuregcm/workers/ScheduledApnPushNotificationSenderServiceCommand.java index cb0e4a15c..6579f633b 100644 --- a/service/src/main/java/org/whispersystems/textsecuregcm/workers/ScheduledApnPushNotificationSenderServiceCommand.java +++ b/service/src/main/java/org/whispersystems/textsecuregcm/workers/ScheduledApnPushNotificationSenderServiceCommand.java @@ -52,7 +52,9 @@ protected void run(Environment environment, Namespace namespace, WhisperServerCo UncaughtExceptionHandler.register(); - MetricsUtil.configureRegistries(configuration, environment); + final CommandDependencies deps = CommandDependencies.build("scheduled-apn-sender", environment, configuration); + + MetricsUtil.configureRegistries(configuration, environment, deps.dynamicConfigurationManager()); if (configuration.getServerFactory() instanceof DefaultServerFactory defaultServerFactory) { defaultServerFactory.getApplicationConnectors() @@ -63,7 +65,6 @@ protected void run(Environment environment, Namespace namespace, WhisperServerCo }); } - final CommandDependencies deps = CommandDependencies.build("scheduled-apn-sender", environment, configuration); final FaultTolerantRedisCluster pushSchedulerCluster = new FaultTolerantRedisCluster("push_scheduler", configuration.getPushSchedulerCluster(), deps.redisClusterClientResources()); diff --git a/service/src/test/java/org/whispersystems/textsecuregcm/metrics/MetricsUtilTest.java b/service/src/test/java/org/whispersystems/textsecuregcm/metrics/MetricsUtilTest.java index 66016f3ef..ad095e1b9 100644 --- a/service/src/test/java/org/whispersystems/textsecuregcm/metrics/MetricsUtilTest.java +++ b/service/src/test/java/org/whispersystems/textsecuregcm/metrics/MetricsUtilTest.java @@ -7,12 +7,20 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; import io.micrometer.core.instrument.Meter; import io.micrometer.core.instrument.MeterRegistry; import io.micrometer.core.instrument.simple.SimpleMeterRegistry; import java.util.List; +import org.assertj.core.api.AbstractStringAssert; import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; +import org.whispersystems.textsecuregcm.configuration.dynamic.DynamicConfiguration; +import org.whispersystems.textsecuregcm.configuration.dynamic.DynamicMetricsConfiguration; +import org.whispersystems.textsecuregcm.storage.DynamicConfigurationManager; class MetricsUtilTest { @@ -25,10 +33,18 @@ void name() { MetricsUtil.name(MetricsUtilTest.class, "namespace", "metric")); } - @Test - void lettuceTagRejection() { + @ParameterizedTest + @ValueSource(booleans = {true, false}) + void lettuceTagRejection(final boolean enableLettuceRemoteTag) { + DynamicConfiguration dynamicConfiguration = mock(DynamicConfiguration.class); + DynamicMetricsConfiguration metricsConfiguration = new DynamicMetricsConfiguration(enableLettuceRemoteTag); + when(dynamicConfiguration.getMetricsConfiguration()).thenReturn(metricsConfiguration); + DynamicConfigurationManager dynamicConfigurationManager = + mock(DynamicConfigurationManager.class); + when(dynamicConfigurationManager.getConfiguration()).thenReturn(dynamicConfiguration); + MeterRegistry registry = new SimpleMeterRegistry(); - MetricsUtil.configureMeterFilters(registry.config()); + MetricsUtil.configureMeterFilters(registry.config(), dynamicConfigurationManager); registry.counter("lettuce.command.completion.max", "command", "hello", "remote", "world", "allowed", "!").increment(); final List meters = registry.getMeters(); @@ -37,6 +53,13 @@ void lettuceTagRejection() { Meter meter = meters.get(0); assertThat(meter.getId().getName()).isEqualTo("chat.lettuce.command.completion.max"); assertThat(meter.getId().getTag("command")).isNull(); + AbstractStringAssert remoteTag = assertThat(meter.getId().getTag("remote")); + + if (enableLettuceRemoteTag) { + remoteTag.isNotNull(); + } else { + remoteTag.isNull(); + } assertThat(meter.getId().getTag("allowed")).isNotNull(); } }