Skip to content

Commit 44b020c

Browse files
2 parents af0f248 + 34c5afd commit 44b020c

File tree

8 files changed

+69
-9
lines changed

8 files changed

+69
-9
lines changed

src/momento/errors/error_details.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,8 @@ class MomentoErrorCode(enum.Enum):
4242
"""System is not in a state required for the operation's execution"""
4343
UNKNOWN_ERROR = 15
4444
"""Unknown error has occurred"""
45+
CONNECTION_ERROR = 16
46+
"""Connection to the Momento server failed"""
4547

4648

4749
@dataclass

src/momento/errors/exceptions.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,24 @@ def __init__(
123123
)
124124

125125

126+
class ConnectionException(SdkException):
127+
"""Connection to the Momento server failed."""
128+
129+
def __init__(
130+
self,
131+
message: str,
132+
service: Service,
133+
transport_details: Optional[MomentoErrorTransportDetails] = None,
134+
):
135+
super().__init__(
136+
message,
137+
MomentoErrorCode.CONNECTION_ERROR,
138+
service,
139+
transport_details,
140+
message_wrapper="Connection to the Momento server failed; please contact us at [email protected]",
141+
)
142+
143+
126144
class FailedPreconditionException(SdkException):
127145
"""The server did not meet the precondition to run a command.
128146

src/momento/internal/aio/_scs_grpc_manager.py

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
from momento.auth import CredentialProvider
1313
from momento.config import Configuration, TopicConfiguration
1414
from momento.config.transport.transport_strategy import StaticGrpcConfiguration
15+
from momento.errors.exceptions import ConnectionException
1516
from momento.internal._utilities import momento_version
1617
from momento.internal._utilities._channel_credentials import (
1718
channel_credentials_from_root_certs_or_default,
@@ -20,6 +21,7 @@
2021
grpc_control_channel_options_from_grpc_config,
2122
grpc_data_channel_options_from_grpc_config,
2223
)
24+
from momento.internal.services import Service
2325
from momento.retry import RetryStrategy
2426

2527
from ... import logs
@@ -90,9 +92,11 @@ async def eagerly_connect(self, timeout_seconds: float) -> None:
9092
try:
9193
await asyncio.wait_for(self.wait_for_ready(), timeout_seconds)
9294
except Exception as error:
95+
self._secure_channel.close()
9396
self._logger.debug(f"Failed to connect to the server within the given timeout. {error}")
94-
raise RuntimeError(
95-
f"Failed to connect to Momento's server within given eager connection timeout {error}"
97+
raise ConnectionException(
98+
message=f"Failed to connect to Momento's server within given eager connection timeout: {error}",
99+
service=Service.CACHE,
96100
) from error
97101

98102
async def wait_for_ready(self) -> None:
@@ -107,7 +111,7 @@ async def wait_for_ready(self) -> None:
107111
elif latest_state == connecting:
108112
self._logger.debug("State transitioned to CONNECTING; waiting to get READY")
109113
else:
110-
self._logger.warn(f"Unexpected connection state: {latest_state}. while trying to eagerly connect")
114+
self._logger.warn(f"Unexpected connection state while trying to eagerly connect: {latest_state}.")
111115
break
112116

113117
# This is a gRPC callback helper that prevents us from repeatedly polling on the state

src/momento/internal/synchronous/_scs_grpc_manager.py

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
from momento.auth import CredentialProvider
1414
from momento.config import Configuration, TopicConfiguration
1515
from momento.config.transport.transport_strategy import StaticGrpcConfiguration
16+
from momento.errors.exceptions import ConnectionException
1617
from momento.internal._utilities import momento_version
1718
from momento.internal._utilities._channel_credentials import (
1819
channel_credentials_from_root_certs_or_default,
@@ -21,6 +22,7 @@
2122
grpc_control_channel_options_from_grpc_config,
2223
grpc_data_channel_options_from_grpc_config,
2324
)
25+
from momento.internal.services import Service
2426
from momento.internal.synchronous._add_header_client_interceptor import (
2527
AddHeaderClientInterceptor,
2628
AddHeaderStreamingClientInterceptor,
@@ -97,7 +99,11 @@ def on_timeout() -> None:
9799
)
98100
# the subscription is no longer needed; it was only meant to watch if we could connect eagerly
99101
self._secure_channel.unsubscribe(on_state_change)
100-
raise RuntimeError("Failed to connect to Momento's server within given eager connection timeout")
102+
self._secure_channel.close()
103+
raise ConnectionException(
104+
message="Failed to connect to Momento's server within given eager connection timeout",
105+
service=Service.CACHE,
106+
)
101107

102108
"""
103109
A callback that is triggered whenever a connection's state changes. We explicitly subscribe to
@@ -122,7 +128,7 @@ def on_state_change(state: grpc.ChannelConnectivity) -> None:
122128
elif state == connecting:
123129
self._logger.debug("State transitioned to CONNECTING; waiting to get READY")
124130
else:
125-
self._logger.warn(f"Unexpected connection state: {state}. while trying to eagerly connect")
131+
self._logger.warn(f"Unexpected connection state while trying to eagerly connect: {state}")
126132
# we could not connect within the timeout and we no longer need this subscription
127133
self._secure_channel.unsubscribe(on_state_change)
128134
connection_event.set()

tests/momento/cache_client/test_init.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
from momento.auth import CredentialProvider
77
from momento.config import Configuration
88
from momento.errors import InvalidArgumentException
9+
from momento.errors.exceptions import ConnectionException
910

1011

1112
def test_init_throws_exception_when_client_uses_negative_default_ttl(
@@ -52,3 +53,14 @@ def test_init_eagerly_connecting_cache_client(
5253
configuration, credential_provider, default_ttl_seconds, eager_connection_timeout=timedelta(seconds=30)
5354
)
5455
assert client
56+
57+
58+
def test_init_cache_client_eager_connection_failure(
59+
configuration: Configuration, credential_provider: CredentialProvider, default_ttl_seconds: timedelta
60+
) -> None:
61+
with pytest.raises(
62+
ConnectionException, match="Failed to connect to Momento's server within given eager connection timeout"
63+
):
64+
CacheClient.create(
65+
configuration, credential_provider, default_ttl_seconds, eager_connection_timeout=timedelta(milliseconds=1)
66+
)

tests/momento/cache_client/test_init_async.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
from momento.auth import CredentialProvider
77
from momento.config import Configuration
88
from momento.errors import InvalidArgumentException
9+
from momento.errors.exceptions import ConnectionException
910

1011

1112
async def test_init_throws_exception_when_client_uses_negative_default_ttl(
@@ -54,3 +55,14 @@ async def test_init_eagerly_connecting_cache_client(
5455
configuration, credential_provider, default_ttl_seconds, eager_connection_timeout=timedelta(seconds=30)
5556
)
5657
assert client
58+
59+
60+
async def test_init_cache_client_eager_connection_failure(
61+
configuration: Configuration, credential_provider: CredentialProvider, default_ttl_seconds: timedelta
62+
) -> None:
63+
with pytest.raises(
64+
ConnectionException, match="Failed to connect to Momento's server within given eager connection timeout"
65+
):
66+
await CacheClientAsync.create(
67+
configuration, credential_provider, default_ttl_seconds, eager_connection_timeout=timedelta(milliseconds=1)
68+
)

tests/momento/vector_index_client/test_control.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -145,8 +145,11 @@ def test_create_index_throws_authentication_exception_for_bad_token(
145145
response = vector_index_client.create_index(index_name, num_dimensions=2)
146146
assert isinstance(response, CreateIndex.Error)
147147
assert response.error_code == MomentoErrorCode.AUTHENTICATION_ERROR
148-
assert response.inner_exception.message == "Invalid signature"
149-
assert response.message == "Invalid authentication credentials to connect to index service: Invalid signature"
148+
assert response.inner_exception.message == "Could not validate authorization token"
149+
assert (
150+
response.message
151+
== "Invalid authentication credentials to connect to index service: Could not validate authorization token"
152+
)
150153

151154

152155
# List indexes

tests/momento/vector_index_client/test_control_async.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -151,8 +151,11 @@ async def test_create_index_throws_authentication_exception_for_bad_token(
151151
response = await vector_index_client.create_index(index_name, num_dimensions=2)
152152
assert isinstance(response, CreateIndex.Error)
153153
assert response.error_code == MomentoErrorCode.AUTHENTICATION_ERROR
154-
assert response.inner_exception.message == "Invalid signature"
155-
assert response.message == "Invalid authentication credentials to connect to index service: Invalid signature"
154+
assert response.inner_exception.message == "Could not validate authorization token"
155+
assert (
156+
response.message
157+
== "Invalid authentication credentials to connect to index service: Could not validate authorization token"
158+
)
156159

157160

158161
# List indexes

0 commit comments

Comments
 (0)