From 81f38c1db79afaa2435dbff8a04db8972404ac59 Mon Sep 17 00:00:00 2001 From: Ameya Lokare Date: Tue, 8 Sep 2026 13:47:03 -0700 Subject: [PATCH] Configure multiple FoundationDB client threads --- .../textsecuregcm/WhisperServerService.java | 12 ++++++++++ ...undationDbExternalClientConfiguration.java | 22 +++++++++++++++++++ .../FoundationDbMessagesConfiguration.java | 3 ++- .../workers/CommandDependencies.java | 12 ++++++++++ ...FoundationDbMessagesConfigurationTest.java | 8 +++++++ 5 files changed, 56 insertions(+), 1 deletion(-) create mode 100644 service/src/main/java/org/whispersystems/textsecuregcm/configuration/FoundationDbExternalClientConfiguration.java diff --git a/service/src/main/java/org/whispersystems/textsecuregcm/WhisperServerService.java b/service/src/main/java/org/whispersystems/textsecuregcm/WhisperServerService.java index d7019b554..b71c6b789 100644 --- a/service/src/main/java/org/whispersystems/textsecuregcm/WhisperServerService.java +++ b/service/src/main/java/org/whispersystems/textsecuregcm/WhisperServerService.java @@ -111,6 +111,7 @@ import org.whispersystems.textsecuregcm.captcha.CaptchaClient; import org.whispersystems.textsecuregcm.captcha.RegistrationCaptchaManager; import org.whispersystems.textsecuregcm.captcha.ShortCodeExpander; import org.whispersystems.textsecuregcm.configuration.BadgeConfiguration; +import org.whispersystems.textsecuregcm.configuration.FoundationDbExternalClientConfiguration; import org.whispersystems.textsecuregcm.configuration.dynamic.DynamicConfiguration; import org.whispersystems.textsecuregcm.configuration.secrets.SecretStore; import org.whispersystems.textsecuregcm.configuration.secrets.SecretsModule; @@ -500,6 +501,17 @@ public class WhisperServerService extends Application config.getFoundationDbMessagesConfiguration().clusters().size()); + externalClientConfiguration.clientLibraryPaths().forEach(path -> fdb.options().setExternalClientLibrary(path)); + fdb.options().setClientThreadsPerVersion(clientThreadsPerVersion); + } + final Map> messageDatabasesByEpoch; { final Map faultTolerantDatabasesByName = diff --git a/service/src/main/java/org/whispersystems/textsecuregcm/configuration/FoundationDbExternalClientConfiguration.java b/service/src/main/java/org/whispersystems/textsecuregcm/configuration/FoundationDbExternalClientConfiguration.java new file mode 100644 index 000000000..589e5a883 --- /dev/null +++ b/service/src/main/java/org/whispersystems/textsecuregcm/configuration/FoundationDbExternalClientConfiguration.java @@ -0,0 +1,22 @@ +/* + * Copyright 2026 Signal Messenger, LLC + * SPDX-License-Identifier: AGPL-3.0-only + */ +package org.whispersystems.textsecuregcm.configuration; + +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.PositiveOrZero; + +import java.util.List; +import java.util.Optional; + +/// Configuration for FoundationDB external clients. Primarily exists because an external client must be specified to +/// make use of the multi-threaded client feature. +/// +/// @param clientLibraryPaths a list of paths to external client libraries (generally .so files on Linux). This allows +/// us to run multiple FDB client versions concurrently if needed; however, during normal operation, we expect the list +/// to be a single path pointing to the current FDB client library +/// @param threadsPerClient the number of networking threads spawned per client +public record FoundationDbExternalClientConfiguration(@NotEmpty List<@NotBlank String> clientLibraryPaths, + Optional<@PositiveOrZero Integer> threadsPerClient) {} diff --git a/service/src/main/java/org/whispersystems/textsecuregcm/configuration/FoundationDbMessagesConfiguration.java b/service/src/main/java/org/whispersystems/textsecuregcm/configuration/FoundationDbMessagesConfiguration.java index 471a980bd..6945ba0ba 100644 --- a/service/src/main/java/org/whispersystems/textsecuregcm/configuration/FoundationDbMessagesConfiguration.java +++ b/service/src/main/java/org/whispersystems/textsecuregcm/configuration/FoundationDbMessagesConfiguration.java @@ -35,7 +35,8 @@ public record FoundationDbMessagesConfiguration(@NotEmpty Map configuration.getFoundationDbMessagesConfiguration().clusters().size()); + externalClientConfiguration.clientLibraryPaths().forEach(path -> fdb.options().setExternalClientLibrary(path)); + fdb.options().setClientThreadsPerVersion(clientThreadsPerVersion); + } + final Map> messageDatabasesByEpoch; { final Map faultTolerantDatabasesByName = diff --git a/service/src/test/java/org/whispersystems/textsecuregcm/configuration/FoundationDbMessagesConfigurationTest.java b/service/src/test/java/org/whispersystems/textsecuregcm/configuration/FoundationDbMessagesConfigurationTest.java index a29e24f8d..6e2382142 100644 --- a/service/src/test/java/org/whispersystems/textsecuregcm/configuration/FoundationDbMessagesConfigurationTest.java +++ b/service/src/test/java/org/whispersystems/textsecuregcm/configuration/FoundationDbMessagesConfigurationTest.java @@ -29,6 +29,7 @@ class FoundationDbMessagesConfigurationTest { FoundationDbMessagesConfiguration.DEFAULT_TRANSACTION_RETRY_LIMIT, FoundationDbMessagesConfiguration.DEFAULT_TRANSACTION_TIMEOUT.multipliedBy(2), FoundationDbMessagesConfiguration.DEFAULT_TRANSACTION_RETRY_LIMIT * 2, + null, null ).isEveryEpochClusterConfigured()); @@ -43,6 +44,7 @@ class FoundationDbMessagesConfigurationTest { FoundationDbMessagesConfiguration.DEFAULT_TRANSACTION_RETRY_LIMIT, FoundationDbMessagesConfiguration.DEFAULT_TRANSACTION_TIMEOUT.multipliedBy(2), FoundationDbMessagesConfiguration.DEFAULT_TRANSACTION_RETRY_LIMIT * 2, + null, null ).isEveryEpochClusterConfigured()); } @@ -60,6 +62,7 @@ class FoundationDbMessagesConfigurationTest { FoundationDbMessagesConfiguration.DEFAULT_TRANSACTION_RETRY_LIMIT, FoundationDbMessagesConfiguration.DEFAULT_TRANSACTION_TIMEOUT.multipliedBy(2), FoundationDbMessagesConfiguration.DEFAULT_TRANSACTION_RETRY_LIMIT * 2, + null, null ).isEveryEpochFreeOfDuplicates()); @@ -74,6 +77,7 @@ class FoundationDbMessagesConfigurationTest { FoundationDbMessagesConfiguration.DEFAULT_TRANSACTION_RETRY_LIMIT, FoundationDbMessagesConfiguration.DEFAULT_TRANSACTION_TIMEOUT.multipliedBy(2), FoundationDbMessagesConfiguration.DEFAULT_TRANSACTION_RETRY_LIMIT * 2, + null, null ).isEveryEpochFreeOfDuplicates()); } @@ -91,6 +95,7 @@ class FoundationDbMessagesConfigurationTest { FoundationDbMessagesConfiguration.DEFAULT_TRANSACTION_RETRY_LIMIT, FoundationDbMessagesConfiguration.DEFAULT_TRANSACTION_TIMEOUT.multipliedBy(2), FoundationDbMessagesConfiguration.DEFAULT_TRANSACTION_RETRY_LIMIT * 2, + null, null ).isActiveEpochConfigured()); @@ -105,6 +110,7 @@ class FoundationDbMessagesConfigurationTest { FoundationDbMessagesConfiguration.DEFAULT_TRANSACTION_RETRY_LIMIT, FoundationDbMessagesConfiguration.DEFAULT_TRANSACTION_TIMEOUT.multipliedBy(2), FoundationDbMessagesConfiguration.DEFAULT_TRANSACTION_RETRY_LIMIT * 2, + null, null ).isActiveEpochConfigured()); } @@ -122,6 +128,7 @@ class FoundationDbMessagesConfigurationTest { FoundationDbMessagesConfiguration.DEFAULT_TRANSACTION_RETRY_LIMIT, FoundationDbMessagesConfiguration.DEFAULT_TRANSACTION_TIMEOUT.multipliedBy(2), FoundationDbMessagesConfiguration.DEFAULT_TRANSACTION_RETRY_LIMIT * 2, + null, null ).isCurrentVersionstampCipherKeyConfigured()); @@ -136,6 +143,7 @@ class FoundationDbMessagesConfigurationTest { FoundationDbMessagesConfiguration.DEFAULT_TRANSACTION_RETRY_LIMIT, FoundationDbMessagesConfiguration.DEFAULT_TRANSACTION_TIMEOUT.multipliedBy(2), FoundationDbMessagesConfiguration.DEFAULT_TRANSACTION_RETRY_LIMIT * 2, + null, null ).isCurrentVersionstampCipherKeyConfigured()); }