diff --git a/.gitignore b/.gitignore index 484d31bd..e00185e6 100644 --- a/.gitignore +++ b/.gitignore @@ -45,3 +45,5 @@ examples/lib/.settings examples/lib/.classpath examples/lib/.project examples/lib/bin +.vscode/ +*logback.xml diff --git a/momento-sdk/src/main/java/momento/sdk/CacheClient.java b/momento-sdk/src/main/java/momento/sdk/CacheClient.java index 8b84bcac..14bed021 100644 --- a/momento-sdk/src/main/java/momento/sdk/CacheClient.java +++ b/momento-sdk/src/main/java/momento/sdk/CacheClient.java @@ -81,7 +81,7 @@ public CacheClient( @Nonnull CredentialProvider credentialProvider, @Nonnull Configuration configuration, @Nonnull Duration itemDefaultTtl) { - this.scsControlClient = new ScsControlClient(credentialProvider); + this.scsControlClient = new ScsControlClient(credentialProvider, configuration); this.scsDataClient = new ScsDataClient(credentialProvider, configuration, itemDefaultTtl); logger.info("Creating Momento Cache Client"); diff --git a/momento-sdk/src/main/java/momento/sdk/ScsControlClient.java b/momento-sdk/src/main/java/momento/sdk/ScsControlClient.java index 2e4c2de0..b4228a9d 100644 --- a/momento-sdk/src/main/java/momento/sdk/ScsControlClient.java +++ b/momento-sdk/src/main/java/momento/sdk/ScsControlClient.java @@ -30,6 +30,7 @@ import java.util.stream.Collectors; import javax.annotation.Nonnull; import momento.sdk.auth.CredentialProvider; +import momento.sdk.config.Configuration; import momento.sdk.exceptions.CacheServiceExceptionMapper; import momento.sdk.exceptions.InternalServerException; import momento.sdk.responses.cache.control.CacheCreateResponse; @@ -48,9 +49,10 @@ final class ScsControlClient extends ScsClient { private final CredentialProvider credentialProvider; private final ScsControlGrpcStubsManager controlGrpcStubsManager; - ScsControlClient(@Nonnull CredentialProvider credentialProvider) { + ScsControlClient(@Nonnull CredentialProvider credentialProvider, Configuration configuration) { this.credentialProvider = credentialProvider; - this.controlGrpcStubsManager = new ScsControlGrpcStubsManager(credentialProvider); + this.controlGrpcStubsManager = + new ScsControlGrpcStubsManager(credentialProvider, configuration); } CompletableFuture createCache(String cacheName) { diff --git a/momento-sdk/src/main/java/momento/sdk/ScsControlGrpcStubsManager.java b/momento-sdk/src/main/java/momento/sdk/ScsControlGrpcStubsManager.java index db8ddb42..a9f3b0e3 100644 --- a/momento-sdk/src/main/java/momento/sdk/ScsControlGrpcStubsManager.java +++ b/momento-sdk/src/main/java/momento/sdk/ScsControlGrpcStubsManager.java @@ -10,6 +10,9 @@ import java.util.concurrent.TimeUnit; import javax.annotation.Nonnull; import momento.sdk.auth.CredentialProvider; +import momento.sdk.config.Configuration; +import momento.sdk.config.transport.GrpcConfiguration; +import momento.sdk.internal.GrpcChannelOptions; /** * Manager responsible for GRPC channels and stubs for the Control Plane. @@ -26,16 +29,24 @@ final class ScsControlGrpcStubsManager implements AutoCloseable { private final ScsControlGrpc.ScsControlFutureStub futureStub; - ScsControlGrpcStubsManager(@Nonnull CredentialProvider credentialProvider) { - this.channel = setupConnection(credentialProvider); + ScsControlGrpcStubsManager( + @Nonnull CredentialProvider credentialProvider, Configuration configuration) { + this.channel = setupConnection(credentialProvider, configuration); this.futureStub = ScsControlGrpc.newFutureStub(channel); } - private static ManagedChannel setupConnection(CredentialProvider credentialProvider) { + private static ManagedChannel setupConnection( + CredentialProvider credentialProvider, Configuration configuration) { final NettyChannelBuilder channelBuilder = NettyChannelBuilder.forAddress(credentialProvider.getControlEndpoint(), 443); - channelBuilder.useTransportSecurity(); - channelBuilder.disableRetry(); + + // Override grpc config to disable keepalive for control clients + final GrpcConfiguration controlConfig = + configuration.getTransportStrategy().getGrpcConfiguration().withKeepAliveDisabled(); + + // set additional channel options (message size, keepalive, auth, etc) + GrpcChannelOptions.applyGrpcConfigurationToChannelBuilder(controlConfig, channelBuilder); + final List clientInterceptors = new ArrayList<>(); clientInterceptors.add(new UserHeaderInterceptor(credentialProvider.getAuthToken())); channelBuilder.intercept(clientInterceptors); diff --git a/momento-sdk/src/main/java/momento/sdk/ScsDataGrpcStubsManager.java b/momento-sdk/src/main/java/momento/sdk/ScsDataGrpcStubsManager.java index dc8ae6b3..a433ad0a 100644 --- a/momento-sdk/src/main/java/momento/sdk/ScsDataGrpcStubsManager.java +++ b/momento-sdk/src/main/java/momento/sdk/ScsDataGrpcStubsManager.java @@ -23,6 +23,7 @@ import javax.annotation.Nonnull; import momento.sdk.auth.CredentialProvider; import momento.sdk.config.Configuration; +import momento.sdk.internal.GrpcChannelOptions; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -171,8 +172,11 @@ private ManagedChannel setupChannel( CredentialProvider credentialProvider, Configuration configuration) { final NettyChannelBuilder channelBuilder = NettyChannelBuilder.forAddress(credentialProvider.getCacheEndpoint(), 443); - channelBuilder.useTransportSecurity(); - channelBuilder.disableRetry(); + + // set additional channel options (message size, keepalive, auth, etc) + GrpcChannelOptions.applyGrpcConfigurationToChannelBuilder( + configuration.getTransportStrategy().getGrpcConfiguration(), channelBuilder); + final List clientInterceptors = new ArrayList<>(); clientInterceptors.add(new UserHeaderInterceptor(credentialProvider.getAuthToken())); clientInterceptors.add( diff --git a/momento-sdk/src/main/java/momento/sdk/ScsTokenGrpcStubsManager.java b/momento-sdk/src/main/java/momento/sdk/ScsTokenGrpcStubsManager.java index 75a190f2..a86de78b 100644 --- a/momento-sdk/src/main/java/momento/sdk/ScsTokenGrpcStubsManager.java +++ b/momento-sdk/src/main/java/momento/sdk/ScsTokenGrpcStubsManager.java @@ -9,6 +9,8 @@ import java.util.concurrent.TimeUnit; import javax.annotation.Nonnull; import momento.sdk.auth.CredentialProvider; +import momento.sdk.config.transport.GrpcConfiguration; +import momento.sdk.internal.GrpcChannelOptions; import momento.token.TokenGrpc; /** @@ -34,8 +36,14 @@ final class ScsTokenGrpcStubsManager implements AutoCloseable { private static ManagedChannel setupConnection(CredentialProvider credentialProvider) { final NettyChannelBuilder channelBuilder = NettyChannelBuilder.forAddress(credentialProvider.getTokenEndpoint(), 443); - channelBuilder.useTransportSecurity(); - channelBuilder.disableRetry(); + + // Note: This is hard-coded for now but we may want to expose it via configuration object + // in the future, as we do with some of the other clients. + final GrpcConfiguration grpcConfig = new GrpcConfiguration(Duration.ofMillis(15000)); + + // set additional channel options (message size, keepalive, auth, etc) + GrpcChannelOptions.applyGrpcConfigurationToChannelBuilder(grpcConfig, channelBuilder); + final List clientInterceptors = new ArrayList<>(); clientInterceptors.add(new UserHeaderInterceptor(credentialProvider.getAuthToken())); channelBuilder.intercept(clientInterceptors); diff --git a/momento-sdk/src/main/java/momento/sdk/ScsTopicGrpcStubsManager.java b/momento-sdk/src/main/java/momento/sdk/ScsTopicGrpcStubsManager.java index 3b259e03..8c304ce3 100644 --- a/momento-sdk/src/main/java/momento/sdk/ScsTopicGrpcStubsManager.java +++ b/momento-sdk/src/main/java/momento/sdk/ScsTopicGrpcStubsManager.java @@ -7,10 +7,10 @@ import java.io.Closeable; import java.util.ArrayList; import java.util.List; -import java.util.concurrent.TimeUnit; import javax.annotation.Nonnull; import momento.sdk.auth.CredentialProvider; import momento.sdk.config.TopicConfiguration; +import momento.sdk.internal.GrpcChannelOptions; /** * Manager responsible for GRPC channels and stubs for the Topics. @@ -28,20 +28,19 @@ final class ScsTopicGrpcStubsManager implements Closeable { ScsTopicGrpcStubsManager( @Nonnull CredentialProvider credentialProvider, @Nonnull TopicConfiguration configuration) { - this.channel = setupConnection(credentialProvider); + this.channel = setupConnection(credentialProvider, configuration); this.stub = PubsubGrpc.newStub(channel); this.configuration = configuration; } - private static ManagedChannel setupConnection(CredentialProvider credentialProvider) { + private static ManagedChannel setupConnection( + CredentialProvider credentialProvider, TopicConfiguration configuration) { final NettyChannelBuilder channelBuilder = NettyChannelBuilder.forAddress(credentialProvider.getCacheEndpoint(), 443); - channelBuilder.useTransportSecurity(); - channelBuilder.disableRetry(); - channelBuilder.keepAliveTime(10, TimeUnit.SECONDS); - channelBuilder.keepAliveTimeout(5, TimeUnit.SECONDS); - channelBuilder.keepAliveWithoutCalls(true); + // set additional channel options (message size, keepalive, auth, etc) + GrpcChannelOptions.applyGrpcConfigurationToChannelBuilder( + configuration.getTransportStrategy().getGrpcConfiguration(), channelBuilder); final List clientInterceptors = new ArrayList<>(); clientInterceptors.add(new UserHeaderInterceptor(credentialProvider.getAuthToken())); diff --git a/momento-sdk/src/main/java/momento/sdk/config/Configurations.java b/momento-sdk/src/main/java/momento/sdk/config/Configurations.java index 34bb6ef8..928dae8b 100644 --- a/momento-sdk/src/main/java/momento/sdk/config/Configurations.java +++ b/momento-sdk/src/main/java/momento/sdk/config/Configurations.java @@ -137,11 +137,19 @@ private Lambda(TransportStrategy transportStrategy, RetryStrategy retryStrategy) *

This configuration may change in future releases to take advantage of improvements we * identify for default configurations. * + *

NOTE: keep-alives are very important for long-lived server environments where there may be + * periods of time when the connection is idle. However, they are very problematic for lambda + * environments where the lambda runtime is continuously frozen and unfrozen, because the lambda + * may be frozen before the "ACK" is received from the server. This can cause the keep-alive to + * timeout even though the connection is completely healthy. Therefore, keep-alives should be + * disabled in lambda and similar environments. + * * @return the latest Lambda configuration */ public static Configuration latest() { - final TransportStrategy transportStrategy = - new StaticTransportStrategy(new GrpcConfiguration(Duration.ofMillis(1100))); + final GrpcConfiguration grpcConfig = + new GrpcConfiguration(Duration.ofMillis(1100)).withKeepAliveDisabled(); + final TransportStrategy transportStrategy = new StaticTransportStrategy(grpcConfig); final RetryStrategy retryStrategy = new FixedCountRetryStrategy(DEFAULT_MAX_RETRIES, new DefaultRetryEligibilityStrategy()); return new Lambda(transportStrategy, retryStrategy); diff --git a/momento-sdk/src/main/java/momento/sdk/config/TopicConfigurations.java b/momento-sdk/src/main/java/momento/sdk/config/TopicConfigurations.java index b5055c19..d710dd39 100644 --- a/momento-sdk/src/main/java/momento/sdk/config/TopicConfigurations.java +++ b/momento-sdk/src/main/java/momento/sdk/config/TopicConfigurations.java @@ -28,8 +28,11 @@ private Laptop(TransportStrategy transportStrategy, Logger logger) { * @return the latest Laptop configuration */ public static TopicConfiguration latest() { - final TransportStrategy transportStrategy = - new StaticTransportStrategy(new GrpcConfiguration(Duration.ofMillis(15000))); + final GrpcConfiguration grpcConfig = + new GrpcConfiguration(Duration.ofMillis(15000)) + .withKeepAliveTime(10000) + .withKeepAliveTimeout(5000); + final TransportStrategy transportStrategy = new StaticTransportStrategy(grpcConfig); final Logger logger = LoggerFactory.getLogger(TopicConfiguration.class); return new Laptop(transportStrategy, logger); } @@ -54,8 +57,11 @@ private InRegion(TransportStrategy transportStrategy, Logger logger) { * @return the latest in-region configuration */ public static TopicConfiguration latest() { - final TransportStrategy transportStrategy = - new StaticTransportStrategy(new GrpcConfiguration(Duration.ofMillis(1100))); + final GrpcConfiguration grpcConfig = + new GrpcConfiguration(Duration.ofMillis(1100)) + .withKeepAliveTime(10000) + .withKeepAliveTimeout(5000); + final TransportStrategy transportStrategy = new StaticTransportStrategy(grpcConfig); final Logger logger = LoggerFactory.getLogger(TopicConfiguration.class); return new InRegion(transportStrategy, logger); } @@ -81,8 +87,11 @@ private LowLatency(TransportStrategy transportStrategy, Logger logger) { * @return the latest low-latency configuration */ public static TopicConfiguration latest() { - final TransportStrategy transportStrategy = - new StaticTransportStrategy(new GrpcConfiguration(Duration.ofMillis(500))); + final GrpcConfiguration grpcConfig = + new GrpcConfiguration(Duration.ofMillis(500)) + .withKeepAliveTime(10000) + .withKeepAliveTimeout(5000); + final TransportStrategy transportStrategy = new StaticTransportStrategy(grpcConfig); final Logger logger = LoggerFactory.getLogger(TopicConfiguration.class); return new LowLatency(transportStrategy, logger); } diff --git a/momento-sdk/src/main/java/momento/sdk/config/transport/GrpcConfiguration.java b/momento-sdk/src/main/java/momento/sdk/config/transport/GrpcConfiguration.java index fdbcc104..5ccf7fd4 100644 --- a/momento-sdk/src/main/java/momento/sdk/config/transport/GrpcConfiguration.java +++ b/momento-sdk/src/main/java/momento/sdk/config/transport/GrpcConfiguration.java @@ -3,13 +3,19 @@ import static momento.sdk.ValidationUtils.ensureRequestDeadlineValid; import java.time.Duration; +import java.util.Optional; import javax.annotation.Nonnull; +import momento.sdk.internal.GrpcChannelOptions; /** Abstracts away the gRPC configuration tunables. */ public class GrpcConfiguration { private final Duration deadline; private final int minNumGrpcChannels; + private final Optional maxMessageSize; + private final Optional keepAliveWithoutCalls; + private final Optional keepAliveTimeoutMs; + private final Optional keepAliveTimeMs; /** * Constructs a GrpcConfiguration. @@ -17,7 +23,13 @@ public class GrpcConfiguration { * @param deadline The maximum duration of a gRPC call. */ public GrpcConfiguration(@Nonnull Duration deadline) { - this(deadline, 1); + this( + deadline, + 1, + Optional.of(GrpcChannelOptions.DEFAULT_MAX_MESSAGE_SIZE), + Optional.of(GrpcChannelOptions.DEFAULT_KEEPALIVE_WITHOUT_STREAM), + Optional.of(GrpcChannelOptions.DEFAULT_KEEPALIVE_TIMEOUT_MS), + Optional.of(GrpcChannelOptions.DEFAULT_KEEPALIVE_TIME_MS)); } /** @@ -25,11 +37,27 @@ public GrpcConfiguration(@Nonnull Duration deadline) { * * @param deadline The maximum duration of a gRPC call. * @param minNumGrpcChannels The minimum number of gRPC channels to keep open at any given time. + * @param maxMessageSize The maximum size of a message (in bytes) that can be received by the + * client. + * @param keepAliveWithoutCalls Whether to send keepalive pings without any active calls. + * @param keepAliveTimeout The time to wait for a keepalive ping response before considering the + * connection dead. + * @param keepAliveTime The time to wait between keepalive pings. */ - public GrpcConfiguration(@Nonnull Duration deadline, int minNumGrpcChannels) { + public GrpcConfiguration( + @Nonnull Duration deadline, + int minNumGrpcChannels, + Optional maxMessageSize, + Optional keepAliveWithoutCalls, + Optional keepAliveTimeout, + Optional keepAliveTime) { ensureRequestDeadlineValid(deadline); this.deadline = deadline; this.minNumGrpcChannels = minNumGrpcChannels; + this.maxMessageSize = maxMessageSize; + this.keepAliveWithoutCalls = keepAliveWithoutCalls; + this.keepAliveTimeoutMs = keepAliveTimeout; + this.keepAliveTimeMs = keepAliveTime; } /** @@ -49,7 +77,13 @@ public Duration getDeadline() { * @return The updated GrpcConfiguration. */ public GrpcConfiguration withDeadline(Duration deadline) { - return new GrpcConfiguration(deadline); + return new GrpcConfiguration( + deadline, + minNumGrpcChannels, + maxMessageSize, + keepAliveWithoutCalls, + keepAliveTimeoutMs, + keepAliveTimeMs); } /** @@ -68,6 +102,158 @@ public int getMinNumGrpcChannels() { * @return The updated GrpcConfiguration. */ public GrpcConfiguration withMinNumGrpcChannels(int minNumGrpcChannels) { - return new GrpcConfiguration(deadline, minNumGrpcChannels); + return new GrpcConfiguration( + deadline, + minNumGrpcChannels, + maxMessageSize, + keepAliveWithoutCalls, + keepAliveTimeoutMs, + keepAliveTimeMs); + } + + /** + * The maximum size of a message (in bytes) that can be received by the client. + * + * @return the maximum message size. + */ + public Optional getMaxMessageSize() { + return maxMessageSize; + } + + /** + * Copy constructor that updates the maximum message size. + * + * @param maxMessageSize The new maximum message size. + * @return The updated GrpcConfiguration. + */ + public GrpcConfiguration withMaxMessageSize(int maxMessageSize) { + return new GrpcConfiguration( + deadline, + minNumGrpcChannels, + Optional.of(maxMessageSize), + keepAliveWithoutCalls, + keepAliveTimeoutMs, + keepAliveTimeMs); + } + + /** + * Whether keepalive will be performed when there are no outstanding requests on a connection. + * + * @return the boolean indicating whether to send keepalive pings without any active calls. + */ + public Optional getKeepAliveWithoutCalls() { + return keepAliveWithoutCalls; + } + + /** + * Copy constructor that updates whether keepalive will be performed when there are no outstanding + * requests on a connection. + * + *

NOTE: keep-alives are very important for long-lived server environments where there may be + * periods of time when the connection is idle. However, they are very problematic for lambda + * environments where the lambda runtime is continuously frozen and unfrozen, because the lambda + * may be frozen before the "ACK" is received from the server. This can cause the keep-alive to + * timeout even though the connection is completely healthy. Therefore, keep-alives should be + * disabled in lambda and similar environments. + * + * @param keepAliveWithoutCalls The boolean indicating whether to send keepalive pings without any + * active calls. + * @return The updated GrpcConfiguration. + */ + public GrpcConfiguration withKeepAliveWithoutCalls(Optional keepAliveWithoutCalls) { + return new GrpcConfiguration( + deadline, + minNumGrpcChannels, + maxMessageSize, + keepAliveWithoutCalls, + keepAliveTimeoutMs, + keepAliveTimeMs); + } + + /** + * The time to wait for a keepalive ping response before considering the connection dead. + * + * @return the time to wait for a keepalive ping response before considering the connection dead. + */ + public Optional getKeepAliveTimeoutMs() { + return keepAliveTimeoutMs; + } + + /** + * Copy constructor that updates the time to wait for a keepalive ping response before considering + * the connection dead. + * + *

NOTE: keep-alives are very important for long-lived server environments where there may be + * periods of time when the connection is idle. However, they are very problematic for lambda + * environments where the lambda runtime is continuously frozen and unfrozen, because the lambda + * may be frozen before the "ACK" is received from the server. This can cause the keep-alive to + * timeout even though the connection is completely healthy. Therefore, keep-alives should be + * disabled in lambda and similar environments. + * + * @param keepAliveTimeoutMs The new time to wait for a keepalive ping response. + * @return The updated GrpcConfiguration. + */ + public GrpcConfiguration withKeepAliveTimeout(int keepAliveTimeoutMs) { + return new GrpcConfiguration( + deadline, + minNumGrpcChannels, + maxMessageSize, + keepAliveWithoutCalls, + Optional.of(keepAliveTimeoutMs), + keepAliveTimeMs); + } + + /** + * The time to wait between keepalive pings. + * + * @return the time to wait between keepalive pings. + */ + public Optional getKeepAliveTimeMs() { + return keepAliveTimeMs; + } + + /** + * Copy constructor that updates the time to wait between keepalive pings. + * + *

NOTE: keep-alives are very important for long-lived server environments where there may be + * periods of time when the connection is idle. However, they are very problematic for lambda + * environments where the lambda runtime is continuously frozen and unfrozen, because the lambda + * may be frozen before the "ACK" is received from the server. This can cause the keep-alive to + * timeout even though the connection is completely healthy. Therefore, keep-alives should be + * disabled in lambda and similar environments. + * + * @param keepAliveTimeMs The new time to wait between keepalive pings. + * @return The updated GrpcConfiguration. + */ + public GrpcConfiguration withKeepAliveTime(int keepAliveTimeMs) { + return new GrpcConfiguration( + deadline, + minNumGrpcChannels, + maxMessageSize, + keepAliveWithoutCalls, + keepAliveTimeoutMs, + Optional.of(keepAliveTimeMs)); + } + + /** + * Copy constructor that disables all client-side keepalive settings. + * + *

NOTE: keep-alives are very important for long-lived server environments where there may be + * periods of time when the connection is idle. However, they are very problematic for lambda + * environments where the lambda runtime is continuously frozen and unfrozen, because the lambda + * may be frozen before the "ACK" is received from the server. This can cause the keep-alive to + * timeout even though the connection is completely healthy. Therefore, keep-alives should be + * disabled in lambda and similar environments. + * + * @return The updated GrpcConfiguration. + */ + public GrpcConfiguration withKeepAliveDisabled() { + return new GrpcConfiguration( + deadline, + minNumGrpcChannels, + maxMessageSize, + Optional.empty(), + Optional.empty(), + Optional.empty()); } } diff --git a/momento-sdk/src/main/java/momento/sdk/internal/GrpcChannelOptions.java b/momento-sdk/src/main/java/momento/sdk/internal/GrpcChannelOptions.java new file mode 100644 index 00000000..fe86e721 --- /dev/null +++ b/momento-sdk/src/main/java/momento/sdk/internal/GrpcChannelOptions.java @@ -0,0 +1,45 @@ +package momento.sdk.internal; + +import io.grpc.netty.shaded.io.grpc.netty.NettyChannelBuilder; +import java.util.Optional; +import java.util.concurrent.TimeUnit; +import momento.sdk.config.transport.GrpcConfiguration; + +public class GrpcChannelOptions { + // The default value for max_send_message_length is 4mb. We need to increase this to 5mb in order + // to + // support cases where users have requested a limit increase up to our maximum item size of 5mb. + public static final int DEFAULT_MAX_MESSAGE_SIZE = 5_243_000; // bytes + + public static final boolean DEFAULT_KEEPALIVE_WITHOUT_STREAM = true; + public static final int DEFAULT_KEEPALIVE_TIME_MS = 5000; // milliseconds + public static final int DEFAULT_KEEPALIVE_TIMEOUT_MS = 1000; // milliseconds + + public static void applyGrpcConfigurationToChannelBuilder( + GrpcConfiguration grpcConfig, NettyChannelBuilder channelBuilder) { + channelBuilder.useTransportSecurity(); + channelBuilder.disableRetry(); + + final Optional maxMessageSize = grpcConfig.getMaxMessageSize(); + if (maxMessageSize.isPresent()) { + channelBuilder.maxInboundMessageSize(maxMessageSize.get()); + } + + // no equivalent for maxOutboundboundMessageSize + + final Optional keepAliveTimeMs = grpcConfig.getKeepAliveTimeMs(); + if (keepAliveTimeMs.isPresent()) { + channelBuilder.keepAliveTime(keepAliveTimeMs.get(), TimeUnit.MILLISECONDS); + } + + final Optional keepAliveTimeoutMs = grpcConfig.getKeepAliveTimeoutMs(); + if (keepAliveTimeoutMs.isPresent()) { + channelBuilder.keepAliveTimeout(keepAliveTimeoutMs.get(), TimeUnit.MILLISECONDS); + } + + final Optional keepAliveWithoutCalls = grpcConfig.getKeepAliveWithoutCalls(); + if (keepAliveWithoutCalls.isPresent()) { + channelBuilder.keepAliveWithoutCalls(keepAliveWithoutCalls.get()); + } + } +} diff --git a/momento-sdk/src/test/java/momento/sdk/Configurations.java b/momento-sdk/src/test/java/momento/sdk/Configurations.java new file mode 100644 index 00000000..8cdfabfe --- /dev/null +++ b/momento-sdk/src/test/java/momento/sdk/Configurations.java @@ -0,0 +1,38 @@ +package momento.sdk; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import momento.sdk.config.Configuration; +import momento.sdk.config.TopicConfiguration; +import momento.sdk.config.transport.GrpcConfiguration; +import org.junit.jupiter.api.Test; + +public class Configurations { + @Test + public void testCacheLambdaConfigurationDisablesKeepalive() { + final Configuration config = momento.sdk.config.Configurations.Lambda.latest(); + final GrpcConfiguration grpcConfig = config.getTransportStrategy().getGrpcConfiguration(); + assertThat(grpcConfig.getKeepAliveWithoutCalls().isEmpty()); + assertTrue(grpcConfig.getKeepAliveTimeMs().isEmpty()); + assertTrue(grpcConfig.getKeepAliveTimeoutMs().isEmpty()); + } + + @Test + public void testCacheLaptopConfigurationEnablesKeepalive() { + final Configuration config = momento.sdk.config.Configurations.Laptop.latest(); + final GrpcConfiguration grpcConfig = config.getTransportStrategy().getGrpcConfiguration(); + assertTrue(grpcConfig.getKeepAliveWithoutCalls().get()); + assertThat(grpcConfig.getKeepAliveTimeMs().get()).isEqualTo(5000); + assertThat(grpcConfig.getKeepAliveTimeoutMs().get()).isEqualTo(1000); + } + + @Test + public void testTopicsLaptopConfigurationEnablesKeepalive() { + final TopicConfiguration config = momento.sdk.config.TopicConfigurations.Laptop.latest(); + final GrpcConfiguration grpcConfig = config.getTransportStrategy().getGrpcConfiguration(); + assertTrue(grpcConfig.getKeepAliveWithoutCalls().get()); + assertThat(grpcConfig.getKeepAliveTimeMs().get()).isEqualTo(10000); + assertThat(grpcConfig.getKeepAliveTimeoutMs().get()).isEqualTo(5000); + } +}