Add dynamic configuration to enable detailed Lettuce metrics
This commit is contained in:
parent
ff59ef8094
commit
699b0c775a
|
@ -312,7 +312,13 @@ public class WhisperServerService extends Application<WhisperServerConfiguration
|
||||||
|
|
||||||
UncaughtExceptionHandler.register();
|
UncaughtExceptionHandler.register();
|
||||||
|
|
||||||
MetricsUtil.configureRegistries(config, environment);
|
DynamicConfigurationManager<DynamicConfiguration> dynamicConfigurationManager =
|
||||||
|
new DynamicConfigurationManager<>(config.getAppConfig().getApplication(),
|
||||||
|
config.getAppConfig().getEnvironment(),
|
||||||
|
config.getAppConfig().getConfigurationName(),
|
||||||
|
DynamicConfiguration.class);
|
||||||
|
|
||||||
|
MetricsUtil.configureRegistries(config, environment, dynamicConfigurationManager);
|
||||||
|
|
||||||
final boolean useRemoteAddress = Optional.ofNullable(
|
final boolean useRemoteAddress = Optional.ofNullable(
|
||||||
System.getenv("SIGNAL_USE_REMOTE_ADDRESS"))
|
System.getenv("SIGNAL_USE_REMOTE_ADDRESS"))
|
||||||
|
@ -342,12 +348,6 @@ public class WhisperServerService extends Application<WhisperServerConfiguration
|
||||||
DynamoDbClient dynamoDbClient = DynamoDbFromConfig.client(config.getDynamoDbClientConfiguration(),
|
DynamoDbClient dynamoDbClient = DynamoDbFromConfig.client(config.getDynamoDbClientConfiguration(),
|
||||||
AWSSDK_CREDENTIALS_PROVIDER);
|
AWSSDK_CREDENTIALS_PROVIDER);
|
||||||
|
|
||||||
DynamicConfigurationManager<DynamicConfiguration> dynamicConfigurationManager =
|
|
||||||
new DynamicConfigurationManager<>(config.getAppConfig().getApplication(),
|
|
||||||
config.getAppConfig().getEnvironment(),
|
|
||||||
config.getAppConfig().getConfigurationName(),
|
|
||||||
DynamicConfiguration.class);
|
|
||||||
|
|
||||||
BlockingQueue<Runnable> messageDeletionQueue = new LinkedBlockingQueue<>();
|
BlockingQueue<Runnable> messageDeletionQueue = new LinkedBlockingQueue<>();
|
||||||
Metrics.gaugeCollectionSize(name(getClass(), "messageDeletionQueueSize"), Collections.emptyList(),
|
Metrics.gaugeCollectionSize(name(getClass(), "messageDeletionQueueSize"), Collections.emptyList(),
|
||||||
messageDeletionQueue);
|
messageDeletionQueue);
|
||||||
|
|
|
@ -63,6 +63,10 @@ public class DynamicConfiguration {
|
||||||
@Valid
|
@Valid
|
||||||
DynamicVirtualThreadConfiguration virtualThreads = new DynamicVirtualThreadConfiguration(Collections.emptySet());
|
DynamicVirtualThreadConfiguration virtualThreads = new DynamicVirtualThreadConfiguration(Collections.emptySet());
|
||||||
|
|
||||||
|
@JsonProperty
|
||||||
|
@Valid
|
||||||
|
DynamicMetricsConfiguration metricsConfiguration = new DynamicMetricsConfiguration(false);
|
||||||
|
|
||||||
public Optional<DynamicExperimentEnrollmentConfiguration> getExperimentEnrollmentConfiguration(
|
public Optional<DynamicExperimentEnrollmentConfiguration> getExperimentEnrollmentConfiguration(
|
||||||
final String experimentName) {
|
final String experimentName) {
|
||||||
return Optional.ofNullable(experiments.get(experimentName));
|
return Optional.ofNullable(experiments.get(experimentName));
|
||||||
|
@ -113,4 +117,8 @@ public class DynamicConfiguration {
|
||||||
return virtualThreads;
|
return virtualThreads;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public DynamicMetricsConfiguration getMetricsConfiguration() {
|
||||||
|
return metricsConfiguration;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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) {
|
||||||
|
}
|
|
@ -18,7 +18,9 @@ import io.micrometer.statsd.StatsdMeterRegistry;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
import org.whispersystems.textsecuregcm.WhisperServerConfiguration;
|
import org.whispersystems.textsecuregcm.WhisperServerConfiguration;
|
||||||
import org.whispersystems.textsecuregcm.WhisperServerVersion;
|
import org.whispersystems.textsecuregcm.WhisperServerVersion;
|
||||||
|
import org.whispersystems.textsecuregcm.configuration.dynamic.DynamicConfiguration;
|
||||||
import org.whispersystems.textsecuregcm.push.PushLatencyManager;
|
import org.whispersystems.textsecuregcm.push.PushLatencyManager;
|
||||||
|
import org.whispersystems.textsecuregcm.storage.DynamicConfigurationManager;
|
||||||
import org.whispersystems.textsecuregcm.util.Constants;
|
import org.whispersystems.textsecuregcm.util.Constants;
|
||||||
|
|
||||||
public class MetricsUtil {
|
public class MetricsUtil {
|
||||||
|
@ -41,7 +43,8 @@ public class MetricsUtil {
|
||||||
return sb.toString();
|
return sb.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void configureRegistries(final WhisperServerConfiguration config, final Environment environment) {
|
public static void configureRegistries(final WhisperServerConfiguration config, final Environment environment,
|
||||||
|
DynamicConfigurationManager<DynamicConfiguration> dynamicConfigurationManager) {
|
||||||
SharedMetricRegistries.add(Constants.METRICS_NAME, environment.metrics());
|
SharedMetricRegistries.add(Constants.METRICS_NAME, environment.metrics());
|
||||||
|
|
||||||
{
|
{
|
||||||
|
@ -54,7 +57,7 @@ public class MetricsUtil {
|
||||||
"version", WhisperServerVersion.getServerVersion(),
|
"version", WhisperServerVersion.getServerVersion(),
|
||||||
"env", config.getDatadogConfiguration().getEnvironment()));
|
"env", config.getDatadogConfiguration().getEnvironment()));
|
||||||
|
|
||||||
configureMeterFilters(dogstatsdMeterRegistry.config());
|
configureMeterFilters(dogstatsdMeterRegistry.config(), dynamicConfigurationManager);
|
||||||
Metrics.addRegistry(dogstatsdMeterRegistry);
|
Metrics.addRegistry(dogstatsdMeterRegistry);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -64,7 +67,8 @@ public class MetricsUtil {
|
||||||
}
|
}
|
||||||
|
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
static MeterRegistry.Config configureMeterFilters(MeterRegistry.Config config) {
|
static MeterRegistry.Config configureMeterFilters(MeterRegistry.Config config,
|
||||||
|
final DynamicConfigurationManager<DynamicConfiguration> dynamicConfigurationManager) {
|
||||||
final DistributionStatisticConfig defaultDistributionStatisticConfig = DistributionStatisticConfig.builder()
|
final DistributionStatisticConfig defaultDistributionStatisticConfig = DistributionStatisticConfig.builder()
|
||||||
.percentiles(.75, .95, .99, .999)
|
.percentiles(.75, .95, .99, .999)
|
||||||
.build();
|
.build();
|
||||||
|
@ -84,6 +88,8 @@ public class MetricsUtil {
|
||||||
return id.withName(PREFIX + "." + id.getName())
|
return id.withName(PREFIX + "." + id.getName())
|
||||||
.replaceTags(id.getTags().stream()
|
.replaceTags(id.getTags().stream()
|
||||||
.filter(tag -> !"command".equals(tag.getKey()))
|
.filter(tag -> !"command".equals(tag.getKey()))
|
||||||
|
.filter(tag -> dynamicConfigurationManager.getConfiguration().getMetricsConfiguration().
|
||||||
|
enableLettuceRemoteTag() || !"remote".equals(tag.getKey()))
|
||||||
.toList());
|
.toList());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -63,11 +63,13 @@ public abstract class AbstractSinglePassCrawlAccountsCommand extends Environment
|
||||||
final WhisperServerConfiguration configuration) throws Exception {
|
final WhisperServerConfiguration configuration) throws Exception {
|
||||||
|
|
||||||
UncaughtExceptionHandler.register();
|
UncaughtExceptionHandler.register();
|
||||||
MetricsUtil.configureRegistries(configuration, environment);
|
|
||||||
|
|
||||||
this.namespace = namespace;
|
this.namespace = namespace;
|
||||||
this.commandDependencies = CommandDependencies.build(getName(), environment, configuration);
|
this.commandDependencies = CommandDependencies.build(getName(), environment, configuration);
|
||||||
|
|
||||||
|
MetricsUtil.configureRegistries(configuration, environment, commandDependencies.dynamicConfigurationManager());
|
||||||
|
|
||||||
|
|
||||||
final int segments = Objects.requireNonNull(namespace.getInt(SEGMENT_COUNT));
|
final int segments = Objects.requireNonNull(namespace.getInt(SEGMENT_COUNT));
|
||||||
|
|
||||||
logger.info("Crawling accounts with {} segments and {} processors",
|
logger.info("Crawling accounts with {} segments and {} processors",
|
||||||
|
|
|
@ -60,7 +60,8 @@ record CommandDependencies(
|
||||||
ClientPresenceManager clientPresenceManager,
|
ClientPresenceManager clientPresenceManager,
|
||||||
KeysManager keysManager,
|
KeysManager keysManager,
|
||||||
FaultTolerantRedisCluster cacheCluster,
|
FaultTolerantRedisCluster cacheCluster,
|
||||||
ClientResources redisClusterClientResources) {
|
ClientResources redisClusterClientResources,
|
||||||
|
DynamicConfigurationManager<DynamicConfiguration> dynamicConfigurationManager) {
|
||||||
|
|
||||||
static CommandDependencies build(
|
static CommandDependencies build(
|
||||||
final String name,
|
final String name,
|
||||||
|
@ -196,7 +197,8 @@ record CommandDependencies(
|
||||||
clientPresenceManager,
|
clientPresenceManager,
|
||||||
keys,
|
keys,
|
||||||
cacheCluster,
|
cacheCluster,
|
||||||
redisClusterClientResources
|
redisClusterClientResources,
|
||||||
|
dynamicConfigurationManager
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -50,7 +50,9 @@ public class MessagePersisterServiceCommand extends ServerCommand<WhisperServerC
|
||||||
|
|
||||||
UncaughtExceptionHandler.register();
|
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) {
|
if (configuration.getServerFactory() instanceof DefaultServerFactory defaultServerFactory) {
|
||||||
defaultServerFactory.getApplicationConnectors()
|
defaultServerFactory.getApplicationConnectors()
|
||||||
|
@ -61,7 +63,6 @@ public class MessagePersisterServiceCommand extends ServerCommand<WhisperServerC
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
final CommandDependencies deps = CommandDependencies.build("message-persister-service", environment, configuration);
|
|
||||||
|
|
||||||
final DynamicConfigurationManager<DynamicConfiguration> dynamicConfigurationManager = new DynamicConfigurationManager<>(
|
final DynamicConfigurationManager<DynamicConfiguration> dynamicConfigurationManager = new DynamicConfigurationManager<>(
|
||||||
configuration.getAppConfig().getApplication(),
|
configuration.getAppConfig().getApplication(),
|
||||||
|
|
|
@ -52,7 +52,9 @@ public class ScheduledApnPushNotificationSenderServiceCommand extends ServerComm
|
||||||
|
|
||||||
UncaughtExceptionHandler.register();
|
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) {
|
if (configuration.getServerFactory() instanceof DefaultServerFactory defaultServerFactory) {
|
||||||
defaultServerFactory.getApplicationConnectors()
|
defaultServerFactory.getApplicationConnectors()
|
||||||
|
@ -63,7 +65,6 @@ public class ScheduledApnPushNotificationSenderServiceCommand extends ServerComm
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
final CommandDependencies deps = CommandDependencies.build("scheduled-apn-sender", environment, configuration);
|
|
||||||
|
|
||||||
final FaultTolerantRedisCluster pushSchedulerCluster = new FaultTolerantRedisCluster("push_scheduler",
|
final FaultTolerantRedisCluster pushSchedulerCluster = new FaultTolerantRedisCluster("push_scheduler",
|
||||||
configuration.getPushSchedulerCluster(), deps.redisClusterClientResources());
|
configuration.getPushSchedulerCluster(), deps.redisClusterClientResources());
|
||||||
|
|
|
@ -7,12 +7,20 @@ package org.whispersystems.textsecuregcm.metrics;
|
||||||
|
|
||||||
import static org.assertj.core.api.Assertions.assertThat;
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
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.Meter;
|
||||||
import io.micrometer.core.instrument.MeterRegistry;
|
import io.micrometer.core.instrument.MeterRegistry;
|
||||||
import io.micrometer.core.instrument.simple.SimpleMeterRegistry;
|
import io.micrometer.core.instrument.simple.SimpleMeterRegistry;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import org.assertj.core.api.AbstractStringAssert;
|
||||||
import org.junit.jupiter.api.Test;
|
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 {
|
class MetricsUtilTest {
|
||||||
|
@ -25,10 +33,18 @@ class MetricsUtilTest {
|
||||||
MetricsUtil.name(MetricsUtilTest.class, "namespace", "metric"));
|
MetricsUtil.name(MetricsUtilTest.class, "namespace", "metric"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@ParameterizedTest
|
||||||
void lettuceTagRejection() {
|
@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<DynamicConfiguration> dynamicConfigurationManager =
|
||||||
|
mock(DynamicConfigurationManager.class);
|
||||||
|
when(dynamicConfigurationManager.getConfiguration()).thenReturn(dynamicConfiguration);
|
||||||
|
|
||||||
MeterRegistry registry = new SimpleMeterRegistry();
|
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();
|
registry.counter("lettuce.command.completion.max", "command", "hello", "remote", "world", "allowed", "!").increment();
|
||||||
final List<Meter> meters = registry.getMeters();
|
final List<Meter> meters = registry.getMeters();
|
||||||
|
@ -37,6 +53,13 @@ class MetricsUtilTest {
|
||||||
Meter meter = meters.get(0);
|
Meter meter = meters.get(0);
|
||||||
assertThat(meter.getId().getName()).isEqualTo("chat.lettuce.command.completion.max");
|
assertThat(meter.getId().getName()).isEqualTo("chat.lettuce.command.completion.max");
|
||||||
assertThat(meter.getId().getTag("command")).isNull();
|
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();
|
assertThat(meter.getId().getTag("allowed")).isNotNull();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue