alternator-client 1.0.0__tar.gz
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.
- alternator_client-1.0.0/PKG-INFO +439 -0
- alternator_client-1.0.0/README.md +398 -0
- alternator_client-1.0.0/alternator/__init__.py +159 -0
- alternator_client-1.0.0/alternator/_constants.py +8 -0
- alternator_client-1.0.0/alternator/_http.py +196 -0
- alternator_client-1.0.0/alternator/_version.py +3 -0
- alternator_client-1.0.0/alternator/async_client.py +480 -0
- alternator_client-1.0.0/alternator/client.py +468 -0
- alternator_client-1.0.0/alternator/config.py +451 -0
- alternator_client-1.0.0/alternator/core/__init__.py +27 -0
- alternator_client-1.0.0/alternator/core/compression.py +56 -0
- alternator_client-1.0.0/alternator/core/go_rand.py +331 -0
- alternator_client-1.0.0/alternator/core/handlers.py +127 -0
- alternator_client-1.0.0/alternator/core/hashing.py +174 -0
- alternator_client-1.0.0/alternator/core/headers.py +98 -0
- alternator_client-1.0.0/alternator/core/key_affinity.py +263 -0
- alternator_client-1.0.0/alternator/core/live_nodes.py +455 -0
- alternator_client-1.0.0/alternator/core/query_plan.py +51 -0
- alternator_client-1.0.0/alternator/core/request.py +60 -0
- alternator_client-1.0.0/alternator/core/routing_scope.py +103 -0
- alternator_client-1.0.0/alternator/core/tls.py +55 -0
- alternator_client-1.0.0/alternator/exceptions.py +85 -0
- alternator_client-1.0.0/alternator/py.typed +0 -0
- alternator_client-1.0.0/alternator_client.egg-info/PKG-INFO +439 -0
- alternator_client-1.0.0/alternator_client.egg-info/SOURCES.txt +28 -0
- alternator_client-1.0.0/alternator_client.egg-info/dependency_links.txt +1 -0
- alternator_client-1.0.0/alternator_client.egg-info/requires.txt +18 -0
- alternator_client-1.0.0/alternator_client.egg-info/top_level.txt +1 -0
- alternator_client-1.0.0/pyproject.toml +131 -0
- alternator_client-1.0.0/setup.cfg +4 -0
|
@@ -0,0 +1,439 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: alternator-client
|
|
3
|
+
Version: 1.0.0
|
|
4
|
+
Summary: Client-side load balancing for ScyllaDB Alternator
|
|
5
|
+
Author-email: ScyllaDB <info@scylladb.com>
|
|
6
|
+
License-Expression: Apache-2.0
|
|
7
|
+
Project-URL: Homepage, https://github.com/scylladb/alternator-client-python
|
|
8
|
+
Project-URL: Documentation, https://github.com/scylladb/alternator-client-python#readme
|
|
9
|
+
Project-URL: Repository, https://github.com/scylladb/alternator-client-python
|
|
10
|
+
Project-URL: Issues, https://github.com/scylladb/alternator-client-python/issues
|
|
11
|
+
Keywords: scylladb,alternator,dynamodb,load-balancing,boto3
|
|
12
|
+
Classifier: Development Status :: 5 - Production/Stable
|
|
13
|
+
Classifier: Intended Audience :: Developers
|
|
14
|
+
Classifier: Programming Language :: Python :: 3
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
20
|
+
Classifier: Topic :: Database
|
|
21
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
22
|
+
Classifier: Typing :: Typed
|
|
23
|
+
Requires-Python: >=3.10
|
|
24
|
+
Description-Content-Type: text/markdown
|
|
25
|
+
Requires-Dist: boto3>=1.28.0
|
|
26
|
+
Requires-Dist: botocore>=1.31.0
|
|
27
|
+
Provides-Extra: async
|
|
28
|
+
Requires-Dist: aioboto3>=12.0.0; extra == "async"
|
|
29
|
+
Requires-Dist: aiobotocore>=2.5.0; extra == "async"
|
|
30
|
+
Requires-Dist: aiohttp>=3.8.0; extra == "async"
|
|
31
|
+
Provides-Extra: dev
|
|
32
|
+
Requires-Dist: pytest>=7.0; extra == "dev"
|
|
33
|
+
Requires-Dist: pytest-asyncio>=0.21; extra == "dev"
|
|
34
|
+
Requires-Dist: pytest-cov>=4.0; extra == "dev"
|
|
35
|
+
Requires-Dist: pytest-timeout>=2.0; extra == "dev"
|
|
36
|
+
Requires-Dist: hypothesis>=6.0; extra == "dev"
|
|
37
|
+
Requires-Dist: mypy>=1.0; extra == "dev"
|
|
38
|
+
Requires-Dist: ruff>=0.1.0; extra == "dev"
|
|
39
|
+
Requires-Dist: types-aiobotocore[dynamodb]>=2.5.0; extra == "dev"
|
|
40
|
+
Requires-Dist: boto3-stubs[dynamodb]>=1.28.0; extra == "dev"
|
|
41
|
+
|
|
42
|
+
# Alternator Load Balancing Client for Python
|
|
43
|
+
|
|
44
|
+
A Python library that provides client-side load balancing for [ScyllaDB Alternator](https://docs.scylladb.com/stable/alternator/), wrapping boto3/aioboto3 to transparently distribute requests across cluster nodes.
|
|
45
|
+
|
|
46
|
+
## Features
|
|
47
|
+
|
|
48
|
+
- **Automatic Load Balancing**: Distributes requests across all available Alternator nodes using round-robin selection
|
|
49
|
+
- **Node Discovery**: Automatically discovers cluster topology via the `/localnodes` endpoint
|
|
50
|
+
- **Topology Awareness**: Route requests to specific datacenters or racks
|
|
51
|
+
- **Key Affinity Routing**: Optimizes LWT (Lightweight Transaction) operations by routing requests for the same partition key to the same node
|
|
52
|
+
- **Request Compression**: Optional gzip compression to reduce bandwidth
|
|
53
|
+
- **Header Optimization**: Filters unnecessary headers to reduce request overhead
|
|
54
|
+
- **TLS Support**: Full TLS/SSL support with custom CA certificates
|
|
55
|
+
- **Async Support**: Full async/await support via aioboto3
|
|
56
|
+
|
|
57
|
+
## Installation
|
|
58
|
+
|
|
59
|
+
```bash
|
|
60
|
+
# Basic installation (sync client only)
|
|
61
|
+
pip install alternator-client
|
|
62
|
+
|
|
63
|
+
# With async support
|
|
64
|
+
pip install alternator-client[async]
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
> **Note:** The PyPI package name is `alternator-client`, but the Python import remains `alternator`.
|
|
68
|
+
|
|
69
|
+
## Quick Start
|
|
70
|
+
|
|
71
|
+
### Synchronous Client
|
|
72
|
+
|
|
73
|
+
```python
|
|
74
|
+
from alternator import AlternatorConfig, AlternatorClient
|
|
75
|
+
|
|
76
|
+
# Configure the client
|
|
77
|
+
config = AlternatorConfig(
|
|
78
|
+
seed_hosts=["192.168.1.1", "192.168.1.2"],
|
|
79
|
+
port=8000,
|
|
80
|
+
)
|
|
81
|
+
|
|
82
|
+
# Use as a context manager (recommended)
|
|
83
|
+
with AlternatorClient(config) as client:
|
|
84
|
+
# Use like a normal boto3 DynamoDB client
|
|
85
|
+
response = client.list_tables()
|
|
86
|
+
print(response["TableNames"])
|
|
87
|
+
|
|
88
|
+
# Put an item
|
|
89
|
+
client.put_item(
|
|
90
|
+
TableName="my_table",
|
|
91
|
+
Item={
|
|
92
|
+
"pk": {"S": "user123"},
|
|
93
|
+
"data": {"S": "Hello, World!"},
|
|
94
|
+
}
|
|
95
|
+
)
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
### Asynchronous Client
|
|
99
|
+
|
|
100
|
+
```python
|
|
101
|
+
import asyncio
|
|
102
|
+
from alternator import AlternatorConfig
|
|
103
|
+
from alternator.async_client import AsyncAlternatorClient
|
|
104
|
+
|
|
105
|
+
async def main():
|
|
106
|
+
config = AlternatorConfig(
|
|
107
|
+
seed_hosts=["192.168.1.1"],
|
|
108
|
+
port=8000,
|
|
109
|
+
)
|
|
110
|
+
|
|
111
|
+
async with AsyncAlternatorClient(config) as client:
|
|
112
|
+
# Use like a normal aioboto3 DynamoDB client
|
|
113
|
+
response = await client.list_tables()
|
|
114
|
+
print(response["TableNames"])
|
|
115
|
+
|
|
116
|
+
asyncio.run(main())
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
## Configuration
|
|
120
|
+
|
|
121
|
+
### Basic Configuration
|
|
122
|
+
|
|
123
|
+
```python
|
|
124
|
+
from alternator import AlternatorConfig
|
|
125
|
+
|
|
126
|
+
config = AlternatorConfig(
|
|
127
|
+
seed_hosts=["node1.example.com", "node2.example.com"],
|
|
128
|
+
port=8000,
|
|
129
|
+
scheme="http", # or "https" for TLS
|
|
130
|
+
)
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
### Using the Builder Pattern
|
|
134
|
+
|
|
135
|
+
```python
|
|
136
|
+
from alternator import (
|
|
137
|
+
AlternatorConfigBuilder,
|
|
138
|
+
CompressionAlgorithm,
|
|
139
|
+
KeyRouteAffinityMode,
|
|
140
|
+
TlsConfig,
|
|
141
|
+
)
|
|
142
|
+
|
|
143
|
+
config = (
|
|
144
|
+
AlternatorConfigBuilder()
|
|
145
|
+
.with_seeds("node1.example.com", "node2.example.com")
|
|
146
|
+
.with_port(8000)
|
|
147
|
+
.with_https(TlsConfig.system_default())
|
|
148
|
+
.with_datacenter("us-east-1")
|
|
149
|
+
.with_compression(CompressionAlgorithm.GZIP, min_size=1024)
|
|
150
|
+
.with_key_affinity(KeyRouteAffinityMode.RMW)
|
|
151
|
+
.with_refresh_intervals(active_ms=1000, idle_ms=60000)
|
|
152
|
+
.build()
|
|
153
|
+
)
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
### Configuration Options
|
|
157
|
+
|
|
158
|
+
| Option | Type | Default | Description |
|
|
159
|
+
|--------|------|---------|-------------|
|
|
160
|
+
| `seed_hosts` | `Sequence[str]` | (required) | Initial nodes for cluster discovery |
|
|
161
|
+
| `port` | `int` | (required) | Alternator port |
|
|
162
|
+
| `scheme` | `str` | `"http"` | Protocol scheme (`"http"` or `"https"`) |
|
|
163
|
+
| `routing_scope` | `RoutingScope` | `ClusterScope()` | Topology-aware routing |
|
|
164
|
+
| `compression` | `CompressionAlgorithm` | `NONE` | Request compression |
|
|
165
|
+
| `min_compression_size_bytes` | `int` | `1024` | Minimum body size to compress |
|
|
166
|
+
| `optimize_headers` | `bool` | `False` | Enable header filtering |
|
|
167
|
+
| `headers_whitelist` | `frozenset[str]` | `None` | Additional headers to keep |
|
|
168
|
+
| `authentication_enabled` | `bool` | `True` | Include auth headers |
|
|
169
|
+
| `tls` | `TlsConfig` | system default | TLS configuration |
|
|
170
|
+
| `key_affinity` | `KeyRouteAffinityConfig` | `NONE` | Key-based routing |
|
|
171
|
+
| `max_pool_connections` | `int` | `200` | Max connections per host |
|
|
172
|
+
| `active_refresh_interval_ms` | `int` | `1000` | Node refresh interval when active |
|
|
173
|
+
| `idle_refresh_interval_ms` | `int` | `60000` | Node refresh interval when idle |
|
|
174
|
+
|
|
175
|
+
## Routing Scopes
|
|
176
|
+
|
|
177
|
+
Control which nodes receive your requests based on topology:
|
|
178
|
+
|
|
179
|
+
```python
|
|
180
|
+
from alternator import ClusterScope, DatacenterScope, RackScope
|
|
181
|
+
|
|
182
|
+
# Route to any node in the cluster (default)
|
|
183
|
+
config = AlternatorConfig(
|
|
184
|
+
seed_hosts=["node1"],
|
|
185
|
+
port=8000,
|
|
186
|
+
routing_scope=ClusterScope(),
|
|
187
|
+
)
|
|
188
|
+
|
|
189
|
+
# Route to nodes in a specific datacenter
|
|
190
|
+
config = AlternatorConfig(
|
|
191
|
+
seed_hosts=["node1"],
|
|
192
|
+
port=8000,
|
|
193
|
+
routing_scope=DatacenterScope(datacenter="us-east-1"),
|
|
194
|
+
)
|
|
195
|
+
|
|
196
|
+
# Route to nodes in a specific rack (with fallback)
|
|
197
|
+
config = AlternatorConfig(
|
|
198
|
+
seed_hosts=["node1"],
|
|
199
|
+
port=8000,
|
|
200
|
+
routing_scope=RackScope(datacenter="us-east-1", rack="rack1"),
|
|
201
|
+
)
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
Scopes automatically fall back to broader scopes if no nodes are available:
|
|
205
|
+
- `RackScope` → `DatacenterScope` → `ClusterScope`
|
|
206
|
+
|
|
207
|
+
## Key Affinity (LWT Optimization)
|
|
208
|
+
|
|
209
|
+
For Lightweight Transactions (conditional writes), routing requests for the same partition key to the same node can improve performance:
|
|
210
|
+
|
|
211
|
+
```python
|
|
212
|
+
from alternator import (
|
|
213
|
+
AlternatorConfigBuilder,
|
|
214
|
+
KeyRouteAffinityMode,
|
|
215
|
+
)
|
|
216
|
+
|
|
217
|
+
config = (
|
|
218
|
+
AlternatorConfigBuilder()
|
|
219
|
+
.with_seeds("node1")
|
|
220
|
+
.with_port(8000)
|
|
221
|
+
.with_key_affinity(
|
|
222
|
+
mode=KeyRouteAffinityMode.RMW, # Only for read-modify-write ops
|
|
223
|
+
table_pk_map={"my_table": "pk"}, # Optional: preload PK names
|
|
224
|
+
)
|
|
225
|
+
.build()
|
|
226
|
+
)
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
### Affinity Modes
|
|
230
|
+
|
|
231
|
+
| Mode | Description |
|
|
232
|
+
|------|-------------|
|
|
233
|
+
| `NONE` | Disabled (default round-robin) |
|
|
234
|
+
| `RMW` | Only for operations with `ConditionExpression` or `ReturnValues` |
|
|
235
|
+
| `ANY_WRITE` | For all write operations (`PutItem`, `UpdateItem`, `DeleteItem`) |
|
|
236
|
+
|
|
237
|
+
## TLS Configuration
|
|
238
|
+
|
|
239
|
+
```python
|
|
240
|
+
from alternator import TlsConfig, TlsSessionCacheConfig
|
|
241
|
+
from pathlib import Path
|
|
242
|
+
|
|
243
|
+
# Use system CA certificates (default)
|
|
244
|
+
tls = TlsConfig.system_default()
|
|
245
|
+
|
|
246
|
+
# Use custom CA certificate
|
|
247
|
+
tls = TlsConfig.with_custom_ca(Path("/path/to/ca.pem"))
|
|
248
|
+
|
|
249
|
+
# Trust all certificates (INSECURE - dev only)
|
|
250
|
+
tls = TlsConfig.trust_all()
|
|
251
|
+
|
|
252
|
+
# Full configuration
|
|
253
|
+
tls = TlsConfig(
|
|
254
|
+
custom_ca_cert_paths=[Path("/path/to/ca.pem")],
|
|
255
|
+
trust_system_ca_certs=True,
|
|
256
|
+
verify_hostname=True,
|
|
257
|
+
session_cache=TlsSessionCacheConfig(
|
|
258
|
+
enabled=True,
|
|
259
|
+
cache_size=1024,
|
|
260
|
+
timeout_seconds=86400,
|
|
261
|
+
),
|
|
262
|
+
)
|
|
263
|
+
```
|
|
264
|
+
|
|
265
|
+
## Request Compression
|
|
266
|
+
|
|
267
|
+
Enable gzip compression for large request bodies:
|
|
268
|
+
|
|
269
|
+
> **Note:** Gzip request compression requires **ScyllaDB 2026.1.0 or later**. Earlier versions do not support the `Content-Encoding: gzip` header. Response compression (`Accept-Encoding: gzip`) is not yet supported by Alternator.
|
|
270
|
+
|
|
271
|
+
```python
|
|
272
|
+
from alternator import AlternatorConfigBuilder, CompressionAlgorithm
|
|
273
|
+
|
|
274
|
+
config = (
|
|
275
|
+
AlternatorConfigBuilder()
|
|
276
|
+
.with_seeds("node1")
|
|
277
|
+
.with_port(8000)
|
|
278
|
+
.with_compression(
|
|
279
|
+
CompressionAlgorithm.GZIP,
|
|
280
|
+
min_size=1024, # Only compress bodies >= 1KB
|
|
281
|
+
)
|
|
282
|
+
.build()
|
|
283
|
+
)
|
|
284
|
+
```
|
|
285
|
+
|
|
286
|
+
## Error Handling
|
|
287
|
+
|
|
288
|
+
```python
|
|
289
|
+
from alternator import (
|
|
290
|
+
AlternatorClient,
|
|
291
|
+
AlternatorConfig,
|
|
292
|
+
AlternatorError,
|
|
293
|
+
NoNodesAvailableError,
|
|
294
|
+
ConfigurationError,
|
|
295
|
+
)
|
|
296
|
+
|
|
297
|
+
try:
|
|
298
|
+
config = AlternatorConfig(seed_hosts=[], port=8000)
|
|
299
|
+
except ConfigurationError as e:
|
|
300
|
+
print(f"Invalid configuration: {e}")
|
|
301
|
+
|
|
302
|
+
try:
|
|
303
|
+
with AlternatorClient(config) as client:
|
|
304
|
+
client.list_tables()
|
|
305
|
+
except NoNodesAvailableError as e:
|
|
306
|
+
print(f"No nodes available: {e}")
|
|
307
|
+
except AlternatorError as e:
|
|
308
|
+
print(f"Alternator error: {e}")
|
|
309
|
+
```
|
|
310
|
+
|
|
311
|
+
## Logging
|
|
312
|
+
|
|
313
|
+
The library uses Python's standard logging module with the logger name `alternator`:
|
|
314
|
+
|
|
315
|
+
```python
|
|
316
|
+
import logging
|
|
317
|
+
|
|
318
|
+
# Enable debug logging
|
|
319
|
+
logging.basicConfig(level=logging.DEBUG)
|
|
320
|
+
logging.getLogger("alternator").setLevel(logging.DEBUG)
|
|
321
|
+
```
|
|
322
|
+
|
|
323
|
+
Log levels:
|
|
324
|
+
- `INFO`: Node discovery events
|
|
325
|
+
- `WARNING`: Fallback events, connection issues
|
|
326
|
+
- `DEBUG`: Detailed routing decisions, node lists
|
|
327
|
+
- `ERROR`: Failed operations
|
|
328
|
+
|
|
329
|
+
## DynamoDB Resource Interface
|
|
330
|
+
|
|
331
|
+
For table-oriented operations, use `AlternatorResource` which wraps boto3's DynamoDB resource:
|
|
332
|
+
|
|
333
|
+
```python
|
|
334
|
+
from alternator import AlternatorConfig, AlternatorResource
|
|
335
|
+
|
|
336
|
+
config = AlternatorConfig(seed_hosts=["192.168.1.1"], port=8000)
|
|
337
|
+
|
|
338
|
+
with AlternatorResource(config) as resource:
|
|
339
|
+
table = resource.Table("my_table")
|
|
340
|
+
table.put_item(Item={"pk": "user123", "data": "hello"})
|
|
341
|
+
response = table.get_item(Key={"pk": "user123"})
|
|
342
|
+
```
|
|
343
|
+
|
|
344
|
+
You can also use the factory function:
|
|
345
|
+
|
|
346
|
+
```python
|
|
347
|
+
from alternator import create_resource, close_resource, AlternatorConfig
|
|
348
|
+
|
|
349
|
+
config = AlternatorConfig(seed_hosts=["node1"], port=8000)
|
|
350
|
+
resource = create_resource(config)
|
|
351
|
+
|
|
352
|
+
try:
|
|
353
|
+
table = resource.Table("my_table")
|
|
354
|
+
table.scan()
|
|
355
|
+
finally:
|
|
356
|
+
close_resource(resource)
|
|
357
|
+
```
|
|
358
|
+
|
|
359
|
+
## Manual Resource Management
|
|
360
|
+
|
|
361
|
+
If you prefer not to use context managers:
|
|
362
|
+
|
|
363
|
+
```python
|
|
364
|
+
from alternator import create_client, close_client, AlternatorConfig
|
|
365
|
+
|
|
366
|
+
config = AlternatorConfig(seed_hosts=["node1"], port=8000)
|
|
367
|
+
client = create_client(config)
|
|
368
|
+
|
|
369
|
+
try:
|
|
370
|
+
client.list_tables()
|
|
371
|
+
finally:
|
|
372
|
+
close_client(client) # Stop background refresh thread
|
|
373
|
+
```
|
|
374
|
+
|
|
375
|
+
Async equivalent:
|
|
376
|
+
|
|
377
|
+
```python
|
|
378
|
+
from alternator import AlternatorConfig
|
|
379
|
+
from alternator.async_client import create_async_client, close_async_client
|
|
380
|
+
|
|
381
|
+
config = AlternatorConfig(seed_hosts=["node1"], port=8000)
|
|
382
|
+
client = await create_async_client(config)
|
|
383
|
+
|
|
384
|
+
try:
|
|
385
|
+
await client.list_tables()
|
|
386
|
+
finally:
|
|
387
|
+
await close_async_client(client)
|
|
388
|
+
```
|
|
389
|
+
|
|
390
|
+
## Production Recommendations
|
|
391
|
+
|
|
392
|
+
- **Connection pool sizing**: The default `max_pool_connections=200` works for most workloads. Increase if you see connection pool exhaustion warnings under high concurrency.
|
|
393
|
+
- **Refresh intervals**: Default active refresh (1s) is appropriate for dynamic clusters. For stable clusters, increase `active_refresh_interval_ms` to reduce discovery overhead.
|
|
394
|
+
- **Timeouts**: Default `discovery_timeout_seconds=5.0` and `read_timeout_seconds=30.0` are conservative. Tune based on your network latency and query complexity.
|
|
395
|
+
- **Monitoring**: Enable `INFO`-level logging for the `alternator` logger to track node discovery events. Use `DEBUG` for detailed routing decisions during troubleshooting.
|
|
396
|
+
- **Seed hosts**: Configure at least 2-3 seed hosts for redundancy in case one seed is temporarily unavailable during startup.
|
|
397
|
+
|
|
398
|
+
## Thread Safety
|
|
399
|
+
|
|
400
|
+
Sync clients created by `create_client` / `AlternatorClient` are thread-safe: the underlying node selection, round-robin counter, and node list updates are all protected by locks. You can safely share a single client across multiple threads.
|
|
401
|
+
|
|
402
|
+
Async clients created by `create_async_client` / `AsyncAlternatorClient` are safe to use from multiple concurrent coroutines within the same event loop. Do not share an async client across different event loops.
|
|
403
|
+
|
|
404
|
+
## Known Limitations
|
|
405
|
+
|
|
406
|
+
- **Request Compression**: Gzip compression requires ScyllaDB 2026.1.0+. Response compression is not yet supported by Alternator.
|
|
407
|
+
- **TLS Session Cache Settings**: The `cache_size` and `timeout_seconds` parameters in `TlsSessionCacheConfig` are not currently used by Python's `ssl` module. Only the `enabled` flag controls session ticket behavior.
|
|
408
|
+
- **Async Key Affinity**: For async clients, partition key auto-discovery happens asynchronously. The first request for an unknown table will use round-robin routing while discovery runs in the background. Subsequent requests will use affinity. Preloading via `table_pk_map` avoids this initial miss.
|
|
409
|
+
- **Batch Operations**: Key affinity routing does not support `BatchWriteItem` operations with items targeting different partition keys.
|
|
410
|
+
|
|
411
|
+
## Development
|
|
412
|
+
|
|
413
|
+
```bash
|
|
414
|
+
# Clone the repository
|
|
415
|
+
git clone https://github.com/scylladb/alternator-client-python.git
|
|
416
|
+
cd alternator-client-python
|
|
417
|
+
|
|
418
|
+
# Install in development mode
|
|
419
|
+
make install
|
|
420
|
+
|
|
421
|
+
# Run tests
|
|
422
|
+
make test-unit
|
|
423
|
+
|
|
424
|
+
# Run linting
|
|
425
|
+
make lint
|
|
426
|
+
|
|
427
|
+
# Start local Scylla cluster for integration tests
|
|
428
|
+
make scylla-start
|
|
429
|
+
make test-integration
|
|
430
|
+
make scylla-stop
|
|
431
|
+
```
|
|
432
|
+
|
|
433
|
+
## License
|
|
434
|
+
|
|
435
|
+
Apache License 2.0
|
|
436
|
+
|
|
437
|
+
## Contributing
|
|
438
|
+
|
|
439
|
+
Contributions are welcome! Please read the contributing guidelines before submitting a pull request.
|