e6data-python-connector 2.3.10rc8__tar.gz → 2.3.11__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.3.10rc8/e6data_python_connector.egg-info → e6data_python_connector-2.3.11}/PKG-INFO +259 -10
- {e6data_python_connector-2.3.10rc8 → e6data_python_connector-2.3.11}/README.md +250 -2
- e6data_python_connector-2.3.11/e6data_python_connector/__init__.py +4 -0
- {e6data_python_connector-2.3.10rc8 → e6data_python_connector-2.3.11}/e6data_python_connector/cluster_manager.py +93 -9
- e6data_python_connector-2.3.11/e6data_python_connector/connection_pool.py +495 -0
- {e6data_python_connector-2.3.10rc8 → e6data_python_connector-2.3.11}/e6data_python_connector/datainputstream.py +31 -19
- {e6data_python_connector-2.3.10rc8 → e6data_python_connector-2.3.11}/e6data_python_connector/e6data_grpc.py +87 -26
- {e6data_python_connector-2.3.10rc8 → e6data_python_connector-2.3.11}/e6data_python_connector/e6x_vector/ttypes.py +26 -2
- {e6data_python_connector-2.3.10rc8 → e6data_python_connector-2.3.11/e6data_python_connector.egg-info}/PKG-INFO +259 -10
- {e6data_python_connector-2.3.10rc8 → e6data_python_connector-2.3.11}/e6data_python_connector.egg-info/SOURCES.txt +10 -0
- e6data_python_connector-2.3.11/e6data_python_connector.egg-info/requires.txt +8 -0
- {e6data_python_connector-2.3.10rc8 → e6data_python_connector-2.3.11}/e6data_python_connector.egg-info/top_level.txt +1 -0
- e6data_python_connector-2.3.11/gen-py/__init__.py +0 -0
- e6data_python_connector-2.3.11/gen-py/e6x_vector/__init__.py +1 -0
- e6data_python_connector-2.3.11/gen-py/e6x_vector/constants.py +15 -0
- e6data_python_connector-2.3.11/gen-py/e6x_vector/ttypes.py +1875 -0
- {e6data_python_connector-2.3.10rc8 → e6data_python_connector-2.3.11}/setup.py +9 -8
- e6data_python_connector-2.3.11/test/test_connection_pool.py +410 -0
- e6data_python_connector-2.3.11/test/test_connection_pool_e2e.py +564 -0
- e6data_python_connector-2.3.11/test/test_pool_concurrency_simple.py +573 -0
- e6data_python_connector-2.3.11/test/test_pool_threading_multiprocessing.py +679 -0
- e6data_python_connector-2.3.11/test/test_simple_connection_pool.py +278 -0
- e6data_python_connector-2.3.10rc8/e6data_python_connector/__init__.py +0 -3
- e6data_python_connector-2.3.10rc8/e6data_python_connector.egg-info/requires.txt +0 -8
- {e6data_python_connector-2.3.10rc8 → e6data_python_connector-2.3.11}/LICENSE +0 -0
- {e6data_python_connector-2.3.10rc8 → e6data_python_connector-2.3.11}/MANIFEST.in +0 -0
- {e6data_python_connector-2.3.10rc8 → e6data_python_connector-2.3.11}/e6data_python_connector/cluster_server/__init__.py +0 -0
- {e6data_python_connector-2.3.10rc8 → e6data_python_connector-2.3.11}/e6data_python_connector/cluster_server/cluster_pb2.py +0 -0
- {e6data_python_connector-2.3.10rc8 → e6data_python_connector-2.3.11}/e6data_python_connector/cluster_server/cluster_pb2_grpc.py +0 -0
- {e6data_python_connector-2.3.10rc8 → e6data_python_connector-2.3.11}/e6data_python_connector/common.py +0 -0
- {e6data_python_connector-2.3.10rc8 → e6data_python_connector-2.3.11}/e6data_python_connector/constants.py +0 -0
- {e6data_python_connector-2.3.10rc8 → e6data_python_connector-2.3.11}/e6data_python_connector/date_time_utils.py +0 -0
- {e6data_python_connector-2.3.10rc8 → e6data_python_connector-2.3.11}/e6data_python_connector/dialect.py +0 -0
- {e6data_python_connector-2.3.10rc8 → e6data_python_connector-2.3.11}/e6data_python_connector/e6x_vector/__init__.py +0 -0
- {e6data_python_connector-2.3.10rc8 → e6data_python_connector-2.3.11}/e6data_python_connector/e6x_vector/constants.py +0 -0
- {e6data_python_connector-2.3.10rc8 → e6data_python_connector-2.3.11}/e6data_python_connector/exceptions.py +0 -0
- {e6data_python_connector-2.3.10rc8 → e6data_python_connector-2.3.11}/e6data_python_connector/server/QueryEngineService.py +0 -0
- {e6data_python_connector-2.3.10rc8 → e6data_python_connector-2.3.11}/e6data_python_connector/server/__init__.py +0 -0
- {e6data_python_connector-2.3.10rc8 → e6data_python_connector-2.3.11}/e6data_python_connector/server/constants.py +0 -0
- {e6data_python_connector-2.3.10rc8 → e6data_python_connector-2.3.11}/e6data_python_connector/server/e6x_engine_pb2.py +0 -0
- {e6data_python_connector-2.3.10rc8 → e6data_python_connector-2.3.11}/e6data_python_connector/server/e6x_engine_pb2.pyi +0 -0
- {e6data_python_connector-2.3.10rc8 → e6data_python_connector-2.3.11}/e6data_python_connector/server/e6x_engine_pb2_grpc.py +0 -0
- {e6data_python_connector-2.3.10rc8 → e6data_python_connector-2.3.11}/e6data_python_connector/server/ttypes.py +0 -0
- {e6data_python_connector-2.3.10rc8 → e6data_python_connector-2.3.11}/e6data_python_connector/strategy.py +0 -0
- {e6data_python_connector-2.3.10rc8 → e6data_python_connector-2.3.11}/e6data_python_connector/typeId.py +0 -0
- {e6data_python_connector-2.3.10rc8 → e6data_python_connector-2.3.11}/e6data_python_connector.egg-info/dependency_links.txt +0 -0
- {e6data_python_connector-2.3.10rc8 → e6data_python_connector-2.3.11}/e6data_python_connector.egg-info/entry_points.txt +0 -0
- {e6data_python_connector-2.3.10rc8 → e6data_python_connector-2.3.11}/pyproject.toml +0 -0
- {e6data_python_connector-2.3.10rc8 → e6data_python_connector-2.3.11}/setup.cfg +0 -0
- {e6data_python_connector-2.3.10rc8 → e6data_python_connector-2.3.11}/test/__init__.py +0 -0
- {e6data_python_connector-2.3.10rc8 → e6data_python_connector-2.3.11}/test/analyze_38_nines.py +0 -0
- {e6data_python_connector-2.3.10rc8 → e6data_python_connector-2.3.11}/test/analyze_all_cases.py +0 -0
- {e6data_python_connector-2.3.10rc8 → e6data_python_connector-2.3.11}/test/analyze_binary.py +0 -0
- {e6data_python_connector-2.3.10rc8 → e6data_python_connector-2.3.11}/test/analyze_correct_value.py +0 -0
- {e6data_python_connector-2.3.10rc8 → e6data_python_connector-2.3.11}/test/analyze_fields.py +0 -0
- {e6data_python_connector-2.3.10rc8 → e6data_python_connector-2.3.11}/test/check_decimal_errors.py +0 -0
- {e6data_python_connector-2.3.10rc8 → e6data_python_connector-2.3.11}/test/cleanup_test_files.py +0 -0
- {e6data_python_connector-2.3.10rc8 → e6data_python_connector-2.3.11}/test/debug_38_nines.py +0 -0
- {e6data_python_connector-2.3.10rc8 → e6data_python_connector-2.3.11}/test/debug_binary.py +0 -0
- {e6data_python_connector-2.3.10rc8 → e6data_python_connector-2.3.11}/test/final_test.py +0 -0
- {e6data_python_connector-2.3.10rc8 → e6data_python_connector-2.3.11}/test/move_tests.py +0 -0
- {e6data_python_connector-2.3.10rc8 → e6data_python_connector-2.3.11}/test/quick_test.py +0 -0
- {e6data_python_connector-2.3.10rc8 → e6data_python_connector-2.3.11}/test/test_38_nines.py +0 -0
- {e6data_python_connector-2.3.10rc8 → e6data_python_connector-2.3.11}/test/test_all_decimal128_cases.py +0 -0
- {e6data_python_connector-2.3.10rc8 → e6data_python_connector-2.3.11}/test/test_cluster_manager_efficiency.py +0 -0
- {e6data_python_connector-2.3.10rc8 → e6data_python_connector-2.3.11}/test/test_cluster_manager_none_strategy.py +0 -0
- {e6data_python_connector-2.3.10rc8 → e6data_python_connector-2.3.11}/test/test_cluster_manager_strategy.py +0 -0
- {e6data_python_connector-2.3.10rc8 → e6data_python_connector-2.3.11}/test/test_comprehensive.py +0 -0
- {e6data_python_connector-2.3.10rc8 → e6data_python_connector-2.3.11}/test/test_current_implementation.py +0 -0
- {e6data_python_connector-2.3.10rc8 → e6data_python_connector-2.3.11}/test/test_decimal128_binary_parsing.py +0 -0
- {e6data_python_connector-2.3.10rc8 → e6data_python_connector-2.3.11}/test/test_decimal128_parsing.py +0 -0
- {e6data_python_connector-2.3.10rc8 → e6data_python_connector-2.3.11}/test/test_fix.py +0 -0
- {e6data_python_connector-2.3.10rc8 → e6data_python_connector-2.3.11}/test/test_improved_parsing.py +0 -0
- {e6data_python_connector-2.3.10rc8 → e6data_python_connector-2.3.11}/test/test_known_case.py +0 -0
- {e6data_python_connector-2.3.10rc8 → e6data_python_connector-2.3.11}/test/test_manual_analysis.py +0 -0
- {e6data_python_connector-2.3.10rc8 → e6data_python_connector-2.3.11}/test/test_mock_server.py +0 -0
- {e6data_python_connector-2.3.10rc8 → e6data_python_connector-2.3.11}/test/test_multiprocessing_fix.py +0 -0
- {e6data_python_connector-2.3.10rc8 → e6data_python_connector-2.3.11}/test/test_new_implementation.py +0 -0
- {e6data_python_connector-2.3.10rc8 → e6data_python_connector-2.3.11}/test/test_specific_binary.py +0 -0
- {e6data_python_connector-2.3.10rc8 → e6data_python_connector-2.3.11}/test/test_strategy.py +0 -0
- {e6data_python_connector-2.3.10rc8 → e6data_python_connector-2.3.11}/test/test_strategy_logic.py +0 -0
- {e6data_python_connector-2.3.10rc8 → e6data_python_connector-2.3.11}/test/test_strategy_persistence_fix.py +0 -0
- {e6data_python_connector-2.3.10rc8 → e6data_python_connector-2.3.11}/test/test_strategy_sharing_fix.py +0 -0
- {e6data_python_connector-2.3.10rc8 → e6data_python_connector-2.3.11}/test/test_user_binary_value.py +0 -0
- {e6data_python_connector-2.3.10rc8 → e6data_python_connector-2.3.11}/test/tests.py +0 -0
- {e6data_python_connector-2.3.10rc8 → e6data_python_connector-2.3.11}/test/tests_grpc.py +0 -0
- {e6data_python_connector-2.3.10rc8 → e6data_python_connector-2.3.11}/test/validate_decimal128.py +0 -0
- {e6data_python_connector-2.3.10rc8 → e6data_python_connector-2.3.11}/test/validate_implementation.py +0 -0
- {e6data_python_connector-2.3.10rc8 → e6data_python_connector-2.3.11}/test/verify_decimal_fix.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.2
|
|
2
2
|
Name: e6data-python-connector
|
|
3
|
-
Version: 2.3.
|
|
3
|
+
Version: 2.3.11
|
|
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.
|
|
@@ -16,16 +16,17 @@ Classifier: Programming Language :: Python :: 3.9
|
|
|
16
16
|
Classifier: Programming Language :: Python :: 3.10
|
|
17
17
|
Classifier: Programming Language :: Python :: 3.11
|
|
18
18
|
Classifier: Programming Language :: Python :: 3.12
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
19
20
|
Description-Content-Type: text/markdown
|
|
20
21
|
License-File: LICENSE
|
|
21
22
|
Requires-Dist: sqlalchemy>=1.0.0
|
|
22
|
-
Requires-Dist: future
|
|
23
|
-
Requires-Dist: python-dateutil
|
|
24
|
-
Requires-Dist: pycryptodome
|
|
25
|
-
Requires-Dist: pytz
|
|
26
|
-
Requires-Dist: thrift
|
|
27
|
-
Requires-Dist: grpcio
|
|
28
|
-
Requires-Dist: grpcio-tools
|
|
23
|
+
Requires-Dist: future>=1.0.0
|
|
24
|
+
Requires-Dist: python-dateutil>=2.9.0
|
|
25
|
+
Requires-Dist: pycryptodome>=3.19.1
|
|
26
|
+
Requires-Dist: pytz>=2024.1
|
|
27
|
+
Requires-Dist: thrift>=0.20.0
|
|
28
|
+
Requires-Dist: grpcio>=1.65.1
|
|
29
|
+
Requires-Dist: grpcio-tools>=1.65.1
|
|
29
30
|
Dynamic: author
|
|
30
31
|
Dynamic: author-email
|
|
31
32
|
Dynamic: classifier
|
|
@@ -38,7 +39,7 @@ Dynamic: summary
|
|
|
38
39
|
|
|
39
40
|
# e6data Python Connector
|
|
40
41
|
|
|
41
|
-

|
|
42
43
|
|
|
43
44
|
## Introduction
|
|
44
45
|
|
|
@@ -75,6 +76,8 @@ Use your e6data Email ID as the username and your access token as the password.
|
|
|
75
76
|
|
|
76
77
|
```python
|
|
77
78
|
from e6data_python_connector import Connection
|
|
79
|
+
# For connection pooling (recommended for concurrent operations)
|
|
80
|
+
from e6data_python_connector import ConnectionPool
|
|
78
81
|
|
|
79
82
|
username = '<username>' # Your e6data Email ID.
|
|
80
83
|
password = '<password>' # Access Token generated in the e6data console.
|
|
@@ -84,6 +87,7 @@ database = '<database>' # Database to perform the query on.
|
|
|
84
87
|
port = 80 # Port of the e6data engine.
|
|
85
88
|
catalog_name = '<catalog_name>'
|
|
86
89
|
|
|
90
|
+
# Single connection (for simple, single-threaded use)
|
|
87
91
|
conn = Connection(
|
|
88
92
|
host=host,
|
|
89
93
|
port=port,
|
|
@@ -91,6 +95,17 @@ conn = Connection(
|
|
|
91
95
|
database=database,
|
|
92
96
|
password=password
|
|
93
97
|
)
|
|
98
|
+
|
|
99
|
+
# Or use connection pool (for concurrent/multi-threaded use)
|
|
100
|
+
pool = ConnectionPool(
|
|
101
|
+
min_size=2,
|
|
102
|
+
max_size=10,
|
|
103
|
+
host=host,
|
|
104
|
+
port=port,
|
|
105
|
+
username=username,
|
|
106
|
+
database=database,
|
|
107
|
+
password=password
|
|
108
|
+
)
|
|
94
109
|
```
|
|
95
110
|
|
|
96
111
|
#### Connection Parameters
|
|
@@ -109,6 +124,7 @@ The `Connection` class supports the following parameters:
|
|
|
109
124
|
| `secure` | bool | No | False | Enable SSL/TLS for secure connections |
|
|
110
125
|
| `auto_resume` | bool | No | True | Automatically resume cluster if suspended |
|
|
111
126
|
| `grpc_options` | dict | No | None | Additional gRPC configuration options |
|
|
127
|
+
| `debug` | bool | No | False | Enable debug logging for troubleshooting |
|
|
112
128
|
|
|
113
129
|
#### Secure Connection Example
|
|
114
130
|
|
|
@@ -439,8 +455,241 @@ For detailed migration instructions, see the [Migration Guide](docs/zero-downtim
|
|
|
439
455
|
- Message size optimization for large queries
|
|
440
456
|
|
|
441
457
|
### Connection Management
|
|
442
|
-
|
|
458
|
+
|
|
459
|
+
#### Connection Pooling
|
|
460
|
+
|
|
461
|
+
The e6data Python connector now includes a built-in connection pool for efficient connection management and reuse across multiple threads. The `ConnectionPool` class provides:
|
|
462
|
+
|
|
463
|
+
- **Thread-safe connection reuse**: Each thread automatically reuses its assigned connection
|
|
464
|
+
- **Automatic lifecycle management**: Handles connection creation, health checks, and cleanup
|
|
465
|
+
- **Overflow connections**: Creates temporary connections when pool is exhausted
|
|
466
|
+
- **Connection health monitoring**: Automatic detection and replacement of broken connections
|
|
467
|
+
- **Statistics tracking**: Monitor pool usage and performance
|
|
468
|
+
|
|
469
|
+
##### Basic Connection Pool Usage
|
|
470
|
+
|
|
471
|
+
```python
|
|
472
|
+
from e6data_python_connector import ConnectionPool
|
|
473
|
+
|
|
474
|
+
# Create a connection pool
|
|
475
|
+
pool = ConnectionPool(
|
|
476
|
+
min_size=2, # Minimum connections to maintain
|
|
477
|
+
max_size=10, # Maximum connections in pool
|
|
478
|
+
max_overflow=5, # Additional temporary connections allowed
|
|
479
|
+
timeout=30.0, # Timeout for getting connection (seconds)
|
|
480
|
+
recycle=3600, # Maximum age before recycling (seconds)
|
|
481
|
+
debug=False, # Enable debug logging
|
|
482
|
+
pre_ping=True, # Check connection health before use
|
|
483
|
+
# Connection parameters
|
|
484
|
+
host=host,
|
|
485
|
+
port=port,
|
|
486
|
+
username=username,
|
|
487
|
+
password=password,
|
|
488
|
+
database=database,
|
|
489
|
+
catalog=catalog_name,
|
|
490
|
+
cluster_name=cluster_name,
|
|
491
|
+
secure=True
|
|
492
|
+
)
|
|
493
|
+
|
|
494
|
+
# Get connection and execute query
|
|
495
|
+
conn = pool.get_connection()
|
|
496
|
+
cursor = conn.cursor()
|
|
497
|
+
cursor.execute("SELECT * FROM table")
|
|
498
|
+
results = cursor.fetchall()
|
|
499
|
+
|
|
500
|
+
# Return connection to pool (important!)
|
|
501
|
+
pool.return_connection(conn)
|
|
502
|
+
|
|
503
|
+
# Clean up when done
|
|
504
|
+
pool.close_all()
|
|
505
|
+
```
|
|
506
|
+
|
|
507
|
+
##### Using Context Manager (Recommended)
|
|
508
|
+
|
|
509
|
+
The context manager pattern ensures connections are automatically returned to the pool:
|
|
510
|
+
|
|
511
|
+
```python
|
|
512
|
+
from e6data_python_connector import ConnectionPool
|
|
513
|
+
|
|
514
|
+
pool = ConnectionPool(
|
|
515
|
+
min_size=2,
|
|
516
|
+
max_size=10,
|
|
517
|
+
host=host,
|
|
518
|
+
port=port,
|
|
519
|
+
username=username,
|
|
520
|
+
password=password,
|
|
521
|
+
database=database
|
|
522
|
+
)
|
|
523
|
+
|
|
524
|
+
# Connection automatically returned to pool after use
|
|
525
|
+
with pool.get_connection_context() as conn:
|
|
526
|
+
cursor = conn.cursor()
|
|
527
|
+
cursor.execute("SELECT * FROM table")
|
|
528
|
+
results = cursor.fetchall()
|
|
529
|
+
print(results)
|
|
530
|
+
```
|
|
531
|
+
|
|
532
|
+
##### Concurrent Query Execution
|
|
533
|
+
|
|
534
|
+
Connection pooling is especially beneficial for concurrent query execution:
|
|
535
|
+
|
|
536
|
+
```python
|
|
537
|
+
import concurrent.futures
|
|
538
|
+
from e6data_python_connector import ConnectionPool
|
|
539
|
+
|
|
540
|
+
def execute_query(pool, query_id, query):
|
|
541
|
+
"""Execute a query using a pooled connection."""
|
|
542
|
+
# Each thread will reuse its assigned connection
|
|
543
|
+
conn = pool.get_connection()
|
|
544
|
+
try:
|
|
545
|
+
cursor = conn.cursor()
|
|
546
|
+
cursor.execute(query)
|
|
547
|
+
results = cursor.fetchall()
|
|
548
|
+
return f"Query {query_id}: {len(results)} rows"
|
|
549
|
+
finally:
|
|
550
|
+
pool.return_connection(conn)
|
|
551
|
+
|
|
552
|
+
# Create pool
|
|
553
|
+
pool = ConnectionPool(
|
|
554
|
+
min_size=3,
|
|
555
|
+
max_size=10,
|
|
556
|
+
host=host,
|
|
557
|
+
port=port,
|
|
558
|
+
username=username,
|
|
559
|
+
password=password,
|
|
560
|
+
database=database
|
|
561
|
+
)
|
|
562
|
+
|
|
563
|
+
# Execute multiple queries concurrently
|
|
564
|
+
queries = [
|
|
565
|
+
"SELECT COUNT(*) FROM table1",
|
|
566
|
+
"SELECT AVG(value) FROM table2",
|
|
567
|
+
"SELECT MAX(date) FROM table3"
|
|
568
|
+
]
|
|
569
|
+
|
|
570
|
+
with concurrent.futures.ThreadPoolExecutor(max_workers=5) as executor:
|
|
571
|
+
futures = [
|
|
572
|
+
executor.submit(execute_query, pool, i, query)
|
|
573
|
+
for i, query in enumerate(queries)
|
|
574
|
+
]
|
|
575
|
+
|
|
576
|
+
for future in concurrent.futures.as_completed(futures):
|
|
577
|
+
print(future.result())
|
|
578
|
+
|
|
579
|
+
# Clean up
|
|
580
|
+
pool.close_all()
|
|
581
|
+
```
|
|
582
|
+
|
|
583
|
+
##### Connection Pool Configuration
|
|
584
|
+
|
|
585
|
+
| Parameter | Type | Default | Description |
|
|
586
|
+
|-----------|------|---------|-------------|
|
|
587
|
+
| `min_size` | int | 2 | Minimum number of connections to maintain |
|
|
588
|
+
| `max_size` | int | 10 | Maximum number of connections in pool |
|
|
589
|
+
| `max_overflow` | int | 5 | Additional temporary connections allowed |
|
|
590
|
+
| `timeout` | float | 30.0 | Timeout for getting connection (seconds) |
|
|
591
|
+
| `recycle` | int | 3600 | Maximum connection age before recycling (seconds) |
|
|
592
|
+
| `debug` | bool | False | Enable debug logging for pool operations |
|
|
593
|
+
| `pre_ping` | bool | True | Check connection health before returning from pool |
|
|
594
|
+
|
|
595
|
+
##### Monitoring Pool Statistics
|
|
596
|
+
|
|
597
|
+
```python
|
|
598
|
+
# Get pool statistics
|
|
599
|
+
stats = pool.get_statistics()
|
|
600
|
+
print(f"Active connections: {stats['active_connections']}")
|
|
601
|
+
print(f"Idle connections: {stats['idle_connections']}")
|
|
602
|
+
print(f"Total requests: {stats['total_requests']}")
|
|
603
|
+
print(f"Failed connections: {stats['failed_connections']}")
|
|
604
|
+
```
|
|
605
|
+
|
|
606
|
+
##### When to Use Connection Pooling
|
|
607
|
+
|
|
608
|
+
Connection pooling is recommended when:
|
|
609
|
+
- Executing multiple queries concurrently
|
|
610
|
+
- Building web applications or APIs
|
|
611
|
+
- Running batch processing jobs
|
|
612
|
+
- Reducing connection overhead
|
|
613
|
+
- Improving application performance
|
|
614
|
+
|
|
615
|
+
##### Direct Connection Usage (Without Pool)
|
|
616
|
+
|
|
617
|
+
For simple, single-threaded applications, you can still use direct connections:
|
|
618
|
+
|
|
619
|
+
```python
|
|
620
|
+
from e6data_python_connector import Connection
|
|
621
|
+
|
|
622
|
+
conn = Connection(
|
|
623
|
+
host=host,
|
|
624
|
+
port=port,
|
|
625
|
+
username=username,
|
|
626
|
+
password=password,
|
|
627
|
+
database=database
|
|
628
|
+
)
|
|
629
|
+
|
|
630
|
+
cursor = conn.cursor()
|
|
631
|
+
cursor.execute("SELECT * FROM table")
|
|
632
|
+
results = cursor.fetchall()
|
|
633
|
+
conn.close()
|
|
634
|
+
```
|
|
635
|
+
|
|
636
|
+
#### Additional Connection Management Features
|
|
443
637
|
- Automatic connection health monitoring
|
|
444
638
|
- Graceful connection recovery and retry logic
|
|
639
|
+
- Blue-green deployment support with automatic failover
|
|
640
|
+
|
|
641
|
+
## Debugging and Troubleshooting
|
|
642
|
+
|
|
643
|
+
### Enable Debug Mode
|
|
644
|
+
|
|
645
|
+
Enable comprehensive debugging to troubleshoot connection and query issues:
|
|
646
|
+
|
|
647
|
+
```python
|
|
648
|
+
from e6data_python_connector import Connection
|
|
649
|
+
|
|
650
|
+
conn = Connection(
|
|
651
|
+
host=host,
|
|
652
|
+
port=port,
|
|
653
|
+
username=username,
|
|
654
|
+
password=password,
|
|
655
|
+
database=database,
|
|
656
|
+
debug=True # Enable debug logging
|
|
657
|
+
)
|
|
658
|
+
```
|
|
659
|
+
|
|
660
|
+
When `debug=True`, the following features are enabled:
|
|
661
|
+
- Python logging at DEBUG level for all operations
|
|
662
|
+
- Blue-green strategy transition logging
|
|
663
|
+
- Connection lifecycle logging
|
|
664
|
+
- Query execution detailed logging
|
|
665
|
+
|
|
666
|
+
### gRPC Network Tracing
|
|
667
|
+
|
|
668
|
+
For low-level gRPC network debugging (HTTP/2 frames, TCP events), set environment variables **before** running your Python script:
|
|
669
|
+
|
|
670
|
+
```bash
|
|
671
|
+
# Enable gRPC network tracing
|
|
672
|
+
export GRPC_VERBOSITY=DEBUG
|
|
673
|
+
export GRPC_TRACE=client_channel,http2
|
|
674
|
+
|
|
675
|
+
# For comprehensive tracing
|
|
676
|
+
export GRPC_TRACE=api,call_error,channel,client_channel,connectivity_state,http,http2_stream,tcp,transport_security
|
|
677
|
+
|
|
678
|
+
# Run your script
|
|
679
|
+
python your_script.py
|
|
680
|
+
```
|
|
681
|
+
|
|
682
|
+
**Note**: These environment variables must be set before Python starts, as the gRPC C++ core reads them at module import time.
|
|
683
|
+
|
|
684
|
+
### Common Issues and Solutions
|
|
685
|
+
|
|
686
|
+
| Issue | Solution |
|
|
687
|
+
|-------|----------|
|
|
688
|
+
| Connection timeout | Check network connectivity, firewall rules, and ensure port 80/443 is open |
|
|
689
|
+
| Authentication failure | Verify username (email) and access token are correct |
|
|
690
|
+
| 503 Service Unavailable | Cluster may be suspended; enable `auto_resume=True` |
|
|
691
|
+
| 456 Strategy Error | Automatic blue-green failover will handle this |
|
|
692
|
+
| Memory issues with large results | Use `fetchall_buffer()` instead of `fetchall()` |
|
|
693
|
+
| gRPC message size errors | Configure `grpc_options` with appropriate message size limits |
|
|
445
694
|
|
|
446
695
|
See [TECH_DOC.md](TECH_DOC.md) for detailed technical documentation.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# e6data Python Connector
|
|
2
2
|
|
|
3
|
-

|
|
4
4
|
|
|
5
5
|
## Introduction
|
|
6
6
|
|
|
@@ -37,6 +37,8 @@ Use your e6data Email ID as the username and your access token as the password.
|
|
|
37
37
|
|
|
38
38
|
```python
|
|
39
39
|
from e6data_python_connector import Connection
|
|
40
|
+
# For connection pooling (recommended for concurrent operations)
|
|
41
|
+
from e6data_python_connector import ConnectionPool
|
|
40
42
|
|
|
41
43
|
username = '<username>' # Your e6data Email ID.
|
|
42
44
|
password = '<password>' # Access Token generated in the e6data console.
|
|
@@ -46,6 +48,7 @@ database = '<database>' # Database to perform the query on.
|
|
|
46
48
|
port = 80 # Port of the e6data engine.
|
|
47
49
|
catalog_name = '<catalog_name>'
|
|
48
50
|
|
|
51
|
+
# Single connection (for simple, single-threaded use)
|
|
49
52
|
conn = Connection(
|
|
50
53
|
host=host,
|
|
51
54
|
port=port,
|
|
@@ -53,6 +56,17 @@ conn = Connection(
|
|
|
53
56
|
database=database,
|
|
54
57
|
password=password
|
|
55
58
|
)
|
|
59
|
+
|
|
60
|
+
# Or use connection pool (for concurrent/multi-threaded use)
|
|
61
|
+
pool = ConnectionPool(
|
|
62
|
+
min_size=2,
|
|
63
|
+
max_size=10,
|
|
64
|
+
host=host,
|
|
65
|
+
port=port,
|
|
66
|
+
username=username,
|
|
67
|
+
database=database,
|
|
68
|
+
password=password
|
|
69
|
+
)
|
|
56
70
|
```
|
|
57
71
|
|
|
58
72
|
#### Connection Parameters
|
|
@@ -71,6 +85,7 @@ The `Connection` class supports the following parameters:
|
|
|
71
85
|
| `secure` | bool | No | False | Enable SSL/TLS for secure connections |
|
|
72
86
|
| `auto_resume` | bool | No | True | Automatically resume cluster if suspended |
|
|
73
87
|
| `grpc_options` | dict | No | None | Additional gRPC configuration options |
|
|
88
|
+
| `debug` | bool | No | False | Enable debug logging for troubleshooting |
|
|
74
89
|
|
|
75
90
|
#### Secure Connection Example
|
|
76
91
|
|
|
@@ -401,8 +416,241 @@ For detailed migration instructions, see the [Migration Guide](docs/zero-downtim
|
|
|
401
416
|
- Message size optimization for large queries
|
|
402
417
|
|
|
403
418
|
### Connection Management
|
|
404
|
-
|
|
419
|
+
|
|
420
|
+
#### Connection Pooling
|
|
421
|
+
|
|
422
|
+
The e6data Python connector now includes a built-in connection pool for efficient connection management and reuse across multiple threads. The `ConnectionPool` class provides:
|
|
423
|
+
|
|
424
|
+
- **Thread-safe connection reuse**: Each thread automatically reuses its assigned connection
|
|
425
|
+
- **Automatic lifecycle management**: Handles connection creation, health checks, and cleanup
|
|
426
|
+
- **Overflow connections**: Creates temporary connections when pool is exhausted
|
|
427
|
+
- **Connection health monitoring**: Automatic detection and replacement of broken connections
|
|
428
|
+
- **Statistics tracking**: Monitor pool usage and performance
|
|
429
|
+
|
|
430
|
+
##### Basic Connection Pool Usage
|
|
431
|
+
|
|
432
|
+
```python
|
|
433
|
+
from e6data_python_connector import ConnectionPool
|
|
434
|
+
|
|
435
|
+
# Create a connection pool
|
|
436
|
+
pool = ConnectionPool(
|
|
437
|
+
min_size=2, # Minimum connections to maintain
|
|
438
|
+
max_size=10, # Maximum connections in pool
|
|
439
|
+
max_overflow=5, # Additional temporary connections allowed
|
|
440
|
+
timeout=30.0, # Timeout for getting connection (seconds)
|
|
441
|
+
recycle=3600, # Maximum age before recycling (seconds)
|
|
442
|
+
debug=False, # Enable debug logging
|
|
443
|
+
pre_ping=True, # Check connection health before use
|
|
444
|
+
# Connection parameters
|
|
445
|
+
host=host,
|
|
446
|
+
port=port,
|
|
447
|
+
username=username,
|
|
448
|
+
password=password,
|
|
449
|
+
database=database,
|
|
450
|
+
catalog=catalog_name,
|
|
451
|
+
cluster_name=cluster_name,
|
|
452
|
+
secure=True
|
|
453
|
+
)
|
|
454
|
+
|
|
455
|
+
# Get connection and execute query
|
|
456
|
+
conn = pool.get_connection()
|
|
457
|
+
cursor = conn.cursor()
|
|
458
|
+
cursor.execute("SELECT * FROM table")
|
|
459
|
+
results = cursor.fetchall()
|
|
460
|
+
|
|
461
|
+
# Return connection to pool (important!)
|
|
462
|
+
pool.return_connection(conn)
|
|
463
|
+
|
|
464
|
+
# Clean up when done
|
|
465
|
+
pool.close_all()
|
|
466
|
+
```
|
|
467
|
+
|
|
468
|
+
##### Using Context Manager (Recommended)
|
|
469
|
+
|
|
470
|
+
The context manager pattern ensures connections are automatically returned to the pool:
|
|
471
|
+
|
|
472
|
+
```python
|
|
473
|
+
from e6data_python_connector import ConnectionPool
|
|
474
|
+
|
|
475
|
+
pool = ConnectionPool(
|
|
476
|
+
min_size=2,
|
|
477
|
+
max_size=10,
|
|
478
|
+
host=host,
|
|
479
|
+
port=port,
|
|
480
|
+
username=username,
|
|
481
|
+
password=password,
|
|
482
|
+
database=database
|
|
483
|
+
)
|
|
484
|
+
|
|
485
|
+
# Connection automatically returned to pool after use
|
|
486
|
+
with pool.get_connection_context() as conn:
|
|
487
|
+
cursor = conn.cursor()
|
|
488
|
+
cursor.execute("SELECT * FROM table")
|
|
489
|
+
results = cursor.fetchall()
|
|
490
|
+
print(results)
|
|
491
|
+
```
|
|
492
|
+
|
|
493
|
+
##### Concurrent Query Execution
|
|
494
|
+
|
|
495
|
+
Connection pooling is especially beneficial for concurrent query execution:
|
|
496
|
+
|
|
497
|
+
```python
|
|
498
|
+
import concurrent.futures
|
|
499
|
+
from e6data_python_connector import ConnectionPool
|
|
500
|
+
|
|
501
|
+
def execute_query(pool, query_id, query):
|
|
502
|
+
"""Execute a query using a pooled connection."""
|
|
503
|
+
# Each thread will reuse its assigned connection
|
|
504
|
+
conn = pool.get_connection()
|
|
505
|
+
try:
|
|
506
|
+
cursor = conn.cursor()
|
|
507
|
+
cursor.execute(query)
|
|
508
|
+
results = cursor.fetchall()
|
|
509
|
+
return f"Query {query_id}: {len(results)} rows"
|
|
510
|
+
finally:
|
|
511
|
+
pool.return_connection(conn)
|
|
512
|
+
|
|
513
|
+
# Create pool
|
|
514
|
+
pool = ConnectionPool(
|
|
515
|
+
min_size=3,
|
|
516
|
+
max_size=10,
|
|
517
|
+
host=host,
|
|
518
|
+
port=port,
|
|
519
|
+
username=username,
|
|
520
|
+
password=password,
|
|
521
|
+
database=database
|
|
522
|
+
)
|
|
523
|
+
|
|
524
|
+
# Execute multiple queries concurrently
|
|
525
|
+
queries = [
|
|
526
|
+
"SELECT COUNT(*) FROM table1",
|
|
527
|
+
"SELECT AVG(value) FROM table2",
|
|
528
|
+
"SELECT MAX(date) FROM table3"
|
|
529
|
+
]
|
|
530
|
+
|
|
531
|
+
with concurrent.futures.ThreadPoolExecutor(max_workers=5) as executor:
|
|
532
|
+
futures = [
|
|
533
|
+
executor.submit(execute_query, pool, i, query)
|
|
534
|
+
for i, query in enumerate(queries)
|
|
535
|
+
]
|
|
536
|
+
|
|
537
|
+
for future in concurrent.futures.as_completed(futures):
|
|
538
|
+
print(future.result())
|
|
539
|
+
|
|
540
|
+
# Clean up
|
|
541
|
+
pool.close_all()
|
|
542
|
+
```
|
|
543
|
+
|
|
544
|
+
##### Connection Pool Configuration
|
|
545
|
+
|
|
546
|
+
| Parameter | Type | Default | Description |
|
|
547
|
+
|-----------|------|---------|-------------|
|
|
548
|
+
| `min_size` | int | 2 | Minimum number of connections to maintain |
|
|
549
|
+
| `max_size` | int | 10 | Maximum number of connections in pool |
|
|
550
|
+
| `max_overflow` | int | 5 | Additional temporary connections allowed |
|
|
551
|
+
| `timeout` | float | 30.0 | Timeout for getting connection (seconds) |
|
|
552
|
+
| `recycle` | int | 3600 | Maximum connection age before recycling (seconds) |
|
|
553
|
+
| `debug` | bool | False | Enable debug logging for pool operations |
|
|
554
|
+
| `pre_ping` | bool | True | Check connection health before returning from pool |
|
|
555
|
+
|
|
556
|
+
##### Monitoring Pool Statistics
|
|
557
|
+
|
|
558
|
+
```python
|
|
559
|
+
# Get pool statistics
|
|
560
|
+
stats = pool.get_statistics()
|
|
561
|
+
print(f"Active connections: {stats['active_connections']}")
|
|
562
|
+
print(f"Idle connections: {stats['idle_connections']}")
|
|
563
|
+
print(f"Total requests: {stats['total_requests']}")
|
|
564
|
+
print(f"Failed connections: {stats['failed_connections']}")
|
|
565
|
+
```
|
|
566
|
+
|
|
567
|
+
##### When to Use Connection Pooling
|
|
568
|
+
|
|
569
|
+
Connection pooling is recommended when:
|
|
570
|
+
- Executing multiple queries concurrently
|
|
571
|
+
- Building web applications or APIs
|
|
572
|
+
- Running batch processing jobs
|
|
573
|
+
- Reducing connection overhead
|
|
574
|
+
- Improving application performance
|
|
575
|
+
|
|
576
|
+
##### Direct Connection Usage (Without Pool)
|
|
577
|
+
|
|
578
|
+
For simple, single-threaded applications, you can still use direct connections:
|
|
579
|
+
|
|
580
|
+
```python
|
|
581
|
+
from e6data_python_connector import Connection
|
|
582
|
+
|
|
583
|
+
conn = Connection(
|
|
584
|
+
host=host,
|
|
585
|
+
port=port,
|
|
586
|
+
username=username,
|
|
587
|
+
password=password,
|
|
588
|
+
database=database
|
|
589
|
+
)
|
|
590
|
+
|
|
591
|
+
cursor = conn.cursor()
|
|
592
|
+
cursor.execute("SELECT * FROM table")
|
|
593
|
+
results = cursor.fetchall()
|
|
594
|
+
conn.close()
|
|
595
|
+
```
|
|
596
|
+
|
|
597
|
+
#### Additional Connection Management Features
|
|
405
598
|
- Automatic connection health monitoring
|
|
406
599
|
- Graceful connection recovery and retry logic
|
|
600
|
+
- Blue-green deployment support with automatic failover
|
|
601
|
+
|
|
602
|
+
## Debugging and Troubleshooting
|
|
603
|
+
|
|
604
|
+
### Enable Debug Mode
|
|
605
|
+
|
|
606
|
+
Enable comprehensive debugging to troubleshoot connection and query issues:
|
|
607
|
+
|
|
608
|
+
```python
|
|
609
|
+
from e6data_python_connector import Connection
|
|
610
|
+
|
|
611
|
+
conn = Connection(
|
|
612
|
+
host=host,
|
|
613
|
+
port=port,
|
|
614
|
+
username=username,
|
|
615
|
+
password=password,
|
|
616
|
+
database=database,
|
|
617
|
+
debug=True # Enable debug logging
|
|
618
|
+
)
|
|
619
|
+
```
|
|
620
|
+
|
|
621
|
+
When `debug=True`, the following features are enabled:
|
|
622
|
+
- Python logging at DEBUG level for all operations
|
|
623
|
+
- Blue-green strategy transition logging
|
|
624
|
+
- Connection lifecycle logging
|
|
625
|
+
- Query execution detailed logging
|
|
626
|
+
|
|
627
|
+
### gRPC Network Tracing
|
|
628
|
+
|
|
629
|
+
For low-level gRPC network debugging (HTTP/2 frames, TCP events), set environment variables **before** running your Python script:
|
|
630
|
+
|
|
631
|
+
```bash
|
|
632
|
+
# Enable gRPC network tracing
|
|
633
|
+
export GRPC_VERBOSITY=DEBUG
|
|
634
|
+
export GRPC_TRACE=client_channel,http2
|
|
635
|
+
|
|
636
|
+
# For comprehensive tracing
|
|
637
|
+
export GRPC_TRACE=api,call_error,channel,client_channel,connectivity_state,http,http2_stream,tcp,transport_security
|
|
638
|
+
|
|
639
|
+
# Run your script
|
|
640
|
+
python your_script.py
|
|
641
|
+
```
|
|
642
|
+
|
|
643
|
+
**Note**: These environment variables must be set before Python starts, as the gRPC C++ core reads them at module import time.
|
|
644
|
+
|
|
645
|
+
### Common Issues and Solutions
|
|
646
|
+
|
|
647
|
+
| Issue | Solution |
|
|
648
|
+
|-------|----------|
|
|
649
|
+
| Connection timeout | Check network connectivity, firewall rules, and ensure port 80/443 is open |
|
|
650
|
+
| Authentication failure | Verify username (email) and access token are correct |
|
|
651
|
+
| 503 Service Unavailable | Cluster may be suspended; enable `auto_resume=True` |
|
|
652
|
+
| 456 Strategy Error | Automatic blue-green failover will handle this |
|
|
653
|
+
| Memory issues with large results | Use `fetchall_buffer()` instead of `fetchall()` |
|
|
654
|
+
| gRPC message size errors | Configure `grpc_options` with appropriate message size limits |
|
|
407
655
|
|
|
408
656
|
See [TECH_DOC.md](TECH_DOC.md) for detailed technical documentation.
|