valkey-glide 2.0.0rc3__cp310-cp310-macosx_11_0_arm64.whl → 2.2.3__cp310-cp310-macosx_11_0_arm64.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- glide/__init__.py +160 -106
- glide/async_commands/cluster_commands.py +108 -105
- glide/async_commands/core.py +637 -444
- glide/async_commands/{server_modules/ft.py → ft.py} +8 -7
- glide/async_commands/{server_modules/glide_json.py → glide_json.py} +15 -92
- glide/async_commands/standalone_commands.py +27 -58
- glide/glide.cpython-310-darwin.so +0 -0
- glide/glide.pyi +26 -1
- glide/glide_client.py +269 -125
- glide/logger.py +33 -21
- glide/opentelemetry.py +185 -0
- glide_shared/__init__.py +330 -0
- glide_shared/commands/__init__.py +0 -0
- {glide/async_commands → glide_shared/commands}/batch.py +476 -64
- glide_shared/commands/batch_options.py +261 -0
- glide_shared/commands/core_options.py +407 -0
- {glide/async_commands → glide_shared/commands}/server_modules/ft_options/ft_aggregate_options.py +3 -3
- {glide/async_commands → glide_shared/commands}/server_modules/ft_options/ft_create_options.py +4 -2
- {glide/async_commands → glide_shared/commands}/server_modules/ft_options/ft_profile_options.py +4 -4
- {glide/async_commands → glide_shared/commands}/server_modules/ft_options/ft_search_options.py +4 -2
- {glide/async_commands → glide_shared/commands}/server_modules/json_batch.py +4 -4
- glide_shared/commands/server_modules/json_options.py +93 -0
- {glide/async_commands → glide_shared/commands}/sorted_set.py +2 -2
- {glide/async_commands → glide_shared/commands}/stream.py +1 -1
- {glide → glide_shared}/config.py +386 -61
- {glide → glide_shared}/constants.py +3 -3
- {glide → glide_shared}/exceptions.py +27 -1
- glide_shared/protobuf/command_request_pb2.py +56 -0
- glide_shared/protobuf/connection_request_pb2.py +56 -0
- {glide → glide_shared}/protobuf/response_pb2.py +6 -6
- {glide → glide_shared}/routes.py +54 -15
- valkey_glide-2.2.3.dist-info/METADATA +211 -0
- valkey_glide-2.2.3.dist-info/RECORD +40 -0
- glide/protobuf/command_request_pb2.py +0 -54
- glide/protobuf/command_request_pb2.pyi +0 -1187
- glide/protobuf/connection_request_pb2.py +0 -54
- glide/protobuf/connection_request_pb2.pyi +0 -320
- glide/protobuf/response_pb2.pyi +0 -100
- valkey_glide-2.0.0rc3.dist-info/METADATA +0 -127
- valkey_glide-2.0.0rc3.dist-info/RECORD +0 -37
- {glide/async_commands → glide_shared/commands}/bitmap.py +0 -0
- {glide/async_commands → glide_shared/commands}/command_args.py +0 -0
- {glide/async_commands → glide_shared/commands}/server_modules/ft_options/ft_constants.py +0 -0
- {glide → glide_shared}/protobuf_codec.py +0 -0
- {valkey_glide-2.0.0rc3.dist-info → valkey_glide-2.2.3.dist-info}/WHEEL +0 -0
{glide → glide_shared}/config.py
RENAMED
|
@@ -6,12 +6,18 @@ from dataclasses import dataclass
|
|
|
6
6
|
from enum import Enum, IntEnum
|
|
7
7
|
from typing import Any, Callable, Dict, List, Optional, Set, Tuple, Union
|
|
8
8
|
|
|
9
|
-
from
|
|
10
|
-
from
|
|
11
|
-
from
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
from
|
|
9
|
+
from glide_shared.commands.core_options import PubSubMsg
|
|
10
|
+
from glide_shared.exceptions import ConfigurationError
|
|
11
|
+
from glide_shared.protobuf.connection_request_pb2 import (
|
|
12
|
+
ConnectionRequest,
|
|
13
|
+
)
|
|
14
|
+
from glide_shared.protobuf.connection_request_pb2 import (
|
|
15
|
+
ProtocolVersion as SentProtocolVersion,
|
|
16
|
+
)
|
|
17
|
+
from glide_shared.protobuf.connection_request_pb2 import ReadFrom as ProtobufReadFrom
|
|
18
|
+
from glide_shared.protobuf.connection_request_pb2 import (
|
|
19
|
+
TlsMode,
|
|
20
|
+
)
|
|
15
21
|
|
|
16
22
|
|
|
17
23
|
class NodeAddress:
|
|
@@ -73,7 +79,7 @@ class BackoffStrategy:
|
|
|
73
79
|
"""
|
|
74
80
|
Represents the strategy used to determine how and when to reconnect, in case of connection failures.
|
|
75
81
|
The time between attempts grows exponentially, to the formula rand(0 .. factor * (exponentBase ^ N)), where N
|
|
76
|
-
is the number of failed attempts.
|
|
82
|
+
is the number of failed attempts, and rand(...) applies a jitter of up to jitter_percent% to introduce randomness and reduce retry storms.
|
|
77
83
|
Once the maximum value is reached, that will remain the time between retry attempts until a reconnect attempt is
|
|
78
84
|
successful.
|
|
79
85
|
The client will attempt to reconnect indefinitely.
|
|
@@ -83,6 +89,7 @@ class BackoffStrategy:
|
|
|
83
89
|
where the time between retries increases. Once the retries have reached the maximum value, the time between
|
|
84
90
|
retries will remain constant until a reconnect attempt is succesful.
|
|
85
91
|
factor (int): The multiplier that will be applied to the waiting time between each retry.
|
|
92
|
+
This value is specified in milliseconds.
|
|
86
93
|
exponent_base (int): The exponent base configured for the strategy.
|
|
87
94
|
jitter_percent (Optional[int]): The Jitter percent on the calculated duration. If not set, a default value will be used.
|
|
88
95
|
"""
|
|
@@ -100,23 +107,92 @@ class BackoffStrategy:
|
|
|
100
107
|
self.jitter_percent = jitter_percent
|
|
101
108
|
|
|
102
109
|
|
|
110
|
+
class ServiceType(Enum):
|
|
111
|
+
"""
|
|
112
|
+
Represents the types of AWS services that can be used for IAM authentication.
|
|
113
|
+
"""
|
|
114
|
+
|
|
115
|
+
ELASTICACHE = 0
|
|
116
|
+
"""Amazon ElastiCache service."""
|
|
117
|
+
MEMORYDB = 1
|
|
118
|
+
"""Amazon MemoryDB service."""
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
class IamAuthConfig:
|
|
122
|
+
"""
|
|
123
|
+
Configuration settings for IAM authentication.
|
|
124
|
+
|
|
125
|
+
Attributes:
|
|
126
|
+
cluster_name (str): The name of the ElastiCache/MemoryDB cluster.
|
|
127
|
+
service (ServiceType): The type of service being used (ElastiCache or MemoryDB).
|
|
128
|
+
region (str): The AWS region where the ElastiCache/MemoryDB cluster is located.
|
|
129
|
+
refresh_interval_seconds (Optional[int]): Optional refresh interval in seconds for renewing IAM authentication tokens.
|
|
130
|
+
If not provided, the core will use a default value of 300 seconds (5 min).
|
|
131
|
+
"""
|
|
132
|
+
|
|
133
|
+
def __init__(
|
|
134
|
+
self,
|
|
135
|
+
cluster_name: str,
|
|
136
|
+
service: ServiceType,
|
|
137
|
+
region: str,
|
|
138
|
+
refresh_interval_seconds: Optional[int] = None,
|
|
139
|
+
):
|
|
140
|
+
self.cluster_name = cluster_name
|
|
141
|
+
self.service = service
|
|
142
|
+
self.region = region
|
|
143
|
+
self.refresh_interval_seconds = refresh_interval_seconds
|
|
144
|
+
|
|
145
|
+
|
|
103
146
|
class ServerCredentials:
|
|
104
147
|
"""
|
|
105
148
|
Represents the credentials for connecting to a server.
|
|
106
149
|
|
|
150
|
+
Exactly one of the following authentication modes must be provided:
|
|
151
|
+
- Password-based authentication: Use password (and optionally username)
|
|
152
|
+
- IAM authentication: Use username (required) and iam_config
|
|
153
|
+
|
|
154
|
+
These modes are mutually exclusive - you cannot use both simultaneously.
|
|
155
|
+
|
|
107
156
|
Attributes:
|
|
108
|
-
password (str): The password that will be used for authenticating connections to the servers.
|
|
157
|
+
password (Optional[str]): The password that will be used for authenticating connections to the servers.
|
|
158
|
+
Mutually exclusive with iam_config. Either password or iam_config must be provided.
|
|
109
159
|
username (Optional[str]): The username that will be used for authenticating connections to the servers.
|
|
110
|
-
If not supplied, "default" will be used.
|
|
160
|
+
If not supplied for password-based authentication, "default" will be used.
|
|
161
|
+
Required for IAM authentication.
|
|
162
|
+
iam_config (Optional[IamAuthConfig]): IAM authentication configuration. Mutually exclusive with password.
|
|
163
|
+
Either password or iam_config must be provided.
|
|
164
|
+
The client will automatically generate and refresh the authentication token based on the provided configuration.
|
|
111
165
|
"""
|
|
112
166
|
|
|
113
167
|
def __init__(
|
|
114
168
|
self,
|
|
115
|
-
password: str,
|
|
169
|
+
password: Optional[str] = None,
|
|
116
170
|
username: Optional[str] = None,
|
|
171
|
+
iam_config: Optional[IamAuthConfig] = None,
|
|
117
172
|
):
|
|
173
|
+
# Validate mutual exclusivity
|
|
174
|
+
if password is not None and iam_config is not None:
|
|
175
|
+
raise ConfigurationError(
|
|
176
|
+
"password and iam_config are mutually exclusive. Use either password-based or IAM authentication, not both."
|
|
177
|
+
)
|
|
178
|
+
|
|
179
|
+
# Validate IAM requires username
|
|
180
|
+
if iam_config is not None and not username:
|
|
181
|
+
raise ConfigurationError("username is required for IAM authentication.")
|
|
182
|
+
|
|
183
|
+
# At least one authentication method must be provided
|
|
184
|
+
if password is None and iam_config is None:
|
|
185
|
+
raise ConfigurationError(
|
|
186
|
+
"Either password or iam_config must be provided for authentication."
|
|
187
|
+
)
|
|
188
|
+
|
|
118
189
|
self.password = password
|
|
119
190
|
self.username = username
|
|
191
|
+
self.iam_config = iam_config
|
|
192
|
+
|
|
193
|
+
def is_iam_auth(self) -> bool:
|
|
194
|
+
"""Returns True if this credential is configured for IAM authentication."""
|
|
195
|
+
return self.iam_config is not None
|
|
120
196
|
|
|
121
197
|
|
|
122
198
|
class PeriodicChecksManualInterval:
|
|
@@ -147,25 +223,107 @@ class PeriodicChecksStatus(Enum):
|
|
|
147
223
|
"""
|
|
148
224
|
|
|
149
225
|
|
|
226
|
+
class TlsAdvancedConfiguration:
|
|
227
|
+
"""
|
|
228
|
+
Represents advanced TLS configuration settings.
|
|
229
|
+
|
|
230
|
+
Attributes:
|
|
231
|
+
use_insecure_tls (Optional[bool]): Whether to bypass TLS certificate verification.
|
|
232
|
+
|
|
233
|
+
- When set to True, the client skips certificate validation.
|
|
234
|
+
This is useful when connecting to servers or clusters using self-signed certificates,
|
|
235
|
+
or when DNS entries (e.g., CNAMEs) don't match certificate hostnames.
|
|
236
|
+
|
|
237
|
+
- This setting is typically used in development or testing environments. **It is
|
|
238
|
+
strongly discouraged in production**, as it introduces security risks such as man-in-the-middle attacks.
|
|
239
|
+
|
|
240
|
+
- Only valid if TLS is already enabled in the base client configuration.
|
|
241
|
+
Enabling it without TLS will result in a `ConfigurationError`.
|
|
242
|
+
|
|
243
|
+
- Default: False (verification is enforced).
|
|
244
|
+
|
|
245
|
+
root_pem_cacerts (Optional[bytes]): Custom root certificate data for TLS connections in PEM format.
|
|
246
|
+
|
|
247
|
+
- When provided, these certificates will be used instead of the system's default trust store.
|
|
248
|
+
This is useful for connecting to servers with self-signed certificates or corporate
|
|
249
|
+
certificate authorities.
|
|
250
|
+
|
|
251
|
+
- If set to an empty bytes object (non-None but length 0), a `ConfigurationError` will be raised.
|
|
252
|
+
|
|
253
|
+
- If None (default), the system's default certificate trust store will be used (platform verifier).
|
|
254
|
+
|
|
255
|
+
- The certificate data should be in PEM format as a bytes object.
|
|
256
|
+
|
|
257
|
+
- Multiple certificates can be provided by concatenating them in PEM format.
|
|
258
|
+
|
|
259
|
+
Example usage::
|
|
260
|
+
|
|
261
|
+
# Load from file
|
|
262
|
+
with open('/path/to/ca-cert.pem', 'rb') as f:
|
|
263
|
+
cert_data = f.read()
|
|
264
|
+
tls_config = TlsAdvancedConfiguration(root_pem_cacerts=cert_data)
|
|
265
|
+
|
|
266
|
+
# Or provide directly
|
|
267
|
+
cert_data = b"-----BEGIN CERTIFICATE-----\\n...\\n-----END CERTIFICATE-----"
|
|
268
|
+
tls_config = TlsAdvancedConfiguration(root_pem_cacerts=cert_data)
|
|
269
|
+
"""
|
|
270
|
+
|
|
271
|
+
def __init__(
|
|
272
|
+
self,
|
|
273
|
+
use_insecure_tls: Optional[bool] = None,
|
|
274
|
+
root_pem_cacerts: Optional[bytes] = None,
|
|
275
|
+
):
|
|
276
|
+
self.use_insecure_tls = use_insecure_tls
|
|
277
|
+
self.root_pem_cacerts = root_pem_cacerts
|
|
278
|
+
|
|
279
|
+
|
|
150
280
|
class AdvancedBaseClientConfiguration:
|
|
151
281
|
"""
|
|
152
282
|
Represents the advanced configuration settings for a base Glide client.
|
|
153
283
|
|
|
154
284
|
Attributes:
|
|
155
285
|
connection_timeout (Optional[int]): The duration in milliseconds to wait for a TCP/TLS connection to complete.
|
|
156
|
-
This applies both during initial client creation and any
|
|
286
|
+
This applies both during initial client creation and any reconnection that may occur during request processing.
|
|
157
287
|
**Note**: A high connection timeout may lead to prolonged blocking of the entire command pipeline.
|
|
158
|
-
If not explicitly set, a default value of
|
|
288
|
+
If not explicitly set, a default value of 2000 milliseconds will be used.
|
|
289
|
+
tls_config (Optional[TlsAdvancedConfiguration]): The advanced TLS configuration settings.
|
|
290
|
+
This allows for more granular control of TLS behavior, such as enabling an insecure mode
|
|
291
|
+
that bypasses certificate validation.
|
|
159
292
|
"""
|
|
160
293
|
|
|
161
|
-
def __init__(
|
|
294
|
+
def __init__(
|
|
295
|
+
self,
|
|
296
|
+
connection_timeout: Optional[int] = None,
|
|
297
|
+
tls_config: Optional[TlsAdvancedConfiguration] = None,
|
|
298
|
+
):
|
|
162
299
|
self.connection_timeout = connection_timeout
|
|
300
|
+
self.tls_config = tls_config
|
|
163
301
|
|
|
164
302
|
def _create_a_protobuf_conn_request(
|
|
165
303
|
self, request: ConnectionRequest
|
|
166
304
|
) -> ConnectionRequest:
|
|
167
305
|
if self.connection_timeout:
|
|
168
306
|
request.connection_timeout = self.connection_timeout
|
|
307
|
+
|
|
308
|
+
if self.tls_config:
|
|
309
|
+
if self.tls_config.use_insecure_tls:
|
|
310
|
+
# Validate that TLS is enabled before allowing insecure mode
|
|
311
|
+
if request.tls_mode == TlsMode.NoTls:
|
|
312
|
+
raise ConfigurationError(
|
|
313
|
+
"use_insecure_tls cannot be enabled when use_tls is disabled."
|
|
314
|
+
)
|
|
315
|
+
|
|
316
|
+
# Override the default SecureTls mode to InsecureTls when user explicitly requests it
|
|
317
|
+
request.tls_mode = TlsMode.InsecureTls
|
|
318
|
+
|
|
319
|
+
# Handle root certificates
|
|
320
|
+
if self.tls_config.root_pem_cacerts is not None:
|
|
321
|
+
if len(self.tls_config.root_pem_cacerts) == 0:
|
|
322
|
+
raise ConfigurationError(
|
|
323
|
+
"root_pem_cacerts cannot be an empty bytes object; use None to use platform verifier"
|
|
324
|
+
)
|
|
325
|
+
request.root_certs.append(self.tls_config.root_pem_cacerts)
|
|
326
|
+
|
|
169
327
|
return request
|
|
170
328
|
|
|
171
329
|
|
|
@@ -182,24 +340,27 @@ class BaseClientConfiguration:
|
|
|
182
340
|
For example::
|
|
183
341
|
|
|
184
342
|
[
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
]
|
|
343
|
+
NodeAddress("sample-address-0001.use1.cache.amazonaws.com", 6379),
|
|
344
|
+
NodeAddress("sample-address-0002.use1.cache.amazonaws.com", 6379)
|
|
345
|
+
]
|
|
188
346
|
|
|
189
347
|
use_tls (bool): True if communication with the cluster should use Transport Level Security.
|
|
190
|
-
Should match the TLS configuration of the server/cluster, otherwise the connection attempt will fail
|
|
348
|
+
Should match the TLS configuration of the server/cluster, otherwise the connection attempt will fail.
|
|
349
|
+
For advanced tls configuration, please use `AdvancedBaseClientConfiguration`.
|
|
191
350
|
credentials (ServerCredentials): Credentials for authentication process.
|
|
192
351
|
If none are set, the client will not authenticate itself with the server.
|
|
193
352
|
read_from (ReadFrom): If not set, `PRIMARY` will be used.
|
|
194
353
|
request_timeout (Optional[int]): The duration in milliseconds that the client should wait for a request to
|
|
195
354
|
complete.
|
|
196
355
|
This duration encompasses sending the request, awaiting for a response from the server, and any required
|
|
197
|
-
|
|
356
|
+
reconnection or retries.
|
|
198
357
|
If the specified timeout is exceeded for a pending request, it will result in a timeout error. If not
|
|
199
358
|
explicitly set, a default value of 250 milliseconds will be used.
|
|
200
359
|
reconnect_strategy (Optional[BackoffStrategy]): Strategy used to determine how and when to reconnect, in case of
|
|
201
360
|
connection failures.
|
|
202
361
|
If not set, a default backoff strategy will be used.
|
|
362
|
+
database_id (Optional[int]): Index of the logical database to connect to.
|
|
363
|
+
Must be a non-negative integer.If not set, the client will connect to database 0.
|
|
203
364
|
client_name (Optional[str]): Client name to be used for the client. Will be used with CLIENT SETNAME command
|
|
204
365
|
during connection establishment.
|
|
205
366
|
protocol (ProtocolVersion): Serialization protocol to be used. If not set, `RESP3` will be used.
|
|
@@ -214,6 +375,25 @@ class BaseClientConfiguration:
|
|
|
214
375
|
If ReadFrom strategy is AZAffinityReplicasAndPrimary, this setting ensures that readonly commands are directed
|
|
215
376
|
to nodes (first replicas then primary) within the specified AZ if they exist.
|
|
216
377
|
advanced_config (Optional[AdvancedBaseClientConfiguration]): Advanced configuration settings for the client.
|
|
378
|
+
|
|
379
|
+
lazy_connect (Optional[bool]): Enables lazy connection mode, where physical connections to the server(s)
|
|
380
|
+
are deferred until the first command is sent. This can reduce startup latency and allow for client
|
|
381
|
+
creation in disconnected environments.
|
|
382
|
+
|
|
383
|
+
When set to `True`, the client will not attempt to connect to the specified nodes during
|
|
384
|
+
initialization. Instead, connections will be established only when a command is actually executed.
|
|
385
|
+
|
|
386
|
+
Note that the first command executed with lazy connections may experience additional latency
|
|
387
|
+
as it needs to establish the connection first. During this initial connection, the standard
|
|
388
|
+
request timeout does not apply yet - instead, the connection establishment is governed by
|
|
389
|
+
`AdvancedBaseClientConfiguration.connection_timeout`. The request timeout (`request_timeout`)
|
|
390
|
+
only begins counting after the connection has been successfully established. This behavior
|
|
391
|
+
can effectively increase the total time needed for the first command to complete.
|
|
392
|
+
|
|
393
|
+
This setting applies to both standalone and cluster modes. Note that if an operation is
|
|
394
|
+
attempted and connection fails (e.g., unreachable nodes), errors will surface at that point.
|
|
395
|
+
|
|
396
|
+
If not set, connections are established immediately during client creation (equivalent to `False`).
|
|
217
397
|
"""
|
|
218
398
|
|
|
219
399
|
def __init__(
|
|
@@ -224,11 +404,13 @@ class BaseClientConfiguration:
|
|
|
224
404
|
read_from: ReadFrom = ReadFrom.PRIMARY,
|
|
225
405
|
request_timeout: Optional[int] = None,
|
|
226
406
|
reconnect_strategy: Optional[BackoffStrategy] = None,
|
|
407
|
+
database_id: Optional[int] = None,
|
|
227
408
|
client_name: Optional[str] = None,
|
|
228
409
|
protocol: ProtocolVersion = ProtocolVersion.RESP3,
|
|
229
410
|
inflight_requests_limit: Optional[int] = None,
|
|
230
411
|
client_az: Optional[str] = None,
|
|
231
412
|
advanced_config: Optional[AdvancedBaseClientConfiguration] = None,
|
|
413
|
+
lazy_connect: Optional[bool] = None,
|
|
232
414
|
):
|
|
233
415
|
self.addresses = addresses
|
|
234
416
|
self.use_tls = use_tls
|
|
@@ -236,11 +418,13 @@ class BaseClientConfiguration:
|
|
|
236
418
|
self.read_from = read_from
|
|
237
419
|
self.request_timeout = request_timeout
|
|
238
420
|
self.reconnect_strategy = reconnect_strategy
|
|
421
|
+
self.database_id = database_id
|
|
239
422
|
self.client_name = client_name
|
|
240
423
|
self.protocol = protocol
|
|
241
424
|
self.inflight_requests_limit = inflight_requests_limit
|
|
242
425
|
self.client_az = client_az
|
|
243
426
|
self.advanced_config = advanced_config
|
|
427
|
+
self.lazy_connect = lazy_connect
|
|
244
428
|
|
|
245
429
|
if read_from == ReadFrom.AZ_AFFINITY and not client_az:
|
|
246
430
|
raise ValueError(
|
|
@@ -252,6 +436,73 @@ class BaseClientConfiguration:
|
|
|
252
436
|
"client_az must be set when read_from is set to AZ_AFFINITY_REPLICAS_AND_PRIMARY"
|
|
253
437
|
)
|
|
254
438
|
|
|
439
|
+
def _set_addresses_in_request(self, request: ConnectionRequest) -> None:
|
|
440
|
+
"""Set addresses in the protobuf request."""
|
|
441
|
+
for address in self.addresses:
|
|
442
|
+
address_info = request.addresses.add()
|
|
443
|
+
address_info.host = address.host
|
|
444
|
+
address_info.port = address.port
|
|
445
|
+
|
|
446
|
+
def _set_reconnect_strategy_in_request(self, request: ConnectionRequest) -> None:
|
|
447
|
+
"""Set reconnect strategy in the protobuf request."""
|
|
448
|
+
if not self.reconnect_strategy:
|
|
449
|
+
return
|
|
450
|
+
|
|
451
|
+
request.connection_retry_strategy.number_of_retries = (
|
|
452
|
+
self.reconnect_strategy.num_of_retries
|
|
453
|
+
)
|
|
454
|
+
request.connection_retry_strategy.factor = self.reconnect_strategy.factor
|
|
455
|
+
request.connection_retry_strategy.exponent_base = (
|
|
456
|
+
self.reconnect_strategy.exponent_base
|
|
457
|
+
)
|
|
458
|
+
if self.reconnect_strategy.jitter_percent is not None:
|
|
459
|
+
request.connection_retry_strategy.jitter_percent = (
|
|
460
|
+
self.reconnect_strategy.jitter_percent
|
|
461
|
+
)
|
|
462
|
+
|
|
463
|
+
def _set_credentials_in_request(self, request: ConnectionRequest) -> None:
|
|
464
|
+
"""Set credentials in the protobuf request."""
|
|
465
|
+
if not self.credentials:
|
|
466
|
+
return
|
|
467
|
+
|
|
468
|
+
if self.credentials.username:
|
|
469
|
+
request.authentication_info.username = self.credentials.username
|
|
470
|
+
|
|
471
|
+
if self.credentials.password:
|
|
472
|
+
request.authentication_info.password = self.credentials.password
|
|
473
|
+
|
|
474
|
+
# Set IAM credentials if present
|
|
475
|
+
if self.credentials.iam_config:
|
|
476
|
+
iam_config = self.credentials.iam_config
|
|
477
|
+
request.authentication_info.iam_credentials.cluster_name = (
|
|
478
|
+
iam_config.cluster_name
|
|
479
|
+
)
|
|
480
|
+
request.authentication_info.iam_credentials.region = iam_config.region
|
|
481
|
+
|
|
482
|
+
# Map ServiceType enum to protobuf ServiceType
|
|
483
|
+
if iam_config.service == ServiceType.ELASTICACHE:
|
|
484
|
+
from glide_shared.protobuf.connection_request_pb2 import (
|
|
485
|
+
ServiceType as ProtobufServiceType,
|
|
486
|
+
)
|
|
487
|
+
|
|
488
|
+
request.authentication_info.iam_credentials.service_type = (
|
|
489
|
+
ProtobufServiceType.ELASTICACHE
|
|
490
|
+
)
|
|
491
|
+
elif iam_config.service == ServiceType.MEMORYDB:
|
|
492
|
+
from glide_shared.protobuf.connection_request_pb2 import (
|
|
493
|
+
ServiceType as ProtobufServiceType,
|
|
494
|
+
)
|
|
495
|
+
|
|
496
|
+
request.authentication_info.iam_credentials.service_type = (
|
|
497
|
+
ProtobufServiceType.MEMORYDB
|
|
498
|
+
)
|
|
499
|
+
|
|
500
|
+
# Set optional refresh interval
|
|
501
|
+
if iam_config.refresh_interval_seconds is not None:
|
|
502
|
+
request.authentication_info.iam_credentials.refresh_interval_seconds = (
|
|
503
|
+
iam_config.refresh_interval_seconds
|
|
504
|
+
)
|
|
505
|
+
|
|
255
506
|
def _create_a_protobuf_conn_request(
|
|
256
507
|
self, cluster_mode: bool = False
|
|
257
508
|
) -> ConnectionRequest:
|
|
@@ -265,41 +516,33 @@ class BaseClientConfiguration:
|
|
|
265
516
|
ConnectionRequest: Protobuf ConnectionRequest.
|
|
266
517
|
"""
|
|
267
518
|
request = ConnectionRequest()
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
address_info.port = address.port
|
|
519
|
+
|
|
520
|
+
# Set basic configuration
|
|
521
|
+
self._set_addresses_in_request(request)
|
|
272
522
|
request.tls_mode = TlsMode.SecureTls if self.use_tls else TlsMode.NoTls
|
|
273
523
|
request.read_from = self.read_from.value
|
|
524
|
+
request.cluster_mode_enabled = cluster_mode
|
|
525
|
+
request.protocol = self.protocol.value
|
|
526
|
+
|
|
527
|
+
# Set optional configuration
|
|
274
528
|
if self.request_timeout:
|
|
275
529
|
request.request_timeout = self.request_timeout
|
|
276
|
-
if self.reconnect_strategy:
|
|
277
|
-
request.connection_retry_strategy.number_of_retries = (
|
|
278
|
-
self.reconnect_strategy.num_of_retries
|
|
279
|
-
)
|
|
280
|
-
request.connection_retry_strategy.factor = self.reconnect_strategy.factor
|
|
281
|
-
request.connection_retry_strategy.exponent_base = (
|
|
282
|
-
self.reconnect_strategy.exponent_base
|
|
283
|
-
)
|
|
284
|
-
if self.reconnect_strategy.jitter_percent is not None:
|
|
285
|
-
request.connection_retry_strategy.jitter_percent = (
|
|
286
|
-
self.reconnect_strategy.jitter_percent
|
|
287
|
-
)
|
|
288
530
|
|
|
289
|
-
request
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
request.authentication_info.username = self.credentials.username
|
|
293
|
-
request.authentication_info.password = self.credentials.password
|
|
531
|
+
self._set_reconnect_strategy_in_request(request)
|
|
532
|
+
self._set_credentials_in_request(request)
|
|
533
|
+
|
|
294
534
|
if self.client_name:
|
|
295
535
|
request.client_name = self.client_name
|
|
296
|
-
request.protocol = self.protocol.value
|
|
297
536
|
if self.inflight_requests_limit:
|
|
298
537
|
request.inflight_requests_limit = self.inflight_requests_limit
|
|
299
538
|
if self.client_az:
|
|
300
539
|
request.client_az = self.client_az
|
|
540
|
+
if self.database_id is not None:
|
|
541
|
+
request.database_id = self.database_id
|
|
301
542
|
if self.advanced_config:
|
|
302
543
|
self.advanced_config._create_a_protobuf_conn_request(request)
|
|
544
|
+
if self.lazy_connect is not None:
|
|
545
|
+
request.lazy_connect = self.lazy_connect
|
|
303
546
|
|
|
304
547
|
return request
|
|
305
548
|
|
|
@@ -308,7 +551,7 @@ class BaseClientConfiguration:
|
|
|
308
551
|
|
|
309
552
|
def _get_pubsub_callback_and_context(
|
|
310
553
|
self,
|
|
311
|
-
) -> Tuple[Optional[Callable[[
|
|
554
|
+
) -> Tuple[Optional[Callable[[PubSubMsg, Any], None]], Any]:
|
|
312
555
|
return None, None
|
|
313
556
|
|
|
314
557
|
|
|
@@ -317,9 +560,13 @@ class AdvancedGlideClientConfiguration(AdvancedBaseClientConfiguration):
|
|
|
317
560
|
Represents the advanced configuration settings for a Standalone Glide client.
|
|
318
561
|
"""
|
|
319
562
|
|
|
320
|
-
def __init__(
|
|
563
|
+
def __init__(
|
|
564
|
+
self,
|
|
565
|
+
connection_timeout: Optional[int] = None,
|
|
566
|
+
tls_config: Optional[TlsAdvancedConfiguration] = None,
|
|
567
|
+
):
|
|
321
568
|
|
|
322
|
-
super().__init__(connection_timeout)
|
|
569
|
+
super().__init__(connection_timeout, tls_config)
|
|
323
570
|
|
|
324
571
|
|
|
325
572
|
class GlideClientConfiguration(BaseClientConfiguration):
|
|
@@ -328,27 +575,28 @@ class GlideClientConfiguration(BaseClientConfiguration):
|
|
|
328
575
|
|
|
329
576
|
Attributes:
|
|
330
577
|
addresses (List[NodeAddress]): DNS Addresses and ports of known nodes in the cluster.
|
|
331
|
-
|
|
578
|
+
Only nodes whose addresses were provided will be used by the client.
|
|
332
579
|
For example::
|
|
333
580
|
|
|
334
581
|
[
|
|
335
|
-
|
|
336
|
-
|
|
582
|
+
NodeAddress("sample-address-0001.use1.cache.amazonaws.com", 6379),
|
|
583
|
+
NodeAddress("sample-address-0002.use1.cache.amazonaws.com", 6379)
|
|
337
584
|
]
|
|
338
585
|
|
|
339
586
|
use_tls (bool): True if communication with the cluster should use Transport Level Security.
|
|
587
|
+
Please use `AdvancedGlideClusterClientConfiguration`.
|
|
340
588
|
credentials (ServerCredentials): Credentials for authentication process.
|
|
341
589
|
If none are set, the client will not authenticate itself with the server.
|
|
342
590
|
read_from (ReadFrom): If not set, `PRIMARY` will be used.
|
|
343
591
|
request_timeout (Optional[int]): The duration in milliseconds that the client should wait for a request to complete.
|
|
344
592
|
This duration encompasses sending the request, awaiting for a response from the server, and any required
|
|
345
|
-
|
|
593
|
+
reconnection or retries.
|
|
346
594
|
If the specified timeout is exceeded for a pending request, it will result in a timeout error.
|
|
347
595
|
If not explicitly set, a default value of 250 milliseconds will be used.
|
|
348
596
|
reconnect_strategy (Optional[BackoffStrategy]): Strategy used to determine how and when to reconnect, in case of
|
|
349
597
|
connection failures.
|
|
350
598
|
If not set, a default backoff strategy will be used.
|
|
351
|
-
database_id (Optional[int]):
|
|
599
|
+
database_id (Optional[int]): Index of the logical database to connect to.
|
|
352
600
|
client_name (Optional[str]): Client name to be used for the client. Will be used with CLIENT SETNAME command during
|
|
353
601
|
connection establishment.
|
|
354
602
|
protocol (ProtocolVersion): The version of the RESP protocol to communicate with the server.
|
|
@@ -387,7 +635,7 @@ class GlideClientConfiguration(BaseClientConfiguration):
|
|
|
387
635
|
Attributes:
|
|
388
636
|
channels_and_patterns (Dict[GlideClientConfiguration.PubSubChannelModes, Set[str]]):
|
|
389
637
|
Channels and patterns by modes.
|
|
390
|
-
callback (Optional[Callable[[
|
|
638
|
+
callback (Optional[Callable[[PubSubMsg, Any], None]]):
|
|
391
639
|
Optional callback to accept the incomming messages.
|
|
392
640
|
context (Any):
|
|
393
641
|
Arbitrary context to pass to the callback.
|
|
@@ -396,7 +644,7 @@ class GlideClientConfiguration(BaseClientConfiguration):
|
|
|
396
644
|
channels_and_patterns: Dict[
|
|
397
645
|
GlideClientConfiguration.PubSubChannelModes, Set[str]
|
|
398
646
|
]
|
|
399
|
-
callback: Optional[Callable[[
|
|
647
|
+
callback: Optional[Callable[[PubSubMsg, Any], None]]
|
|
400
648
|
context: Any
|
|
401
649
|
|
|
402
650
|
def __init__(
|
|
@@ -414,6 +662,7 @@ class GlideClientConfiguration(BaseClientConfiguration):
|
|
|
414
662
|
inflight_requests_limit: Optional[int] = None,
|
|
415
663
|
client_az: Optional[str] = None,
|
|
416
664
|
advanced_config: Optional[AdvancedGlideClientConfiguration] = None,
|
|
665
|
+
lazy_connect: Optional[bool] = None,
|
|
417
666
|
):
|
|
418
667
|
super().__init__(
|
|
419
668
|
addresses=addresses,
|
|
@@ -422,13 +671,14 @@ class GlideClientConfiguration(BaseClientConfiguration):
|
|
|
422
671
|
read_from=read_from,
|
|
423
672
|
request_timeout=request_timeout,
|
|
424
673
|
reconnect_strategy=reconnect_strategy,
|
|
674
|
+
database_id=database_id,
|
|
425
675
|
client_name=client_name,
|
|
426
676
|
protocol=protocol,
|
|
427
677
|
inflight_requests_limit=inflight_requests_limit,
|
|
428
678
|
client_az=client_az,
|
|
429
679
|
advanced_config=advanced_config,
|
|
680
|
+
lazy_connect=lazy_connect,
|
|
430
681
|
)
|
|
431
|
-
self.database_id = database_id
|
|
432
682
|
self.pubsub_subscriptions = pubsub_subscriptions
|
|
433
683
|
|
|
434
684
|
def _create_a_protobuf_conn_request(
|
|
@@ -436,8 +686,6 @@ class GlideClientConfiguration(BaseClientConfiguration):
|
|
|
436
686
|
) -> ConnectionRequest:
|
|
437
687
|
assert cluster_mode is False
|
|
438
688
|
request = super()._create_a_protobuf_conn_request(cluster_mode)
|
|
439
|
-
if self.database_id:
|
|
440
|
-
request.database_id = self.database_id
|
|
441
689
|
|
|
442
690
|
if self.pubsub_subscriptions:
|
|
443
691
|
if self.protocol == ProtocolVersion.RESP2:
|
|
@@ -468,7 +716,7 @@ class GlideClientConfiguration(BaseClientConfiguration):
|
|
|
468
716
|
|
|
469
717
|
def _get_pubsub_callback_and_context(
|
|
470
718
|
self,
|
|
471
|
-
) -> Tuple[Optional[Callable[[
|
|
719
|
+
) -> Tuple[Optional[Callable[[PubSubMsg, Any], None]], Any]:
|
|
472
720
|
if self.pubsub_subscriptions:
|
|
473
721
|
return self.pubsub_subscriptions.callback, self.pubsub_subscriptions.context
|
|
474
722
|
return None, None
|
|
@@ -477,10 +725,38 @@ class GlideClientConfiguration(BaseClientConfiguration):
|
|
|
477
725
|
class AdvancedGlideClusterClientConfiguration(AdvancedBaseClientConfiguration):
|
|
478
726
|
"""
|
|
479
727
|
Represents the advanced configuration settings for a Glide Cluster client.
|
|
728
|
+
|
|
729
|
+
Attributes:
|
|
730
|
+
connection_timeout (Optional[int]): The duration in milliseconds to wait for a TCP/TLS connection to complete.
|
|
731
|
+
This applies both during initial client creation and any reconnection that may occur during request processing.
|
|
732
|
+
**Note**: A high connection timeout may lead to prolonged blocking of the entire command pipeline.
|
|
733
|
+
If not explicitly set, a default value of 2000 milliseconds will be used.
|
|
734
|
+
tls_config (Optional[TlsAdvancedConfiguration]): The advanced TLS configuration settings.
|
|
735
|
+
This allows for more granular control of TLS behavior, such as enabling an insecure mode
|
|
736
|
+
that bypasses certificate validation.
|
|
737
|
+
refresh_topology_from_initial_nodes (bool): Enables refreshing the cluster topology using only the initial nodes.
|
|
738
|
+
When this option is enabled, all topology updates (both the periodic checks and on-demand refreshes
|
|
739
|
+
triggered by topology changes) will query only the initial nodes provided when creating the client, rather than using the internal cluster view.
|
|
480
740
|
"""
|
|
481
741
|
|
|
482
|
-
def __init__(
|
|
483
|
-
|
|
742
|
+
def __init__(
|
|
743
|
+
self,
|
|
744
|
+
connection_timeout: Optional[int] = None,
|
|
745
|
+
tls_config: Optional[TlsAdvancedConfiguration] = None,
|
|
746
|
+
refresh_topology_from_initial_nodes: bool = False,
|
|
747
|
+
):
|
|
748
|
+
super().__init__(connection_timeout, tls_config)
|
|
749
|
+
self.refresh_topology_from_initial_nodes = refresh_topology_from_initial_nodes
|
|
750
|
+
|
|
751
|
+
def _create_a_protobuf_conn_request(
|
|
752
|
+
self, request: ConnectionRequest
|
|
753
|
+
) -> ConnectionRequest:
|
|
754
|
+
super()._create_a_protobuf_conn_request(request)
|
|
755
|
+
|
|
756
|
+
request.refresh_topology_from_initial_nodes = (
|
|
757
|
+
self.refresh_topology_from_initial_nodes
|
|
758
|
+
)
|
|
759
|
+
return request
|
|
484
760
|
|
|
485
761
|
|
|
486
762
|
class GlideClusterClientConfiguration(BaseClientConfiguration):
|
|
@@ -493,21 +769,23 @@ class GlideClusterClientConfiguration(BaseClientConfiguration):
|
|
|
493
769
|
For example::
|
|
494
770
|
|
|
495
771
|
[
|
|
496
|
-
|
|
772
|
+
NodeAddress("sample-address-0001.use1.cache.amazonaws.com", 6379),
|
|
497
773
|
]
|
|
498
774
|
|
|
499
775
|
use_tls (bool): True if communication with the cluster should use Transport Level Security.
|
|
776
|
+
For advanced tls configuration, please use `AdvancedGlideClusterClientConfiguration`.
|
|
500
777
|
credentials (ServerCredentials): Credentials for authentication process.
|
|
501
778
|
If none are set, the client will not authenticate itself with the server.
|
|
502
779
|
read_from (ReadFrom): If not set, `PRIMARY` will be used.
|
|
503
780
|
request_timeout (Optional[int]): The duration in milliseconds that the client should wait for a request to complete.
|
|
504
781
|
This duration encompasses sending the request, awaiting for a response from the server, and any required
|
|
505
|
-
|
|
782
|
+
reconnection or retries.
|
|
506
783
|
If the specified timeout is exceeded for a pending request, it will result in a timeout error. If not explicitly
|
|
507
784
|
set, a default value of 250 milliseconds will be used.
|
|
508
785
|
reconnect_strategy (Optional[BackoffStrategy]): Strategy used to determine how and when to reconnect, in case of
|
|
509
786
|
connection failures.
|
|
510
787
|
If not set, a default backoff strategy will be used.
|
|
788
|
+
database_id (Optional[int]): Index of the logical database to connect to.
|
|
511
789
|
client_name (Optional[str]): Client name to be used for the client. Will be used with CLIENT SETNAME command during
|
|
512
790
|
connection establishment.
|
|
513
791
|
protocol (ProtocolVersion): The version of the RESP protocol to communicate with the server.
|
|
@@ -557,7 +835,7 @@ class GlideClusterClientConfiguration(BaseClientConfiguration):
|
|
|
557
835
|
Attributes:
|
|
558
836
|
channels_and_patterns (Dict[GlideClusterClientConfiguration.PubSubChannelModes, Set[str]]):
|
|
559
837
|
Channels and patterns by modes.
|
|
560
|
-
callback (Optional[Callable[[
|
|
838
|
+
callback (Optional[Callable[[PubSubMsg, Any], None]]):
|
|
561
839
|
Optional callback to accept the incoming messages.
|
|
562
840
|
context (Any):
|
|
563
841
|
Arbitrary context to pass to the callback.
|
|
@@ -566,7 +844,7 @@ class GlideClusterClientConfiguration(BaseClientConfiguration):
|
|
|
566
844
|
channels_and_patterns: Dict[
|
|
567
845
|
GlideClusterClientConfiguration.PubSubChannelModes, Set[str]
|
|
568
846
|
]
|
|
569
|
-
callback: Optional[Callable[[
|
|
847
|
+
callback: Optional[Callable[[PubSubMsg, Any], None]]
|
|
570
848
|
context: Any
|
|
571
849
|
|
|
572
850
|
def __init__(
|
|
@@ -577,6 +855,7 @@ class GlideClusterClientConfiguration(BaseClientConfiguration):
|
|
|
577
855
|
read_from: ReadFrom = ReadFrom.PRIMARY,
|
|
578
856
|
request_timeout: Optional[int] = None,
|
|
579
857
|
reconnect_strategy: Optional[BackoffStrategy] = None,
|
|
858
|
+
database_id: Optional[int] = None,
|
|
580
859
|
client_name: Optional[str] = None,
|
|
581
860
|
protocol: ProtocolVersion = ProtocolVersion.RESP3,
|
|
582
861
|
periodic_checks: Union[
|
|
@@ -586,6 +865,7 @@ class GlideClusterClientConfiguration(BaseClientConfiguration):
|
|
|
586
865
|
inflight_requests_limit: Optional[int] = None,
|
|
587
866
|
client_az: Optional[str] = None,
|
|
588
867
|
advanced_config: Optional[AdvancedGlideClusterClientConfiguration] = None,
|
|
868
|
+
lazy_connect: Optional[bool] = None,
|
|
589
869
|
):
|
|
590
870
|
super().__init__(
|
|
591
871
|
addresses=addresses,
|
|
@@ -594,11 +874,13 @@ class GlideClusterClientConfiguration(BaseClientConfiguration):
|
|
|
594
874
|
read_from=read_from,
|
|
595
875
|
request_timeout=request_timeout,
|
|
596
876
|
reconnect_strategy=reconnect_strategy,
|
|
877
|
+
database_id=database_id,
|
|
597
878
|
client_name=client_name,
|
|
598
879
|
protocol=protocol,
|
|
599
880
|
inflight_requests_limit=inflight_requests_limit,
|
|
600
881
|
client_az=client_az,
|
|
601
882
|
advanced_config=advanced_config,
|
|
883
|
+
lazy_connect=lazy_connect,
|
|
602
884
|
)
|
|
603
885
|
self.periodic_checks = periodic_checks
|
|
604
886
|
self.pubsub_subscriptions = pubsub_subscriptions
|
|
@@ -637,6 +919,8 @@ class GlideClusterClientConfiguration(BaseClientConfiguration):
|
|
|
637
919
|
for channel_pattern in channels_patterns:
|
|
638
920
|
entry.channels_or_patterns.append(str.encode(channel_pattern))
|
|
639
921
|
|
|
922
|
+
if self.lazy_connect is not None:
|
|
923
|
+
request.lazy_connect = self.lazy_connect
|
|
640
924
|
return request
|
|
641
925
|
|
|
642
926
|
def _is_pubsub_configured(self) -> bool:
|
|
@@ -644,7 +928,48 @@ class GlideClusterClientConfiguration(BaseClientConfiguration):
|
|
|
644
928
|
|
|
645
929
|
def _get_pubsub_callback_and_context(
|
|
646
930
|
self,
|
|
647
|
-
) -> Tuple[Optional[Callable[[
|
|
931
|
+
) -> Tuple[Optional[Callable[[PubSubMsg, Any], None]], Any]:
|
|
648
932
|
if self.pubsub_subscriptions:
|
|
649
933
|
return self.pubsub_subscriptions.callback, self.pubsub_subscriptions.context
|
|
650
934
|
return None, None
|
|
935
|
+
|
|
936
|
+
|
|
937
|
+
def load_root_certificates_from_file(path: str) -> bytes:
|
|
938
|
+
"""
|
|
939
|
+
Load PEM-encoded root certificates from a file.
|
|
940
|
+
|
|
941
|
+
This is a convenience function for loading custom root certificates from disk
|
|
942
|
+
to be used with TlsAdvancedConfiguration.
|
|
943
|
+
|
|
944
|
+
Args:
|
|
945
|
+
path (str): The file path to the PEM-encoded certificate file.
|
|
946
|
+
|
|
947
|
+
Returns:
|
|
948
|
+
bytes: The certificate data in PEM format.
|
|
949
|
+
|
|
950
|
+
Raises:
|
|
951
|
+
FileNotFoundError: If the certificate file does not exist.
|
|
952
|
+
ConfigurationError: If the certificate file is empty.
|
|
953
|
+
|
|
954
|
+
Example usage::
|
|
955
|
+
|
|
956
|
+
from glide_shared.config import load_root_certificates_from_file, TlsAdvancedConfiguration
|
|
957
|
+
|
|
958
|
+
# Load certificates from file
|
|
959
|
+
certs = load_root_certificates_from_file('/path/to/ca-cert.pem')
|
|
960
|
+
|
|
961
|
+
# Use in TLS configuration
|
|
962
|
+
tls_config = TlsAdvancedConfiguration(root_pem_cacerts=certs)
|
|
963
|
+
"""
|
|
964
|
+
try:
|
|
965
|
+
with open(path, "rb") as f:
|
|
966
|
+
data = f.read()
|
|
967
|
+
except FileNotFoundError:
|
|
968
|
+
raise FileNotFoundError(f"Certificate file not found: {path}")
|
|
969
|
+
except Exception as e:
|
|
970
|
+
raise ConfigurationError(f"Failed to read certificate file: {e}")
|
|
971
|
+
|
|
972
|
+
if len(data) == 0:
|
|
973
|
+
raise ConfigurationError(f"Certificate file is empty: {path}")
|
|
974
|
+
|
|
975
|
+
return data
|