e6data-python-connector 2.2.6rc1__tar.gz → 2.3.7__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.
- {e6data_python_connector-2.2.6rc1/e6data_python_connector.egg-info → e6data_python_connector-2.3.7}/PKG-INFO +157 -3
- {e6data_python_connector-2.2.6rc1 → e6data_python_connector-2.3.7}/README.md +156 -2
- {e6data_python_connector-2.2.6rc1 → e6data_python_connector-2.3.7}/e6data_python_connector/cluster_manager.py +155 -38
- e6data_python_connector-2.3.7/e6data_python_connector/datainputstream.py +655 -0
- {e6data_python_connector-2.2.6rc1 → e6data_python_connector-2.3.7}/e6data_python_connector/dialect.py +3 -3
- {e6data_python_connector-2.2.6rc1 → e6data_python_connector-2.3.7}/e6data_python_connector/e6data_grpc.py +498 -54
- {e6data_python_connector-2.2.6rc1 → e6data_python_connector-2.3.7}/e6data_python_connector/e6x_vector/constants.py +2 -1
- {e6data_python_connector-2.2.6rc1 → e6data_python_connector-2.3.7}/e6data_python_connector/e6x_vector/ttypes.py +247 -43
- e6data_python_connector-2.3.7/e6data_python_connector/server/e6x_engine_pb2.py +141 -0
- {e6data_python_connector-2.2.6rc1 → e6data_python_connector-2.3.7}/e6data_python_connector/server/e6x_engine_pb2.pyi +127 -73
- {e6data_python_connector-2.2.6rc1 → e6data_python_connector-2.3.7}/e6data_python_connector/server/e6x_engine_pb2_grpc.py +432 -116
- e6data_python_connector-2.3.7/e6data_python_connector/strategy.py +201 -0
- {e6data_python_connector-2.2.6rc1 → e6data_python_connector-2.3.7/e6data_python_connector.egg-info}/PKG-INFO +157 -3
- {e6data_python_connector-2.2.6rc1 → e6data_python_connector-2.3.7}/e6data_python_connector.egg-info/SOURCES.txt +42 -1
- {e6data_python_connector-2.2.6rc1 → e6data_python_connector-2.3.7}/e6data_python_connector.egg-info/top_level.txt +1 -0
- {e6data_python_connector-2.2.6rc1 → e6data_python_connector-2.3.7}/setup.py +1 -1
- e6data_python_connector-2.3.7/test/__init__.py +1 -0
- e6data_python_connector-2.3.7/test/analyze_38_nines.py +97 -0
- e6data_python_connector-2.3.7/test/analyze_all_cases.py +130 -0
- e6data_python_connector-2.3.7/test/analyze_binary.py +70 -0
- e6data_python_connector-2.3.7/test/analyze_correct_value.py +116 -0
- e6data_python_connector-2.3.7/test/analyze_fields.py +123 -0
- e6data_python_connector-2.3.7/test/check_decimal_errors.py +25 -0
- e6data_python_connector-2.3.7/test/cleanup_test_files.py +31 -0
- e6data_python_connector-2.3.7/test/debug_38_nines.py +80 -0
- e6data_python_connector-2.3.7/test/debug_binary.py +151 -0
- e6data_python_connector-2.3.7/test/final_test.py +175 -0
- e6data_python_connector-2.3.7/test/move_tests.py +48 -0
- e6data_python_connector-2.3.7/test/quick_test.py +26 -0
- e6data_python_connector-2.3.7/test/test_38_nines.py +11 -0
- e6data_python_connector-2.3.7/test/test_all_decimal128_cases.py +145 -0
- e6data_python_connector-2.3.7/test/test_cluster_manager_efficiency.py +198 -0
- e6data_python_connector-2.3.7/test/test_cluster_manager_none_strategy.py +187 -0
- e6data_python_connector-2.3.7/test/test_cluster_manager_strategy.py +157 -0
- e6data_python_connector-2.3.7/test/test_comprehensive.py +172 -0
- e6data_python_connector-2.3.7/test/test_current_implementation.py +118 -0
- e6data_python_connector-2.3.7/test/test_decimal128_binary_parsing.py +200 -0
- e6data_python_connector-2.3.7/test/test_decimal128_parsing.py +254 -0
- e6data_python_connector-2.3.7/test/test_fix.py +28 -0
- e6data_python_connector-2.3.7/test/test_improved_parsing.py +113 -0
- e6data_python_connector-2.3.7/test/test_known_case.py +66 -0
- e6data_python_connector-2.3.7/test/test_manual_analysis.py +110 -0
- e6data_python_connector-2.3.7/test/test_mock_server.py +183 -0
- e6data_python_connector-2.3.7/test/test_multiprocessing_fix.py +122 -0
- e6data_python_connector-2.3.7/test/test_new_implementation.py +147 -0
- e6data_python_connector-2.3.7/test/test_specific_binary.py +99 -0
- e6data_python_connector-2.3.7/test/test_strategy.py +293 -0
- e6data_python_connector-2.3.7/test/test_strategy_logic.py +101 -0
- e6data_python_connector-2.3.7/test/test_strategy_persistence_fix.py +237 -0
- e6data_python_connector-2.3.7/test/test_strategy_sharing_fix.py +142 -0
- e6data_python_connector-2.3.7/test/test_user_binary_value.py +71 -0
- e6data_python_connector-2.3.7/test/tests.py +156 -0
- e6data_python_connector-2.3.7/test/tests_grpc.py +155 -0
- e6data_python_connector-2.3.7/test/validate_decimal128.py +75 -0
- e6data_python_connector-2.3.7/test/validate_implementation.py +152 -0
- e6data_python_connector-2.3.7/test/verify_decimal_fix.py +35 -0
- e6data_python_connector-2.2.6rc1/e6data_python_connector/datainputstream.py +0 -306
- e6data_python_connector-2.2.6rc1/e6data_python_connector/server/e6x_engine_pb2.py +0 -140
- {e6data_python_connector-2.2.6rc1 → e6data_python_connector-2.3.7}/LICENSE +0 -0
- {e6data_python_connector-2.2.6rc1 → e6data_python_connector-2.3.7}/MANIFEST.in +0 -0
- {e6data_python_connector-2.2.6rc1 → e6data_python_connector-2.3.7}/e6data_python_connector/__init__.py +0 -0
- {e6data_python_connector-2.2.6rc1 → e6data_python_connector-2.3.7}/e6data_python_connector/cluster_server/__init__.py +0 -0
- {e6data_python_connector-2.2.6rc1 → e6data_python_connector-2.3.7}/e6data_python_connector/cluster_server/cluster_pb2.py +0 -0
- {e6data_python_connector-2.2.6rc1 → e6data_python_connector-2.3.7}/e6data_python_connector/cluster_server/cluster_pb2_grpc.py +0 -0
- {e6data_python_connector-2.2.6rc1 → e6data_python_connector-2.3.7}/e6data_python_connector/common.py +0 -0
- {e6data_python_connector-2.2.6rc1 → e6data_python_connector-2.3.7}/e6data_python_connector/constants.py +0 -0
- {e6data_python_connector-2.2.6rc1 → e6data_python_connector-2.3.7}/e6data_python_connector/date_time_utils.py +0 -0
- {e6data_python_connector-2.2.6rc1 → e6data_python_connector-2.3.7}/e6data_python_connector/e6x_vector/__init__.py +0 -0
- {e6data_python_connector-2.2.6rc1 → e6data_python_connector-2.3.7}/e6data_python_connector/exceptions.py +0 -0
- {e6data_python_connector-2.2.6rc1 → e6data_python_connector-2.3.7}/e6data_python_connector/server/QueryEngineService.py +0 -0
- {e6data_python_connector-2.2.6rc1 → e6data_python_connector-2.3.7}/e6data_python_connector/server/__init__.py +0 -0
- {e6data_python_connector-2.2.6rc1 → e6data_python_connector-2.3.7}/e6data_python_connector/server/constants.py +0 -0
- {e6data_python_connector-2.2.6rc1 → e6data_python_connector-2.3.7}/e6data_python_connector/server/ttypes.py +0 -0
- {e6data_python_connector-2.2.6rc1 → e6data_python_connector-2.3.7}/e6data_python_connector/typeId.py +0 -0
- {e6data_python_connector-2.2.6rc1 → e6data_python_connector-2.3.7}/e6data_python_connector.egg-info/dependency_links.txt +0 -0
- {e6data_python_connector-2.2.6rc1 → e6data_python_connector-2.3.7}/e6data_python_connector.egg-info/entry_points.txt +0 -0
- {e6data_python_connector-2.2.6rc1 → e6data_python_connector-2.3.7}/e6data_python_connector.egg-info/requires.txt +0 -0
- {e6data_python_connector-2.2.6rc1 → e6data_python_connector-2.3.7}/pyproject.toml +0 -0
- {e6data_python_connector-2.2.6rc1 → e6data_python_connector-2.3.7}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.2
|
|
2
2
|
Name: e6data-python-connector
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.3.7
|
|
4
4
|
Summary: Client for the e6data distributed SQL Engine.
|
|
5
5
|
Home-page: https://github.com/e6x-labs/e6data-python-connector
|
|
6
6
|
Author: e6data, Inc.
|
|
@@ -38,11 +38,11 @@ Dynamic: summary
|
|
|
38
38
|
|
|
39
39
|
# e6data Python Connector
|
|
40
40
|
|
|
41
|
-

|
|
42
42
|
|
|
43
43
|
## Introduction
|
|
44
44
|
|
|
45
|
-
The e6data Connector for Python provides an interface for writing Python applications that can connect to e6data and perform operations.
|
|
45
|
+
The e6data Connector for Python provides an interface for writing Python applications that can connect to e6data and perform operations. It includes automatic support for blue-green deployments, ensuring seamless failover during server updates without query interruption.
|
|
46
46
|
|
|
47
47
|
### Dependencies
|
|
48
48
|
Make sure to install below dependencies and wheel before install e6data-python-connector.
|
|
@@ -241,3 +241,157 @@ cursor.clear()
|
|
|
241
241
|
cursor.close()
|
|
242
242
|
conn.close()
|
|
243
243
|
```
|
|
244
|
+
|
|
245
|
+
## Zero Downtime Deployment
|
|
246
|
+
|
|
247
|
+
### 🚀 Zero Downtime Features
|
|
248
|
+
|
|
249
|
+
The e6data Python Connector provides **automatic zero downtime deployment** support through intelligent blue-green deployment strategy management:
|
|
250
|
+
|
|
251
|
+
#### ✅ **No Code Changes Required**
|
|
252
|
+
Your existing applications automatically benefit from zero downtime deployment without any modifications:
|
|
253
|
+
|
|
254
|
+
```python
|
|
255
|
+
# Your existing code works exactly the same
|
|
256
|
+
from e6data_python_connector import Connection
|
|
257
|
+
|
|
258
|
+
conn = Connection(
|
|
259
|
+
host='your-host',
|
|
260
|
+
port=80,
|
|
261
|
+
username='your-email',
|
|
262
|
+
password='your-token',
|
|
263
|
+
database='your-database'
|
|
264
|
+
)
|
|
265
|
+
|
|
266
|
+
cursor = conn.cursor()
|
|
267
|
+
cursor.execute("SELECT * FROM your_table")
|
|
268
|
+
results = cursor.fetchall()
|
|
269
|
+
```
|
|
270
|
+
|
|
271
|
+
#### 🔄 **Automatic Strategy Detection**
|
|
272
|
+
- Detects active deployment strategy (blue/green) on connection
|
|
273
|
+
- Caches strategy information for optimal performance
|
|
274
|
+
- Automatically switches strategies when deployments occur
|
|
275
|
+
|
|
276
|
+
#### 🛡️ **Seamless Query Protection**
|
|
277
|
+
- **Running queries continue uninterrupted** during deployments
|
|
278
|
+
- New queries automatically use the new deployment strategy
|
|
279
|
+
- Graceful transitions ensure no query loss or failures
|
|
280
|
+
|
|
281
|
+
#### ⚡ **Performance Optimized**
|
|
282
|
+
- **< 100ms** additional latency on first connection (one-time cost)
|
|
283
|
+
- **0ms overhead** for 95% of queries (cached strategy)
|
|
284
|
+
- **< 1KB** additional memory usage per connection
|
|
285
|
+
|
|
286
|
+
#### 🔧 **Thread & Process Safe**
|
|
287
|
+
- Full support for multi-threaded applications
|
|
288
|
+
- Process-safe shared memory management
|
|
289
|
+
- Concurrent query execution without conflicts
|
|
290
|
+
|
|
291
|
+
### Advanced Configuration (Optional)
|
|
292
|
+
|
|
293
|
+
For enhanced monitoring and performance tuning:
|
|
294
|
+
|
|
295
|
+
```python
|
|
296
|
+
# Enhanced gRPC configuration for zero downtime
|
|
297
|
+
grpc_options = {
|
|
298
|
+
'keepalive_timeout_ms': 60000, # 1 minute keepalive timeout
|
|
299
|
+
'keepalive_time_ms': 30000, # 30 seconds keepalive interval
|
|
300
|
+
'max_receive_message_length': 100 * 1024 * 1024, # 100MB
|
|
301
|
+
'max_send_message_length': 100 * 1024 * 1024, # 100MB
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
conn = Connection(
|
|
305
|
+
host='your-host',
|
|
306
|
+
port=80,
|
|
307
|
+
username='your-email',
|
|
308
|
+
password='your-token',
|
|
309
|
+
database='your-database',
|
|
310
|
+
grpc_options=grpc_options
|
|
311
|
+
)
|
|
312
|
+
```
|
|
313
|
+
|
|
314
|
+
### Environment Configuration
|
|
315
|
+
|
|
316
|
+
Configure zero downtime features using environment variables:
|
|
317
|
+
|
|
318
|
+
```bash
|
|
319
|
+
# Strategy cache timeout (default: 300 seconds)
|
|
320
|
+
export E6DATA_STRATEGY_CACHE_TIMEOUT=300
|
|
321
|
+
|
|
322
|
+
# Maximum retry attempts (default: 5)
|
|
323
|
+
export E6DATA_MAX_RETRY_ATTEMPTS=5
|
|
324
|
+
|
|
325
|
+
# Enable debug logging for strategy operations
|
|
326
|
+
export E6DATA_STRATEGY_LOG_LEVEL=INFO
|
|
327
|
+
```
|
|
328
|
+
|
|
329
|
+
### Testing Zero Downtime
|
|
330
|
+
|
|
331
|
+
Use the included mock server for testing and development:
|
|
332
|
+
|
|
333
|
+
```bash
|
|
334
|
+
# Terminal 1: Start mock server
|
|
335
|
+
python mock_grpc_server.py
|
|
336
|
+
|
|
337
|
+
# Terminal 2: Run test client
|
|
338
|
+
python test_mock_server.py
|
|
339
|
+
|
|
340
|
+
# Or use the convenience script
|
|
341
|
+
./run_mock_test.sh
|
|
342
|
+
```
|
|
343
|
+
|
|
344
|
+
### 📚 **Comprehensive Documentation**
|
|
345
|
+
|
|
346
|
+
Explore detailed documentation in the [`docs/zero-downtime/`](docs/zero-downtime/) directory:
|
|
347
|
+
|
|
348
|
+
- **[📋 Overview](docs/zero-downtime/README.md)** - Complete guide and feature overview
|
|
349
|
+
- **[🔧 API Reference](docs/zero-downtime/api-reference.md)** - Detailed API documentation
|
|
350
|
+
- **[🌊 Flow Documentation](docs/zero-downtime/flow-documentation.md)** - Process flows and diagrams
|
|
351
|
+
- **[💼 Business Logic](docs/zero-downtime/business-logic.md)** - Business rules and decisions
|
|
352
|
+
- **[🏗️ Architecture](docs/zero-downtime/architecture.md)** - System architecture and design
|
|
353
|
+
- **[⚙️ Configuration](docs/zero-downtime/configuration.md)** - Complete configuration guide
|
|
354
|
+
- **[🧪 Testing](docs/zero-downtime/testing.md)** - Testing strategies and tools
|
|
355
|
+
- **[🔍 Troubleshooting](docs/zero-downtime/troubleshooting.md)** - Common issues and solutions
|
|
356
|
+
- **[🚀 Migration Guide](docs/zero-downtime/migration-guide.md)** - Step-by-step migration instructions
|
|
357
|
+
|
|
358
|
+
### Key Benefits
|
|
359
|
+
|
|
360
|
+
| Feature | Benefit |
|
|
361
|
+
|---------|---------|
|
|
362
|
+
| **Zero Downtime** | Applications continue running during e6data deployments |
|
|
363
|
+
| **Automatic** | No code changes or manual intervention required |
|
|
364
|
+
| **Reliable** | Robust error handling and automatic recovery |
|
|
365
|
+
| **Fast** | Minimal performance impact with intelligent caching |
|
|
366
|
+
| **Safe** | Thread-safe and process-safe operation |
|
|
367
|
+
| **Monitored** | Comprehensive logging and monitoring capabilities |
|
|
368
|
+
|
|
369
|
+
### Migration
|
|
370
|
+
|
|
371
|
+
Existing applications automatically benefit from zero downtime deployment:
|
|
372
|
+
|
|
373
|
+
1. **Update connector**: `pip install --upgrade e6data-python-connector`
|
|
374
|
+
2. **No code changes**: Your existing code works without modifications
|
|
375
|
+
3. **Monitor**: Use enhanced logging to monitor strategy transitions
|
|
376
|
+
4. **Validate**: Test with your existing applications
|
|
377
|
+
|
|
378
|
+
For detailed migration instructions, see the [Migration Guide](docs/zero-downtime/migration-guide.md).
|
|
379
|
+
|
|
380
|
+
## Performance Optimization
|
|
381
|
+
|
|
382
|
+
### Memory Efficiency
|
|
383
|
+
- Use `fetchall_buffer()` for memory-efficient large result sets
|
|
384
|
+
- Automatic cleanup of query-strategy mappings
|
|
385
|
+
- Bounded memory usage with TTL-based caching
|
|
386
|
+
|
|
387
|
+
### Network Performance
|
|
388
|
+
- Configure gRPC options for optimal network performance
|
|
389
|
+
- Intelligent keepalive settings for connection stability
|
|
390
|
+
- Message size optimization for large queries
|
|
391
|
+
|
|
392
|
+
### Connection Management
|
|
393
|
+
- Enable connection pooling for better resource utilization
|
|
394
|
+
- Automatic connection health monitoring
|
|
395
|
+
- Graceful connection recovery and retry logic
|
|
396
|
+
|
|
397
|
+
See [TECH_DOC.md](TECH_DOC.md) for detailed technical documentation.
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
# e6data Python Connector
|
|
2
2
|
|
|
3
|
-

|
|
4
4
|
|
|
5
5
|
## Introduction
|
|
6
6
|
|
|
7
|
-
The e6data Connector for Python provides an interface for writing Python applications that can connect to e6data and perform operations.
|
|
7
|
+
The e6data Connector for Python provides an interface for writing Python applications that can connect to e6data and perform operations. It includes automatic support for blue-green deployments, ensuring seamless failover during server updates without query interruption.
|
|
8
8
|
|
|
9
9
|
### Dependencies
|
|
10
10
|
Make sure to install below dependencies and wheel before install e6data-python-connector.
|
|
@@ -203,3 +203,157 @@ cursor.clear()
|
|
|
203
203
|
cursor.close()
|
|
204
204
|
conn.close()
|
|
205
205
|
```
|
|
206
|
+
|
|
207
|
+
## Zero Downtime Deployment
|
|
208
|
+
|
|
209
|
+
### 🚀 Zero Downtime Features
|
|
210
|
+
|
|
211
|
+
The e6data Python Connector provides **automatic zero downtime deployment** support through intelligent blue-green deployment strategy management:
|
|
212
|
+
|
|
213
|
+
#### ✅ **No Code Changes Required**
|
|
214
|
+
Your existing applications automatically benefit from zero downtime deployment without any modifications:
|
|
215
|
+
|
|
216
|
+
```python
|
|
217
|
+
# Your existing code works exactly the same
|
|
218
|
+
from e6data_python_connector import Connection
|
|
219
|
+
|
|
220
|
+
conn = Connection(
|
|
221
|
+
host='your-host',
|
|
222
|
+
port=80,
|
|
223
|
+
username='your-email',
|
|
224
|
+
password='your-token',
|
|
225
|
+
database='your-database'
|
|
226
|
+
)
|
|
227
|
+
|
|
228
|
+
cursor = conn.cursor()
|
|
229
|
+
cursor.execute("SELECT * FROM your_table")
|
|
230
|
+
results = cursor.fetchall()
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
#### 🔄 **Automatic Strategy Detection**
|
|
234
|
+
- Detects active deployment strategy (blue/green) on connection
|
|
235
|
+
- Caches strategy information for optimal performance
|
|
236
|
+
- Automatically switches strategies when deployments occur
|
|
237
|
+
|
|
238
|
+
#### 🛡️ **Seamless Query Protection**
|
|
239
|
+
- **Running queries continue uninterrupted** during deployments
|
|
240
|
+
- New queries automatically use the new deployment strategy
|
|
241
|
+
- Graceful transitions ensure no query loss or failures
|
|
242
|
+
|
|
243
|
+
#### ⚡ **Performance Optimized**
|
|
244
|
+
- **< 100ms** additional latency on first connection (one-time cost)
|
|
245
|
+
- **0ms overhead** for 95% of queries (cached strategy)
|
|
246
|
+
- **< 1KB** additional memory usage per connection
|
|
247
|
+
|
|
248
|
+
#### 🔧 **Thread & Process Safe**
|
|
249
|
+
- Full support for multi-threaded applications
|
|
250
|
+
- Process-safe shared memory management
|
|
251
|
+
- Concurrent query execution without conflicts
|
|
252
|
+
|
|
253
|
+
### Advanced Configuration (Optional)
|
|
254
|
+
|
|
255
|
+
For enhanced monitoring and performance tuning:
|
|
256
|
+
|
|
257
|
+
```python
|
|
258
|
+
# Enhanced gRPC configuration for zero downtime
|
|
259
|
+
grpc_options = {
|
|
260
|
+
'keepalive_timeout_ms': 60000, # 1 minute keepalive timeout
|
|
261
|
+
'keepalive_time_ms': 30000, # 30 seconds keepalive interval
|
|
262
|
+
'max_receive_message_length': 100 * 1024 * 1024, # 100MB
|
|
263
|
+
'max_send_message_length': 100 * 1024 * 1024, # 100MB
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
conn = Connection(
|
|
267
|
+
host='your-host',
|
|
268
|
+
port=80,
|
|
269
|
+
username='your-email',
|
|
270
|
+
password='your-token',
|
|
271
|
+
database='your-database',
|
|
272
|
+
grpc_options=grpc_options
|
|
273
|
+
)
|
|
274
|
+
```
|
|
275
|
+
|
|
276
|
+
### Environment Configuration
|
|
277
|
+
|
|
278
|
+
Configure zero downtime features using environment variables:
|
|
279
|
+
|
|
280
|
+
```bash
|
|
281
|
+
# Strategy cache timeout (default: 300 seconds)
|
|
282
|
+
export E6DATA_STRATEGY_CACHE_TIMEOUT=300
|
|
283
|
+
|
|
284
|
+
# Maximum retry attempts (default: 5)
|
|
285
|
+
export E6DATA_MAX_RETRY_ATTEMPTS=5
|
|
286
|
+
|
|
287
|
+
# Enable debug logging for strategy operations
|
|
288
|
+
export E6DATA_STRATEGY_LOG_LEVEL=INFO
|
|
289
|
+
```
|
|
290
|
+
|
|
291
|
+
### Testing Zero Downtime
|
|
292
|
+
|
|
293
|
+
Use the included mock server for testing and development:
|
|
294
|
+
|
|
295
|
+
```bash
|
|
296
|
+
# Terminal 1: Start mock server
|
|
297
|
+
python mock_grpc_server.py
|
|
298
|
+
|
|
299
|
+
# Terminal 2: Run test client
|
|
300
|
+
python test_mock_server.py
|
|
301
|
+
|
|
302
|
+
# Or use the convenience script
|
|
303
|
+
./run_mock_test.sh
|
|
304
|
+
```
|
|
305
|
+
|
|
306
|
+
### 📚 **Comprehensive Documentation**
|
|
307
|
+
|
|
308
|
+
Explore detailed documentation in the [`docs/zero-downtime/`](docs/zero-downtime/) directory:
|
|
309
|
+
|
|
310
|
+
- **[📋 Overview](docs/zero-downtime/README.md)** - Complete guide and feature overview
|
|
311
|
+
- **[🔧 API Reference](docs/zero-downtime/api-reference.md)** - Detailed API documentation
|
|
312
|
+
- **[🌊 Flow Documentation](docs/zero-downtime/flow-documentation.md)** - Process flows and diagrams
|
|
313
|
+
- **[💼 Business Logic](docs/zero-downtime/business-logic.md)** - Business rules and decisions
|
|
314
|
+
- **[🏗️ Architecture](docs/zero-downtime/architecture.md)** - System architecture and design
|
|
315
|
+
- **[⚙️ Configuration](docs/zero-downtime/configuration.md)** - Complete configuration guide
|
|
316
|
+
- **[🧪 Testing](docs/zero-downtime/testing.md)** - Testing strategies and tools
|
|
317
|
+
- **[🔍 Troubleshooting](docs/zero-downtime/troubleshooting.md)** - Common issues and solutions
|
|
318
|
+
- **[🚀 Migration Guide](docs/zero-downtime/migration-guide.md)** - Step-by-step migration instructions
|
|
319
|
+
|
|
320
|
+
### Key Benefits
|
|
321
|
+
|
|
322
|
+
| Feature | Benefit |
|
|
323
|
+
|---------|---------|
|
|
324
|
+
| **Zero Downtime** | Applications continue running during e6data deployments |
|
|
325
|
+
| **Automatic** | No code changes or manual intervention required |
|
|
326
|
+
| **Reliable** | Robust error handling and automatic recovery |
|
|
327
|
+
| **Fast** | Minimal performance impact with intelligent caching |
|
|
328
|
+
| **Safe** | Thread-safe and process-safe operation |
|
|
329
|
+
| **Monitored** | Comprehensive logging and monitoring capabilities |
|
|
330
|
+
|
|
331
|
+
### Migration
|
|
332
|
+
|
|
333
|
+
Existing applications automatically benefit from zero downtime deployment:
|
|
334
|
+
|
|
335
|
+
1. **Update connector**: `pip install --upgrade e6data-python-connector`
|
|
336
|
+
2. **No code changes**: Your existing code works without modifications
|
|
337
|
+
3. **Monitor**: Use enhanced logging to monitor strategy transitions
|
|
338
|
+
4. **Validate**: Test with your existing applications
|
|
339
|
+
|
|
340
|
+
For detailed migration instructions, see the [Migration Guide](docs/zero-downtime/migration-guide.md).
|
|
341
|
+
|
|
342
|
+
## Performance Optimization
|
|
343
|
+
|
|
344
|
+
### Memory Efficiency
|
|
345
|
+
- Use `fetchall_buffer()` for memory-efficient large result sets
|
|
346
|
+
- Automatic cleanup of query-strategy mappings
|
|
347
|
+
- Bounded memory usage with TTL-based caching
|
|
348
|
+
|
|
349
|
+
### Network Performance
|
|
350
|
+
- Configure gRPC options for optimal network performance
|
|
351
|
+
- Intelligent keepalive settings for connection stability
|
|
352
|
+
- Message size optimization for large queries
|
|
353
|
+
|
|
354
|
+
### Connection Management
|
|
355
|
+
- Enable connection pooling for better resource utilization
|
|
356
|
+
- Automatic connection health monitoring
|
|
357
|
+
- Graceful connection recovery and retry logic
|
|
358
|
+
|
|
359
|
+
See [TECH_DOC.md](TECH_DOC.md) for detailed technical documentation.
|
|
@@ -6,27 +6,27 @@ import grpc
|
|
|
6
6
|
from grpc._channel import _InactiveRpcError
|
|
7
7
|
import multiprocessing
|
|
8
8
|
|
|
9
|
+
from e6data_python_connector.strategy import _get_active_strategy, _set_active_strategy, _set_pending_strategy, \
|
|
10
|
+
_get_grpc_header as _get_strategy_header
|
|
9
11
|
|
|
10
|
-
|
|
12
|
+
|
|
13
|
+
def _get_grpc_header(engine_ip=None, cluster=None, strategy=None):
|
|
11
14
|
"""
|
|
12
15
|
Generate gRPC metadata headers for the request.
|
|
13
16
|
|
|
14
17
|
This function creates a list of metadata headers to be used in gRPC requests.
|
|
15
|
-
It includes optional headers for the engine IP and
|
|
18
|
+
It includes optional headers for the engine IP, cluster UUID, and deployment strategy.
|
|
16
19
|
|
|
17
20
|
Args:
|
|
18
21
|
engine_ip (str, optional): The IP address of the engine. Defaults to None.
|
|
19
22
|
cluster (str, optional): The UUID of the cluster. Defaults to None.
|
|
23
|
+
strategy (str, optional): The deployment strategy (blue/green). Defaults to None.
|
|
20
24
|
|
|
21
25
|
Returns:
|
|
22
26
|
list: A list of tuples representing the gRPC metadata headers.
|
|
23
27
|
"""
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
metadata.append(('plannerip', engine_ip))
|
|
27
|
-
if cluster:
|
|
28
|
-
metadata.append(('cluster-uuid', cluster))
|
|
29
|
-
return metadata
|
|
28
|
+
# Use the strategy module's implementation
|
|
29
|
+
return _get_strategy_header(engine_ip=engine_ip, cluster=cluster, strategy=strategy)
|
|
30
30
|
|
|
31
31
|
|
|
32
32
|
class _StatusLock:
|
|
@@ -136,7 +136,8 @@ class ClusterManager:
|
|
|
136
136
|
cluster_uuid (str): The unique identifier for the target cluster.
|
|
137
137
|
"""
|
|
138
138
|
|
|
139
|
-
def __init__(self, host: str, port: int, user: str, password: str, secure_channel: bool = False, timeout=60 * 5,
|
|
139
|
+
def __init__(self, host: str, port: int, user: str, password: str, secure_channel: bool = False, timeout=60 * 5,
|
|
140
|
+
cluster_uuid=None, grpc_options=None):
|
|
140
141
|
"""
|
|
141
142
|
Initializes a new instance of the ClusterManager class.
|
|
142
143
|
|
|
@@ -187,22 +188,146 @@ class ClusterManager:
|
|
|
187
188
|
)
|
|
188
189
|
return cluster_pb2_grpc.ClusterServiceStub(self._channel)
|
|
189
190
|
|
|
190
|
-
def
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
191
|
+
def _try_cluster_request(self, request_type, payload=None):
|
|
192
|
+
"""
|
|
193
|
+
Execute a cluster request with strategy fallback for 456 errors.
|
|
194
|
+
|
|
195
|
+
For efficiency:
|
|
196
|
+
- If we have an active strategy, use it first
|
|
197
|
+
- Only try authentication sequence (blue -> green) if no active strategy
|
|
198
|
+
- On 456 error, switch to alternative strategy and update active strategy
|
|
199
|
+
|
|
200
|
+
Args:
|
|
201
|
+
request_type: Type of request ('status' or 'resume')
|
|
202
|
+
payload: Request payload (optional, will be created if not provided)
|
|
203
|
+
|
|
204
|
+
Returns:
|
|
205
|
+
The response from the successful request
|
|
206
|
+
"""
|
|
207
|
+
current_strategy = _get_active_strategy()
|
|
208
|
+
|
|
209
|
+
# Create payload if not provided
|
|
210
|
+
if payload is None:
|
|
211
|
+
if request_type == "status":
|
|
212
|
+
payload = cluster_pb2.ClusterStatusRequest(
|
|
195
213
|
user=self._user,
|
|
196
214
|
password=self._password
|
|
197
215
|
)
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
216
|
+
elif request_type == "resume":
|
|
217
|
+
payload = cluster_pb2.ResumeRequest(
|
|
218
|
+
user=self._user,
|
|
219
|
+
password=self._password
|
|
202
220
|
)
|
|
203
|
-
|
|
204
|
-
|
|
221
|
+
|
|
222
|
+
# If we have an active strategy, use it first
|
|
223
|
+
if current_strategy is not None:
|
|
224
|
+
try:
|
|
225
|
+
if request_type == "status":
|
|
226
|
+
response = self._get_connection.status(
|
|
227
|
+
payload,
|
|
228
|
+
metadata=_get_grpc_header(cluster=self.cluster_uuid, strategy=current_strategy)
|
|
229
|
+
)
|
|
230
|
+
elif request_type == "resume":
|
|
231
|
+
response = self._get_connection.resume(
|
|
232
|
+
payload,
|
|
233
|
+
metadata=_get_grpc_header(cluster=self.cluster_uuid, strategy=current_strategy)
|
|
234
|
+
)
|
|
235
|
+
else:
|
|
236
|
+
raise ValueError(f"Unknown request type: {request_type}")
|
|
237
|
+
|
|
238
|
+
# Check for new strategy in response
|
|
239
|
+
if hasattr(response, 'new_strategy') and response.new_strategy:
|
|
240
|
+
new_strategy = response.new_strategy.lower()
|
|
241
|
+
if new_strategy != current_strategy:
|
|
242
|
+
_set_pending_strategy(new_strategy)
|
|
243
|
+
|
|
244
|
+
return response
|
|
245
|
+
|
|
205
246
|
except _InactiveRpcError as e:
|
|
247
|
+
if e.code() == grpc.StatusCode.UNKNOWN and 'status: 456' in e.details():
|
|
248
|
+
# 456 error - switch to alternative strategy
|
|
249
|
+
alternative_strategy = 'green' if current_strategy == 'blue' else 'blue'
|
|
250
|
+
|
|
251
|
+
try:
|
|
252
|
+
if request_type == "status":
|
|
253
|
+
response = self._get_connection.status(
|
|
254
|
+
payload,
|
|
255
|
+
metadata=_get_grpc_header(cluster=self.cluster_uuid, strategy=alternative_strategy)
|
|
256
|
+
)
|
|
257
|
+
elif request_type == "resume":
|
|
258
|
+
response = self._get_connection.resume(
|
|
259
|
+
payload,
|
|
260
|
+
metadata=_get_grpc_header(cluster=self.cluster_uuid, strategy=alternative_strategy)
|
|
261
|
+
)
|
|
262
|
+
|
|
263
|
+
# Update active strategy since the alternative worked
|
|
264
|
+
_set_active_strategy(alternative_strategy)
|
|
265
|
+
|
|
266
|
+
# Check for new strategy in response
|
|
267
|
+
if hasattr(response, 'new_strategy') and response.new_strategy:
|
|
268
|
+
new_strategy = response.new_strategy.lower()
|
|
269
|
+
if new_strategy != alternative_strategy:
|
|
270
|
+
_set_pending_strategy(new_strategy)
|
|
271
|
+
|
|
272
|
+
return response
|
|
273
|
+
|
|
274
|
+
except _InactiveRpcError as e2:
|
|
275
|
+
raise e # Raise the original error
|
|
276
|
+
else:
|
|
277
|
+
# Non-456 error - don't retry
|
|
278
|
+
raise e
|
|
279
|
+
|
|
280
|
+
# No active strategy - start with authentication logic (blue first, then green)
|
|
281
|
+
strategies_to_try = ['blue', 'green']
|
|
282
|
+
|
|
283
|
+
for i, strategy in enumerate(strategies_to_try):
|
|
284
|
+
try:
|
|
285
|
+
|
|
286
|
+
if request_type == "status":
|
|
287
|
+
response = self._get_connection.status(
|
|
288
|
+
payload,
|
|
289
|
+
metadata=_get_grpc_header(cluster=self.cluster_uuid, strategy=strategy)
|
|
290
|
+
)
|
|
291
|
+
elif request_type == "resume":
|
|
292
|
+
response = self._get_connection.resume(
|
|
293
|
+
payload,
|
|
294
|
+
metadata=_get_grpc_header(cluster=self.cluster_uuid, strategy=strategy)
|
|
295
|
+
)
|
|
296
|
+
else:
|
|
297
|
+
raise ValueError(f"Unknown request type: {request_type}")
|
|
298
|
+
|
|
299
|
+
# Set the working strategy as active
|
|
300
|
+
_set_active_strategy(strategy)
|
|
301
|
+
|
|
302
|
+
# Check for new strategy in response
|
|
303
|
+
if hasattr(response, 'new_strategy') and response.new_strategy:
|
|
304
|
+
new_strategy = response.new_strategy.lower()
|
|
305
|
+
if new_strategy != strategy:
|
|
306
|
+
_set_pending_strategy(new_strategy)
|
|
307
|
+
|
|
308
|
+
return response
|
|
309
|
+
|
|
310
|
+
except _InactiveRpcError as e:
|
|
311
|
+
if e.code() == grpc.StatusCode.UNKNOWN and 'status: 456' in e.details():
|
|
312
|
+
# 456 error - try next strategy
|
|
313
|
+
if i < len(strategies_to_try) - 1:
|
|
314
|
+
continue
|
|
315
|
+
else:
|
|
316
|
+
raise e
|
|
317
|
+
else:
|
|
318
|
+
# Non-456 error - don't retry
|
|
319
|
+
raise e
|
|
320
|
+
|
|
321
|
+
# If we get here, all strategies failed
|
|
322
|
+
raise e
|
|
323
|
+
|
|
324
|
+
def _check_cluster_status(self):
|
|
325
|
+
while True:
|
|
326
|
+
try:
|
|
327
|
+
# Use the unified strategy-aware request method
|
|
328
|
+
response = self._try_cluster_request("status")
|
|
329
|
+
yield response.status
|
|
330
|
+
except _InactiveRpcError:
|
|
206
331
|
yield None
|
|
207
332
|
|
|
208
333
|
def resume(self) -> bool:
|
|
@@ -237,25 +362,17 @@ class ClusterManager:
|
|
|
237
362
|
if lock.is_active:
|
|
238
363
|
return True
|
|
239
364
|
|
|
240
|
-
# Retrieve the current cluster status
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
current_status = self._get_connection.status(
|
|
246
|
-
status_payload,
|
|
247
|
-
metadata=_get_grpc_header(cluster=self.cluster_uuid)
|
|
248
|
-
)
|
|
365
|
+
# Retrieve the current cluster status with strategy header
|
|
366
|
+
try:
|
|
367
|
+
current_status = self._try_cluster_request("status")
|
|
368
|
+
except _InactiveRpcError:
|
|
369
|
+
return False
|
|
249
370
|
if current_status.status == 'suspended':
|
|
250
|
-
# Send the resume request
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
response = self._get_connection.resume(
|
|
256
|
-
payload,
|
|
257
|
-
metadata=_get_grpc_header(cluster=self.cluster_uuid)
|
|
258
|
-
)
|
|
371
|
+
# Send the resume request with strategy header
|
|
372
|
+
try:
|
|
373
|
+
response = self._try_cluster_request("resume")
|
|
374
|
+
except _InactiveRpcError:
|
|
375
|
+
return False
|
|
259
376
|
elif current_status.status == 'active':
|
|
260
377
|
return True
|
|
261
378
|
elif current_status.status != 'resuming':
|