e6data-python-connector 2.3.10rc8__tar.gz → 2.3.11rc1__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.11rc1}/PKG-INFO +204 -10
- {e6data_python_connector-2.3.10rc8 → e6data_python_connector-2.3.11rc1}/README.md +195 -2
- e6data_python_connector-2.3.11rc1/e6data_python_connector/__init__.py +4 -0
- e6data_python_connector-2.3.11rc1/e6data_python_connector/connection_pool.py +495 -0
- {e6data_python_connector-2.3.10rc8 → e6data_python_connector-2.3.11rc1}/e6data_python_connector/datainputstream.py +31 -19
- {e6data_python_connector-2.3.10rc8 → e6data_python_connector-2.3.11rc1}/e6data_python_connector/e6data_grpc.py +30 -2
- {e6data_python_connector-2.3.10rc8 → e6data_python_connector-2.3.11rc1}/e6data_python_connector/e6x_vector/ttypes.py +26 -2
- {e6data_python_connector-2.3.10rc8 → e6data_python_connector-2.3.11rc1/e6data_python_connector.egg-info}/PKG-INFO +204 -10
- {e6data_python_connector-2.3.10rc8 → e6data_python_connector-2.3.11rc1}/e6data_python_connector.egg-info/SOURCES.txt +10 -0
- e6data_python_connector-2.3.11rc1/e6data_python_connector.egg-info/requires.txt +8 -0
- {e6data_python_connector-2.3.10rc8 → e6data_python_connector-2.3.11rc1}/e6data_python_connector.egg-info/top_level.txt +1 -0
- e6data_python_connector-2.3.11rc1/gen-py/__init__.py +0 -0
- e6data_python_connector-2.3.11rc1/gen-py/e6x_vector/__init__.py +1 -0
- e6data_python_connector-2.3.11rc1/gen-py/e6x_vector/constants.py +15 -0
- e6data_python_connector-2.3.11rc1/gen-py/e6x_vector/ttypes.py +1875 -0
- {e6data_python_connector-2.3.10rc8 → e6data_python_connector-2.3.11rc1}/setup.py +9 -8
- e6data_python_connector-2.3.11rc1/test/test_connection_pool.py +410 -0
- e6data_python_connector-2.3.11rc1/test/test_connection_pool_e2e.py +564 -0
- e6data_python_connector-2.3.11rc1/test/test_pool_concurrency_simple.py +573 -0
- e6data_python_connector-2.3.11rc1/test/test_pool_threading_multiprocessing.py +679 -0
- e6data_python_connector-2.3.11rc1/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.11rc1}/LICENSE +0 -0
- {e6data_python_connector-2.3.10rc8 → e6data_python_connector-2.3.11rc1}/MANIFEST.in +0 -0
- {e6data_python_connector-2.3.10rc8 → e6data_python_connector-2.3.11rc1}/e6data_python_connector/cluster_manager.py +0 -0
- {e6data_python_connector-2.3.10rc8 → e6data_python_connector-2.3.11rc1}/e6data_python_connector/cluster_server/__init__.py +0 -0
- {e6data_python_connector-2.3.10rc8 → e6data_python_connector-2.3.11rc1}/e6data_python_connector/cluster_server/cluster_pb2.py +0 -0
- {e6data_python_connector-2.3.10rc8 → e6data_python_connector-2.3.11rc1}/e6data_python_connector/cluster_server/cluster_pb2_grpc.py +0 -0
- {e6data_python_connector-2.3.10rc8 → e6data_python_connector-2.3.11rc1}/e6data_python_connector/common.py +0 -0
- {e6data_python_connector-2.3.10rc8 → e6data_python_connector-2.3.11rc1}/e6data_python_connector/constants.py +0 -0
- {e6data_python_connector-2.3.10rc8 → e6data_python_connector-2.3.11rc1}/e6data_python_connector/date_time_utils.py +0 -0
- {e6data_python_connector-2.3.10rc8 → e6data_python_connector-2.3.11rc1}/e6data_python_connector/dialect.py +0 -0
- {e6data_python_connector-2.3.10rc8 → e6data_python_connector-2.3.11rc1}/e6data_python_connector/e6x_vector/__init__.py +0 -0
- {e6data_python_connector-2.3.10rc8 → e6data_python_connector-2.3.11rc1}/e6data_python_connector/e6x_vector/constants.py +0 -0
- {e6data_python_connector-2.3.10rc8 → e6data_python_connector-2.3.11rc1}/e6data_python_connector/exceptions.py +0 -0
- {e6data_python_connector-2.3.10rc8 → e6data_python_connector-2.3.11rc1}/e6data_python_connector/server/QueryEngineService.py +0 -0
- {e6data_python_connector-2.3.10rc8 → e6data_python_connector-2.3.11rc1}/e6data_python_connector/server/__init__.py +0 -0
- {e6data_python_connector-2.3.10rc8 → e6data_python_connector-2.3.11rc1}/e6data_python_connector/server/constants.py +0 -0
- {e6data_python_connector-2.3.10rc8 → e6data_python_connector-2.3.11rc1}/e6data_python_connector/server/e6x_engine_pb2.py +0 -0
- {e6data_python_connector-2.3.10rc8 → e6data_python_connector-2.3.11rc1}/e6data_python_connector/server/e6x_engine_pb2.pyi +0 -0
- {e6data_python_connector-2.3.10rc8 → e6data_python_connector-2.3.11rc1}/e6data_python_connector/server/e6x_engine_pb2_grpc.py +0 -0
- {e6data_python_connector-2.3.10rc8 → e6data_python_connector-2.3.11rc1}/e6data_python_connector/server/ttypes.py +0 -0
- {e6data_python_connector-2.3.10rc8 → e6data_python_connector-2.3.11rc1}/e6data_python_connector/strategy.py +0 -0
- {e6data_python_connector-2.3.10rc8 → e6data_python_connector-2.3.11rc1}/e6data_python_connector/typeId.py +0 -0
- {e6data_python_connector-2.3.10rc8 → e6data_python_connector-2.3.11rc1}/e6data_python_connector.egg-info/dependency_links.txt +0 -0
- {e6data_python_connector-2.3.10rc8 → e6data_python_connector-2.3.11rc1}/e6data_python_connector.egg-info/entry_points.txt +0 -0
- {e6data_python_connector-2.3.10rc8 → e6data_python_connector-2.3.11rc1}/pyproject.toml +0 -0
- {e6data_python_connector-2.3.10rc8 → e6data_python_connector-2.3.11rc1}/setup.cfg +0 -0
- {e6data_python_connector-2.3.10rc8 → e6data_python_connector-2.3.11rc1}/test/__init__.py +0 -0
- {e6data_python_connector-2.3.10rc8 → e6data_python_connector-2.3.11rc1}/test/analyze_38_nines.py +0 -0
- {e6data_python_connector-2.3.10rc8 → e6data_python_connector-2.3.11rc1}/test/analyze_all_cases.py +0 -0
- {e6data_python_connector-2.3.10rc8 → e6data_python_connector-2.3.11rc1}/test/analyze_binary.py +0 -0
- {e6data_python_connector-2.3.10rc8 → e6data_python_connector-2.3.11rc1}/test/analyze_correct_value.py +0 -0
- {e6data_python_connector-2.3.10rc8 → e6data_python_connector-2.3.11rc1}/test/analyze_fields.py +0 -0
- {e6data_python_connector-2.3.10rc8 → e6data_python_connector-2.3.11rc1}/test/check_decimal_errors.py +0 -0
- {e6data_python_connector-2.3.10rc8 → e6data_python_connector-2.3.11rc1}/test/cleanup_test_files.py +0 -0
- {e6data_python_connector-2.3.10rc8 → e6data_python_connector-2.3.11rc1}/test/debug_38_nines.py +0 -0
- {e6data_python_connector-2.3.10rc8 → e6data_python_connector-2.3.11rc1}/test/debug_binary.py +0 -0
- {e6data_python_connector-2.3.10rc8 → e6data_python_connector-2.3.11rc1}/test/final_test.py +0 -0
- {e6data_python_connector-2.3.10rc8 → e6data_python_connector-2.3.11rc1}/test/move_tests.py +0 -0
- {e6data_python_connector-2.3.10rc8 → e6data_python_connector-2.3.11rc1}/test/quick_test.py +0 -0
- {e6data_python_connector-2.3.10rc8 → e6data_python_connector-2.3.11rc1}/test/test_38_nines.py +0 -0
- {e6data_python_connector-2.3.10rc8 → e6data_python_connector-2.3.11rc1}/test/test_all_decimal128_cases.py +0 -0
- {e6data_python_connector-2.3.10rc8 → e6data_python_connector-2.3.11rc1}/test/test_cluster_manager_efficiency.py +0 -0
- {e6data_python_connector-2.3.10rc8 → e6data_python_connector-2.3.11rc1}/test/test_cluster_manager_none_strategy.py +0 -0
- {e6data_python_connector-2.3.10rc8 → e6data_python_connector-2.3.11rc1}/test/test_cluster_manager_strategy.py +0 -0
- {e6data_python_connector-2.3.10rc8 → e6data_python_connector-2.3.11rc1}/test/test_comprehensive.py +0 -0
- {e6data_python_connector-2.3.10rc8 → e6data_python_connector-2.3.11rc1}/test/test_current_implementation.py +0 -0
- {e6data_python_connector-2.3.10rc8 → e6data_python_connector-2.3.11rc1}/test/test_decimal128_binary_parsing.py +0 -0
- {e6data_python_connector-2.3.10rc8 → e6data_python_connector-2.3.11rc1}/test/test_decimal128_parsing.py +0 -0
- {e6data_python_connector-2.3.10rc8 → e6data_python_connector-2.3.11rc1}/test/test_fix.py +0 -0
- {e6data_python_connector-2.3.10rc8 → e6data_python_connector-2.3.11rc1}/test/test_improved_parsing.py +0 -0
- {e6data_python_connector-2.3.10rc8 → e6data_python_connector-2.3.11rc1}/test/test_known_case.py +0 -0
- {e6data_python_connector-2.3.10rc8 → e6data_python_connector-2.3.11rc1}/test/test_manual_analysis.py +0 -0
- {e6data_python_connector-2.3.10rc8 → e6data_python_connector-2.3.11rc1}/test/test_mock_server.py +0 -0
- {e6data_python_connector-2.3.10rc8 → e6data_python_connector-2.3.11rc1}/test/test_multiprocessing_fix.py +0 -0
- {e6data_python_connector-2.3.10rc8 → e6data_python_connector-2.3.11rc1}/test/test_new_implementation.py +0 -0
- {e6data_python_connector-2.3.10rc8 → e6data_python_connector-2.3.11rc1}/test/test_specific_binary.py +0 -0
- {e6data_python_connector-2.3.10rc8 → e6data_python_connector-2.3.11rc1}/test/test_strategy.py +0 -0
- {e6data_python_connector-2.3.10rc8 → e6data_python_connector-2.3.11rc1}/test/test_strategy_logic.py +0 -0
- {e6data_python_connector-2.3.10rc8 → e6data_python_connector-2.3.11rc1}/test/test_strategy_persistence_fix.py +0 -0
- {e6data_python_connector-2.3.10rc8 → e6data_python_connector-2.3.11rc1}/test/test_strategy_sharing_fix.py +0 -0
- {e6data_python_connector-2.3.10rc8 → e6data_python_connector-2.3.11rc1}/test/test_user_binary_value.py +0 -0
- {e6data_python_connector-2.3.10rc8 → e6data_python_connector-2.3.11rc1}/test/tests.py +0 -0
- {e6data_python_connector-2.3.10rc8 → e6data_python_connector-2.3.11rc1}/test/tests_grpc.py +0 -0
- {e6data_python_connector-2.3.10rc8 → e6data_python_connector-2.3.11rc1}/test/validate_decimal128.py +0 -0
- {e6data_python_connector-2.3.10rc8 → e6data_python_connector-2.3.11rc1}/test/validate_implementation.py +0 -0
- {e6data_python_connector-2.3.10rc8 → e6data_python_connector-2.3.11rc1}/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.11rc1
|
|
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
|
|
@@ -439,8 +454,187 @@ For detailed migration instructions, see the [Migration Guide](docs/zero-downtim
|
|
|
439
454
|
- Message size optimization for large queries
|
|
440
455
|
|
|
441
456
|
### Connection Management
|
|
442
|
-
|
|
457
|
+
|
|
458
|
+
#### Connection Pooling
|
|
459
|
+
|
|
460
|
+
The e6data Python connector now includes a built-in connection pool for efficient connection management and reuse across multiple threads. The `ConnectionPool` class provides:
|
|
461
|
+
|
|
462
|
+
- **Thread-safe connection reuse**: Each thread automatically reuses its assigned connection
|
|
463
|
+
- **Automatic lifecycle management**: Handles connection creation, health checks, and cleanup
|
|
464
|
+
- **Overflow connections**: Creates temporary connections when pool is exhausted
|
|
465
|
+
- **Connection health monitoring**: Automatic detection and replacement of broken connections
|
|
466
|
+
- **Statistics tracking**: Monitor pool usage and performance
|
|
467
|
+
|
|
468
|
+
##### Basic Connection Pool Usage
|
|
469
|
+
|
|
470
|
+
```python
|
|
471
|
+
from e6data_python_connector import ConnectionPool
|
|
472
|
+
|
|
473
|
+
# Create a connection pool
|
|
474
|
+
pool = ConnectionPool(
|
|
475
|
+
min_size=2, # Minimum connections to maintain
|
|
476
|
+
max_size=10, # Maximum connections in pool
|
|
477
|
+
max_overflow=5, # Additional temporary connections allowed
|
|
478
|
+
timeout=30.0, # Timeout for getting connection (seconds)
|
|
479
|
+
recycle=3600, # Maximum age before recycling (seconds)
|
|
480
|
+
debug=False, # Enable debug logging
|
|
481
|
+
pre_ping=True, # Check connection health before use
|
|
482
|
+
# Connection parameters
|
|
483
|
+
host=host,
|
|
484
|
+
port=port,
|
|
485
|
+
username=username,
|
|
486
|
+
password=password,
|
|
487
|
+
database=database,
|
|
488
|
+
catalog=catalog_name,
|
|
489
|
+
cluster_name=cluster_name,
|
|
490
|
+
secure=True
|
|
491
|
+
)
|
|
492
|
+
|
|
493
|
+
# Get connection and execute query
|
|
494
|
+
conn = pool.get_connection()
|
|
495
|
+
cursor = conn.cursor()
|
|
496
|
+
cursor.execute("SELECT * FROM table")
|
|
497
|
+
results = cursor.fetchall()
|
|
498
|
+
|
|
499
|
+
# Return connection to pool (important!)
|
|
500
|
+
pool.return_connection(conn)
|
|
501
|
+
|
|
502
|
+
# Clean up when done
|
|
503
|
+
pool.close_all()
|
|
504
|
+
```
|
|
505
|
+
|
|
506
|
+
##### Using Context Manager (Recommended)
|
|
507
|
+
|
|
508
|
+
The context manager pattern ensures connections are automatically returned to the pool:
|
|
509
|
+
|
|
510
|
+
```python
|
|
511
|
+
from e6data_python_connector import ConnectionPool
|
|
512
|
+
|
|
513
|
+
pool = ConnectionPool(
|
|
514
|
+
min_size=2,
|
|
515
|
+
max_size=10,
|
|
516
|
+
host=host,
|
|
517
|
+
port=port,
|
|
518
|
+
username=username,
|
|
519
|
+
password=password,
|
|
520
|
+
database=database
|
|
521
|
+
)
|
|
522
|
+
|
|
523
|
+
# Connection automatically returned to pool after use
|
|
524
|
+
with pool.get_connection_context() as conn:
|
|
525
|
+
cursor = conn.cursor()
|
|
526
|
+
cursor.execute("SELECT * FROM table")
|
|
527
|
+
results = cursor.fetchall()
|
|
528
|
+
print(results)
|
|
529
|
+
```
|
|
530
|
+
|
|
531
|
+
##### Concurrent Query Execution
|
|
532
|
+
|
|
533
|
+
Connection pooling is especially beneficial for concurrent query execution:
|
|
534
|
+
|
|
535
|
+
```python
|
|
536
|
+
import concurrent.futures
|
|
537
|
+
from e6data_python_connector import ConnectionPool
|
|
538
|
+
|
|
539
|
+
def execute_query(pool, query_id, query):
|
|
540
|
+
"""Execute a query using a pooled connection."""
|
|
541
|
+
# Each thread will reuse its assigned connection
|
|
542
|
+
conn = pool.get_connection()
|
|
543
|
+
try:
|
|
544
|
+
cursor = conn.cursor()
|
|
545
|
+
cursor.execute(query)
|
|
546
|
+
results = cursor.fetchall()
|
|
547
|
+
return f"Query {query_id}: {len(results)} rows"
|
|
548
|
+
finally:
|
|
549
|
+
pool.return_connection(conn)
|
|
550
|
+
|
|
551
|
+
# Create pool
|
|
552
|
+
pool = ConnectionPool(
|
|
553
|
+
min_size=3,
|
|
554
|
+
max_size=10,
|
|
555
|
+
host=host,
|
|
556
|
+
port=port,
|
|
557
|
+
username=username,
|
|
558
|
+
password=password,
|
|
559
|
+
database=database
|
|
560
|
+
)
|
|
561
|
+
|
|
562
|
+
# Execute multiple queries concurrently
|
|
563
|
+
queries = [
|
|
564
|
+
"SELECT COUNT(*) FROM table1",
|
|
565
|
+
"SELECT AVG(value) FROM table2",
|
|
566
|
+
"SELECT MAX(date) FROM table3"
|
|
567
|
+
]
|
|
568
|
+
|
|
569
|
+
with concurrent.futures.ThreadPoolExecutor(max_workers=5) as executor:
|
|
570
|
+
futures = [
|
|
571
|
+
executor.submit(execute_query, pool, i, query)
|
|
572
|
+
for i, query in enumerate(queries)
|
|
573
|
+
]
|
|
574
|
+
|
|
575
|
+
for future in concurrent.futures.as_completed(futures):
|
|
576
|
+
print(future.result())
|
|
577
|
+
|
|
578
|
+
# Clean up
|
|
579
|
+
pool.close_all()
|
|
580
|
+
```
|
|
581
|
+
|
|
582
|
+
##### Connection Pool Configuration
|
|
583
|
+
|
|
584
|
+
| Parameter | Type | Default | Description |
|
|
585
|
+
|-----------|------|---------|-------------|
|
|
586
|
+
| `min_size` | int | 2 | Minimum number of connections to maintain |
|
|
587
|
+
| `max_size` | int | 10 | Maximum number of connections in pool |
|
|
588
|
+
| `max_overflow` | int | 5 | Additional temporary connections allowed |
|
|
589
|
+
| `timeout` | float | 30.0 | Timeout for getting connection (seconds) |
|
|
590
|
+
| `recycle` | int | 3600 | Maximum connection age before recycling (seconds) |
|
|
591
|
+
| `debug` | bool | False | Enable debug logging for pool operations |
|
|
592
|
+
| `pre_ping` | bool | True | Check connection health before returning from pool |
|
|
593
|
+
|
|
594
|
+
##### Monitoring Pool Statistics
|
|
595
|
+
|
|
596
|
+
```python
|
|
597
|
+
# Get pool statistics
|
|
598
|
+
stats = pool.get_statistics()
|
|
599
|
+
print(f"Active connections: {stats['active_connections']}")
|
|
600
|
+
print(f"Idle connections: {stats['idle_connections']}")
|
|
601
|
+
print(f"Total requests: {stats['total_requests']}")
|
|
602
|
+
print(f"Failed connections: {stats['failed_connections']}")
|
|
603
|
+
```
|
|
604
|
+
|
|
605
|
+
##### When to Use Connection Pooling
|
|
606
|
+
|
|
607
|
+
Connection pooling is recommended when:
|
|
608
|
+
- Executing multiple queries concurrently
|
|
609
|
+
- Building web applications or APIs
|
|
610
|
+
- Running batch processing jobs
|
|
611
|
+
- Reducing connection overhead
|
|
612
|
+
- Improving application performance
|
|
613
|
+
|
|
614
|
+
##### Direct Connection Usage (Without Pool)
|
|
615
|
+
|
|
616
|
+
For simple, single-threaded applications, you can still use direct connections:
|
|
617
|
+
|
|
618
|
+
```python
|
|
619
|
+
from e6data_python_connector import Connection
|
|
620
|
+
|
|
621
|
+
conn = Connection(
|
|
622
|
+
host=host,
|
|
623
|
+
port=port,
|
|
624
|
+
username=username,
|
|
625
|
+
password=password,
|
|
626
|
+
database=database
|
|
627
|
+
)
|
|
628
|
+
|
|
629
|
+
cursor = conn.cursor()
|
|
630
|
+
cursor.execute("SELECT * FROM table")
|
|
631
|
+
results = cursor.fetchall()
|
|
632
|
+
conn.close()
|
|
633
|
+
```
|
|
634
|
+
|
|
635
|
+
#### Additional Connection Management Features
|
|
443
636
|
- Automatic connection health monitoring
|
|
444
637
|
- Graceful connection recovery and retry logic
|
|
638
|
+
- Blue-green deployment support with automatic failover
|
|
445
639
|
|
|
446
640
|
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
|
|
@@ -401,8 +415,187 @@ For detailed migration instructions, see the [Migration Guide](docs/zero-downtim
|
|
|
401
415
|
- Message size optimization for large queries
|
|
402
416
|
|
|
403
417
|
### Connection Management
|
|
404
|
-
|
|
418
|
+
|
|
419
|
+
#### Connection Pooling
|
|
420
|
+
|
|
421
|
+
The e6data Python connector now includes a built-in connection pool for efficient connection management and reuse across multiple threads. The `ConnectionPool` class provides:
|
|
422
|
+
|
|
423
|
+
- **Thread-safe connection reuse**: Each thread automatically reuses its assigned connection
|
|
424
|
+
- **Automatic lifecycle management**: Handles connection creation, health checks, and cleanup
|
|
425
|
+
- **Overflow connections**: Creates temporary connections when pool is exhausted
|
|
426
|
+
- **Connection health monitoring**: Automatic detection and replacement of broken connections
|
|
427
|
+
- **Statistics tracking**: Monitor pool usage and performance
|
|
428
|
+
|
|
429
|
+
##### Basic Connection Pool Usage
|
|
430
|
+
|
|
431
|
+
```python
|
|
432
|
+
from e6data_python_connector import ConnectionPool
|
|
433
|
+
|
|
434
|
+
# Create a connection pool
|
|
435
|
+
pool = ConnectionPool(
|
|
436
|
+
min_size=2, # Minimum connections to maintain
|
|
437
|
+
max_size=10, # Maximum connections in pool
|
|
438
|
+
max_overflow=5, # Additional temporary connections allowed
|
|
439
|
+
timeout=30.0, # Timeout for getting connection (seconds)
|
|
440
|
+
recycle=3600, # Maximum age before recycling (seconds)
|
|
441
|
+
debug=False, # Enable debug logging
|
|
442
|
+
pre_ping=True, # Check connection health before use
|
|
443
|
+
# Connection parameters
|
|
444
|
+
host=host,
|
|
445
|
+
port=port,
|
|
446
|
+
username=username,
|
|
447
|
+
password=password,
|
|
448
|
+
database=database,
|
|
449
|
+
catalog=catalog_name,
|
|
450
|
+
cluster_name=cluster_name,
|
|
451
|
+
secure=True
|
|
452
|
+
)
|
|
453
|
+
|
|
454
|
+
# Get connection and execute query
|
|
455
|
+
conn = pool.get_connection()
|
|
456
|
+
cursor = conn.cursor()
|
|
457
|
+
cursor.execute("SELECT * FROM table")
|
|
458
|
+
results = cursor.fetchall()
|
|
459
|
+
|
|
460
|
+
# Return connection to pool (important!)
|
|
461
|
+
pool.return_connection(conn)
|
|
462
|
+
|
|
463
|
+
# Clean up when done
|
|
464
|
+
pool.close_all()
|
|
465
|
+
```
|
|
466
|
+
|
|
467
|
+
##### Using Context Manager (Recommended)
|
|
468
|
+
|
|
469
|
+
The context manager pattern ensures connections are automatically returned to the pool:
|
|
470
|
+
|
|
471
|
+
```python
|
|
472
|
+
from e6data_python_connector import ConnectionPool
|
|
473
|
+
|
|
474
|
+
pool = ConnectionPool(
|
|
475
|
+
min_size=2,
|
|
476
|
+
max_size=10,
|
|
477
|
+
host=host,
|
|
478
|
+
port=port,
|
|
479
|
+
username=username,
|
|
480
|
+
password=password,
|
|
481
|
+
database=database
|
|
482
|
+
)
|
|
483
|
+
|
|
484
|
+
# Connection automatically returned to pool after use
|
|
485
|
+
with pool.get_connection_context() as conn:
|
|
486
|
+
cursor = conn.cursor()
|
|
487
|
+
cursor.execute("SELECT * FROM table")
|
|
488
|
+
results = cursor.fetchall()
|
|
489
|
+
print(results)
|
|
490
|
+
```
|
|
491
|
+
|
|
492
|
+
##### Concurrent Query Execution
|
|
493
|
+
|
|
494
|
+
Connection pooling is especially beneficial for concurrent query execution:
|
|
495
|
+
|
|
496
|
+
```python
|
|
497
|
+
import concurrent.futures
|
|
498
|
+
from e6data_python_connector import ConnectionPool
|
|
499
|
+
|
|
500
|
+
def execute_query(pool, query_id, query):
|
|
501
|
+
"""Execute a query using a pooled connection."""
|
|
502
|
+
# Each thread will reuse its assigned connection
|
|
503
|
+
conn = pool.get_connection()
|
|
504
|
+
try:
|
|
505
|
+
cursor = conn.cursor()
|
|
506
|
+
cursor.execute(query)
|
|
507
|
+
results = cursor.fetchall()
|
|
508
|
+
return f"Query {query_id}: {len(results)} rows"
|
|
509
|
+
finally:
|
|
510
|
+
pool.return_connection(conn)
|
|
511
|
+
|
|
512
|
+
# Create pool
|
|
513
|
+
pool = ConnectionPool(
|
|
514
|
+
min_size=3,
|
|
515
|
+
max_size=10,
|
|
516
|
+
host=host,
|
|
517
|
+
port=port,
|
|
518
|
+
username=username,
|
|
519
|
+
password=password,
|
|
520
|
+
database=database
|
|
521
|
+
)
|
|
522
|
+
|
|
523
|
+
# Execute multiple queries concurrently
|
|
524
|
+
queries = [
|
|
525
|
+
"SELECT COUNT(*) FROM table1",
|
|
526
|
+
"SELECT AVG(value) FROM table2",
|
|
527
|
+
"SELECT MAX(date) FROM table3"
|
|
528
|
+
]
|
|
529
|
+
|
|
530
|
+
with concurrent.futures.ThreadPoolExecutor(max_workers=5) as executor:
|
|
531
|
+
futures = [
|
|
532
|
+
executor.submit(execute_query, pool, i, query)
|
|
533
|
+
for i, query in enumerate(queries)
|
|
534
|
+
]
|
|
535
|
+
|
|
536
|
+
for future in concurrent.futures.as_completed(futures):
|
|
537
|
+
print(future.result())
|
|
538
|
+
|
|
539
|
+
# Clean up
|
|
540
|
+
pool.close_all()
|
|
541
|
+
```
|
|
542
|
+
|
|
543
|
+
##### Connection Pool Configuration
|
|
544
|
+
|
|
545
|
+
| Parameter | Type | Default | Description |
|
|
546
|
+
|-----------|------|---------|-------------|
|
|
547
|
+
| `min_size` | int | 2 | Minimum number of connections to maintain |
|
|
548
|
+
| `max_size` | int | 10 | Maximum number of connections in pool |
|
|
549
|
+
| `max_overflow` | int | 5 | Additional temporary connections allowed |
|
|
550
|
+
| `timeout` | float | 30.0 | Timeout for getting connection (seconds) |
|
|
551
|
+
| `recycle` | int | 3600 | Maximum connection age before recycling (seconds) |
|
|
552
|
+
| `debug` | bool | False | Enable debug logging for pool operations |
|
|
553
|
+
| `pre_ping` | bool | True | Check connection health before returning from pool |
|
|
554
|
+
|
|
555
|
+
##### Monitoring Pool Statistics
|
|
556
|
+
|
|
557
|
+
```python
|
|
558
|
+
# Get pool statistics
|
|
559
|
+
stats = pool.get_statistics()
|
|
560
|
+
print(f"Active connections: {stats['active_connections']}")
|
|
561
|
+
print(f"Idle connections: {stats['idle_connections']}")
|
|
562
|
+
print(f"Total requests: {stats['total_requests']}")
|
|
563
|
+
print(f"Failed connections: {stats['failed_connections']}")
|
|
564
|
+
```
|
|
565
|
+
|
|
566
|
+
##### When to Use Connection Pooling
|
|
567
|
+
|
|
568
|
+
Connection pooling is recommended when:
|
|
569
|
+
- Executing multiple queries concurrently
|
|
570
|
+
- Building web applications or APIs
|
|
571
|
+
- Running batch processing jobs
|
|
572
|
+
- Reducing connection overhead
|
|
573
|
+
- Improving application performance
|
|
574
|
+
|
|
575
|
+
##### Direct Connection Usage (Without Pool)
|
|
576
|
+
|
|
577
|
+
For simple, single-threaded applications, you can still use direct connections:
|
|
578
|
+
|
|
579
|
+
```python
|
|
580
|
+
from e6data_python_connector import Connection
|
|
581
|
+
|
|
582
|
+
conn = Connection(
|
|
583
|
+
host=host,
|
|
584
|
+
port=port,
|
|
585
|
+
username=username,
|
|
586
|
+
password=password,
|
|
587
|
+
database=database
|
|
588
|
+
)
|
|
589
|
+
|
|
590
|
+
cursor = conn.cursor()
|
|
591
|
+
cursor.execute("SELECT * FROM table")
|
|
592
|
+
results = cursor.fetchall()
|
|
593
|
+
conn.close()
|
|
594
|
+
```
|
|
595
|
+
|
|
596
|
+
#### Additional Connection Management Features
|
|
405
597
|
- Automatic connection health monitoring
|
|
406
598
|
- Graceful connection recovery and retry logic
|
|
599
|
+
- Blue-green deployment support with automatic failover
|
|
407
600
|
|
|
408
601
|
See [TECH_DOC.md](TECH_DOC.md) for detailed technical documentation.
|