diff --git a/src/Momento.Sdk/CacheClient.cs b/src/Momento.Sdk/CacheClient.cs
index 0e0bf4b1..0d718142 100644
--- a/src/Momento.Sdk/CacheClient.cs
+++ b/src/Momento.Sdk/CacheClient.cs
@@ -35,6 +35,25 @@ private ScsDataClient DataClient
protected readonly IConfiguration config;
///
protected readonly ILogger _logger;
+
+ ///
+ /// Async factory function to construct a Momento CacheClient with an eager connection to the
+ /// Momento server. Calling the CacheClient constructor directly will not establish a connection
+ /// immediately, but instead establish it lazily when the first request is issued. This factory
+ /// function will ensure that the connection is established before the first request.
+ ///
+ /// Configuration to use for the transport, retries, middlewares. See for out-of-the-box configuration choices, eg
+ /// Momento auth provider.
+ /// Default time to live for the item in cache.
+ /// Maximum time to wait for eager connection.
+ /// is zero or negative.
+ /// The eager connection could not be established within the specified
+ public static async Task CreateAsync(IConfiguration config, ICredentialProvider authProvider, TimeSpan defaultTtl, TimeSpan eagerConnectionTimeout)
+ {
+ CacheClient cacheClient = new CacheClient(config, authProvider, defaultTtl);
+ await cacheClient.DataClient.EagerConnectAsync(eagerConnectionTimeout);
+ return cacheClient;
+ }
///
diff --git a/src/Momento.Sdk/Config/Configurations.cs b/src/Momento.Sdk/Config/Configurations.cs
index 2d7e8a08..dc1f640e 100644
--- a/src/Momento.Sdk/Config/Configurations.cs
+++ b/src/Momento.Sdk/Config/Configurations.cs
@@ -184,11 +184,7 @@ private Lambda(ILoggerFactory loggerFactory, IRetryStrategy retryStrategy, ITran
///
public static IConfiguration Latest(ILoggerFactory? loggerFactory = null)
{
- var config = Default.V1(loggerFactory);
- var transportStrategy = config.TransportStrategy.WithEagerConnectionTimeout(
- TimeSpan.FromSeconds(30)
- );
- return config.WithTransportStrategy(transportStrategy);
+ return Default.V1(loggerFactory);
}
}
}
diff --git a/src/Momento.Sdk/Config/Transport/ITransportStrategy.cs b/src/Momento.Sdk/Config/Transport/ITransportStrategy.cs
index 0d894552..78849ec5 100644
--- a/src/Momento.Sdk/Config/Transport/ITransportStrategy.cs
+++ b/src/Momento.Sdk/Config/Transport/ITransportStrategy.cs
@@ -14,14 +14,6 @@ public interface ITransportStrategy
///
public int MaxConcurrentRequests { get; }
- ///
- /// If null, the client will only attempt to connect to the server lazily when the first request is executed.
- /// If provided, the client will attempt to connect to the server immediately upon construction; if the connection
- /// cannot be established within the specified TimeSpan, it will abort the connection attempt, log a warning,
- /// and proceed with execution so that the application doesn't hang.
- ///
- public TimeSpan? EagerConnectionTimeout { get; }
-
///
/// Configures the low-level gRPC settings for the Momento client's communication
/// with the Momento server.
@@ -50,15 +42,4 @@ public interface ITransportStrategy
///
/// A new ITransportStrategy with the specified client timeout
public ITransportStrategy WithClientTimeout(TimeSpan clientTimeout);
-
- ///
- /// Copy constructor to enable eager connection to the server
- ///
- /// A timeout for attempting an eager connection to the server. When the client
- /// is constructed, it will attempt to connect to the server immediately. If the connection
- /// cannot be established within the specified TimeSpan, it will abort the connection attempt, log a warning,
- /// and proceed with execution so that the application doesn't hang.
- ///
- /// A new ITransportStrategy configured to eagerly connect to the server upon construction
- public ITransportStrategy WithEagerConnectionTimeout(TimeSpan connectionTimeout);
}
diff --git a/src/Momento.Sdk/Exceptions/ConnectionException.cs b/src/Momento.Sdk/Exceptions/ConnectionException.cs
new file mode 100644
index 00000000..87ced02e
--- /dev/null
+++ b/src/Momento.Sdk/Exceptions/ConnectionException.cs
@@ -0,0 +1,15 @@
+namespace Momento.Sdk.Exceptions;
+
+using System;
+
+///
+/// Unable to connect to the server.
+///
+public class ConnectionException : SdkException
+{
+ ///
+ public ConnectionException(string message, MomentoErrorTransportDetails transportDetails, Exception? e = null) : base(MomentoErrorCode.CONNECTION_ERROR, message, transportDetails, e)
+ {
+ this.MessageWrapper = "Unable to connect to the server; consider retrying. If the error persists, please contact us at support@momentohq.com";
+ }
+}
diff --git a/src/Momento.Sdk/Exceptions/SdkException.cs b/src/Momento.Sdk/Exceptions/SdkException.cs
index cf3c5ab6..2822fca8 100644
--- a/src/Momento.Sdk/Exceptions/SdkException.cs
+++ b/src/Momento.Sdk/Exceptions/SdkException.cs
@@ -66,6 +66,10 @@ public enum MomentoErrorCode
///
FAILED_PRECONDITION_ERROR,
///
+ /// Unable to connect to the server
+ ///
+ CONNECTION_ERROR,
+ ///
/// Unknown error has occurred
///
UNKNOWN_ERROR
diff --git a/src/Momento.Sdk/Internal/DataGrpcManager.cs b/src/Momento.Sdk/Internal/DataGrpcManager.cs
index b1210b1f..06d3eeff 100644
--- a/src/Momento.Sdk/Internal/DataGrpcManager.cs
+++ b/src/Momento.Sdk/Internal/DataGrpcManager.cs
@@ -15,6 +15,7 @@
using Momento.Sdk.Config;
using Momento.Sdk.Config.Middleware;
using Momento.Sdk.Config.Retry;
+using Momento.Sdk.Exceptions;
using Momento.Sdk.Internal.Middleware;
using static System.Reflection.Assembly;
@@ -299,24 +300,25 @@ internal DataGrpcManager(IConfiguration config, string authToken, string endpoin
).ToList();
var client = new Scs.ScsClient(invoker);
-
- if (config.TransportStrategy.EagerConnectionTimeout != null)
+ Client = new DataClientWithMiddleware(client, middlewares);
+ }
+
+ internal async Task EagerConnectAsync(TimeSpan eagerConnectionTimeout)
+ {
+ _logger.LogDebug("Attempting eager connection to server");
+ var pingClient = new Ping.PingClient(this.channel);
+ try
{
- TimeSpan eagerConnectionTimeout = config.TransportStrategy.EagerConnectionTimeout.Value;
- _logger.LogDebug("TransportStrategy EagerConnection is enabled; attempting to connect to server");
- var pingClient = new Ping.PingClient(this.channel);
- try
- {
- pingClient.Ping(new _PingRequest(),
- new CallOptions(deadline: DateTime.UtcNow.Add(eagerConnectionTimeout)));
- }
- catch (RpcException ex)
- {
- _logger.LogWarning($"Failed to eagerly connect to the server; continuing with execution in case failure is recoverable later: {ex}");
- }
+ await pingClient.PingAsync(new _PingRequest(),
+ new CallOptions(deadline: DateTime.UtcNow.Add(eagerConnectionTimeout)));
+ }
+ catch (RpcException ex)
+ {
+ MomentoErrorTransportDetails transportDetails = new MomentoErrorTransportDetails(
+ new MomentoGrpcErrorDetails(ex.StatusCode, ex.Message, null)
+ );
+ throw new ConnectionException("Eager connection to server failed", transportDetails, ex);
}
-
- Client = new DataClientWithMiddleware(client, middlewares);
}
public void Dispose()
diff --git a/src/Momento.Sdk/Internal/ScsDataClient.cs b/src/Momento.Sdk/Internal/ScsDataClient.cs
index 3750f1ab..024b1271 100644
--- a/src/Momento.Sdk/Internal/ScsDataClient.cs
+++ b/src/Momento.Sdk/Internal/ScsDataClient.cs
@@ -33,6 +33,11 @@ public ScsDataClientBase(IConfiguration config, string authToken, string endpoin
this._logger = config.LoggerFactory.CreateLogger();
this._exceptionMapper = new CacheExceptionMapper(config.LoggerFactory);
}
+
+ internal Task EagerConnectAsync(TimeSpan eagerConnectionTimeout)
+ {
+ return this.grpcManager.EagerConnectAsync(eagerConnectionTimeout);
+ }
protected Metadata MetadataWithCache(string cacheName)
{
diff --git a/tests/Integration/Momento.Sdk.Tests/CacheEagerConnectionTest.cs b/tests/Integration/Momento.Sdk.Tests/CacheEagerConnectionTest.cs
index c8e3f286..6b1541e4 100644
--- a/tests/Integration/Momento.Sdk.Tests/CacheEagerConnectionTest.cs
+++ b/tests/Integration/Momento.Sdk.Tests/CacheEagerConnectionTest.cs
@@ -29,15 +29,6 @@ public void CacheClientConstructor_LazyConnection()
var client = new CacheClient(config, authProvider, defaultTtl);
}
- [Fact]
- public void CacheClientConstructor_EagerConnection_Success()
- {
- var config = Configurations.Laptop.Latest(loggerFactory);
- config = config.WithTransportStrategy(config.TransportStrategy.WithEagerConnectionTimeout(TimeSpan.FromSeconds(5)));
- // just validating that we can construct the client when the eager connection is successful
- var client = new CacheClient(config, authProvider, defaultTtl);
- }
-
[Fact]
public void CacheClientConstructor_WithChannelsAndMaxConn_Success()
{
@@ -56,13 +47,12 @@ public void CacheClientConstructor_WithChannelsAndMaxConn_Success()
}
[Fact]
- public void CacheClientConstructor_EagerConnection_BadEndpoint()
+ public async void CacheClientCreate_EagerConnection_BadEndpoint()
{
var config = Configurations.Laptop.Latest(loggerFactory);
- config = config.WithTransportStrategy(config.TransportStrategy.WithEagerConnectionTimeout(TimeSpan.FromSeconds(2)));
var authProviderWithBadCacheEndpoint = authProvider.WithCacheEndpoint("cache.cell-external-beta-1.prod.a.momentohq.com:65000");
Console.WriteLine($"Hello developer! We are about to run a test that verifies that the cache client is still operational even if our eager connection (ping) fails. So you will see the test log a warning message about that. It's expected, don't worry!");
- // validating that the constructor doesn't fail when the eager connection fails
- var client = new CacheClient(config, authProviderWithBadCacheEndpoint, defaultTtl);
+
+ await Assert.ThrowsAsync(async () => await CacheClient.CreateAsync(config, authProviderWithBadCacheEndpoint, defaultTtl, TimeSpan.FromSeconds(2)));
}
}
diff --git a/tests/Unit/Momento.Sdk.Tests/ConfigTest.cs b/tests/Unit/Momento.Sdk.Tests/ConfigTest.cs
index 9b86fecd..78e49fda 100644
--- a/tests/Unit/Momento.Sdk.Tests/ConfigTest.cs
+++ b/tests/Unit/Momento.Sdk.Tests/ConfigTest.cs
@@ -19,11 +19,4 @@ public void V1VConfigs_EqualLatest_HappyPath()
Assert.Equal(Configurations.InRegion.Default.Latest(), Configurations.InRegion.Default.V1());
Assert.Equal(Configurations.InRegion.LowLatency.Latest(), Configurations.InRegion.LowLatency.V1());
}
-
- [Fact]
- public void LambdaLatest_HasEagerConnectionTimeout_HappyPath()
- {
- var config = Configurations.InRegion.Lambda.Latest();
- Assert.Equal(TimeSpan.FromSeconds(30), config.TransportStrategy.EagerConnectionTimeout);
- }
}