e6data-python-connector 2.3.11rc1__tar.gz → 2.3.12rc1__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.11rc1/e6data_python_connector.egg-info → e6data_python_connector-2.3.12rc1}/PKG-INFO +57 -2
- {e6data_python_connector-2.3.11rc1 → e6data_python_connector-2.3.12rc1}/README.md +56 -1
- {e6data_python_connector-2.3.11rc1 → e6data_python_connector-2.3.12rc1}/e6data_python_connector/cluster_manager.py +93 -9
- {e6data_python_connector-2.3.11rc1 → e6data_python_connector-2.3.12rc1}/e6data_python_connector/e6data_grpc.py +75 -32
- {e6data_python_connector-2.3.11rc1 → e6data_python_connector-2.3.12rc1/e6data_python_connector.egg-info}/PKG-INFO +57 -2
- {e6data_python_connector-2.3.11rc1 → e6data_python_connector-2.3.12rc1}/setup.py +1 -1
- {e6data_python_connector-2.3.11rc1 → e6data_python_connector-2.3.12rc1}/LICENSE +0 -0
- {e6data_python_connector-2.3.11rc1 → e6data_python_connector-2.3.12rc1}/MANIFEST.in +0 -0
- {e6data_python_connector-2.3.11rc1 → e6data_python_connector-2.3.12rc1}/e6data_python_connector/__init__.py +0 -0
- {e6data_python_connector-2.3.11rc1 → e6data_python_connector-2.3.12rc1}/e6data_python_connector/cluster_server/__init__.py +0 -0
- {e6data_python_connector-2.3.11rc1 → e6data_python_connector-2.3.12rc1}/e6data_python_connector/cluster_server/cluster_pb2.py +0 -0
- {e6data_python_connector-2.3.11rc1 → e6data_python_connector-2.3.12rc1}/e6data_python_connector/cluster_server/cluster_pb2_grpc.py +0 -0
- {e6data_python_connector-2.3.11rc1 → e6data_python_connector-2.3.12rc1}/e6data_python_connector/common.py +0 -0
- {e6data_python_connector-2.3.11rc1 → e6data_python_connector-2.3.12rc1}/e6data_python_connector/connection_pool.py +0 -0
- {e6data_python_connector-2.3.11rc1 → e6data_python_connector-2.3.12rc1}/e6data_python_connector/constants.py +0 -0
- {e6data_python_connector-2.3.11rc1 → e6data_python_connector-2.3.12rc1}/e6data_python_connector/datainputstream.py +0 -0
- {e6data_python_connector-2.3.11rc1 → e6data_python_connector-2.3.12rc1}/e6data_python_connector/date_time_utils.py +0 -0
- {e6data_python_connector-2.3.11rc1 → e6data_python_connector-2.3.12rc1}/e6data_python_connector/dialect.py +0 -0
- {e6data_python_connector-2.3.11rc1 → e6data_python_connector-2.3.12rc1}/e6data_python_connector/e6x_vector/__init__.py +0 -0
- {e6data_python_connector-2.3.11rc1 → e6data_python_connector-2.3.12rc1}/e6data_python_connector/e6x_vector/constants.py +0 -0
- {e6data_python_connector-2.3.11rc1 → e6data_python_connector-2.3.12rc1}/e6data_python_connector/e6x_vector/ttypes.py +0 -0
- {e6data_python_connector-2.3.11rc1 → e6data_python_connector-2.3.12rc1}/e6data_python_connector/exceptions.py +0 -0
- {e6data_python_connector-2.3.11rc1 → e6data_python_connector-2.3.12rc1}/e6data_python_connector/server/QueryEngineService.py +0 -0
- {e6data_python_connector-2.3.11rc1 → e6data_python_connector-2.3.12rc1}/e6data_python_connector/server/__init__.py +0 -0
- {e6data_python_connector-2.3.11rc1 → e6data_python_connector-2.3.12rc1}/e6data_python_connector/server/constants.py +0 -0
- {e6data_python_connector-2.3.11rc1 → e6data_python_connector-2.3.12rc1}/e6data_python_connector/server/e6x_engine_pb2.py +0 -0
- {e6data_python_connector-2.3.11rc1 → e6data_python_connector-2.3.12rc1}/e6data_python_connector/server/e6x_engine_pb2.pyi +0 -0
- {e6data_python_connector-2.3.11rc1 → e6data_python_connector-2.3.12rc1}/e6data_python_connector/server/e6x_engine_pb2_grpc.py +0 -0
- {e6data_python_connector-2.3.11rc1 → e6data_python_connector-2.3.12rc1}/e6data_python_connector/server/ttypes.py +0 -0
- {e6data_python_connector-2.3.11rc1 → e6data_python_connector-2.3.12rc1}/e6data_python_connector/strategy.py +0 -0
- {e6data_python_connector-2.3.11rc1 → e6data_python_connector-2.3.12rc1}/e6data_python_connector/typeId.py +0 -0
- {e6data_python_connector-2.3.11rc1 → e6data_python_connector-2.3.12rc1}/e6data_python_connector.egg-info/SOURCES.txt +0 -0
- {e6data_python_connector-2.3.11rc1 → e6data_python_connector-2.3.12rc1}/e6data_python_connector.egg-info/dependency_links.txt +0 -0
- {e6data_python_connector-2.3.11rc1 → e6data_python_connector-2.3.12rc1}/e6data_python_connector.egg-info/entry_points.txt +0 -0
- {e6data_python_connector-2.3.11rc1 → e6data_python_connector-2.3.12rc1}/e6data_python_connector.egg-info/requires.txt +0 -0
- {e6data_python_connector-2.3.11rc1 → e6data_python_connector-2.3.12rc1}/e6data_python_connector.egg-info/top_level.txt +0 -0
- {e6data_python_connector-2.3.11rc1 → e6data_python_connector-2.3.12rc1}/gen-py/__init__.py +0 -0
- {e6data_python_connector-2.3.11rc1 → e6data_python_connector-2.3.12rc1}/gen-py/e6x_vector/__init__.py +0 -0
- {e6data_python_connector-2.3.11rc1 → e6data_python_connector-2.3.12rc1}/gen-py/e6x_vector/constants.py +0 -0
- {e6data_python_connector-2.3.11rc1 → e6data_python_connector-2.3.12rc1}/gen-py/e6x_vector/ttypes.py +0 -0
- {e6data_python_connector-2.3.11rc1 → e6data_python_connector-2.3.12rc1}/pyproject.toml +0 -0
- {e6data_python_connector-2.3.11rc1 → e6data_python_connector-2.3.12rc1}/setup.cfg +0 -0
- {e6data_python_connector-2.3.11rc1 → e6data_python_connector-2.3.12rc1}/test/__init__.py +0 -0
- {e6data_python_connector-2.3.11rc1 → e6data_python_connector-2.3.12rc1}/test/analyze_38_nines.py +0 -0
- {e6data_python_connector-2.3.11rc1 → e6data_python_connector-2.3.12rc1}/test/analyze_all_cases.py +0 -0
- {e6data_python_connector-2.3.11rc1 → e6data_python_connector-2.3.12rc1}/test/analyze_binary.py +0 -0
- {e6data_python_connector-2.3.11rc1 → e6data_python_connector-2.3.12rc1}/test/analyze_correct_value.py +0 -0
- {e6data_python_connector-2.3.11rc1 → e6data_python_connector-2.3.12rc1}/test/analyze_fields.py +0 -0
- {e6data_python_connector-2.3.11rc1 → e6data_python_connector-2.3.12rc1}/test/check_decimal_errors.py +0 -0
- {e6data_python_connector-2.3.11rc1 → e6data_python_connector-2.3.12rc1}/test/cleanup_test_files.py +0 -0
- {e6data_python_connector-2.3.11rc1 → e6data_python_connector-2.3.12rc1}/test/debug_38_nines.py +0 -0
- {e6data_python_connector-2.3.11rc1 → e6data_python_connector-2.3.12rc1}/test/debug_binary.py +0 -0
- {e6data_python_connector-2.3.11rc1 → e6data_python_connector-2.3.12rc1}/test/final_test.py +0 -0
- {e6data_python_connector-2.3.11rc1 → e6data_python_connector-2.3.12rc1}/test/move_tests.py +0 -0
- {e6data_python_connector-2.3.11rc1 → e6data_python_connector-2.3.12rc1}/test/quick_test.py +0 -0
- {e6data_python_connector-2.3.11rc1 → e6data_python_connector-2.3.12rc1}/test/test_38_nines.py +0 -0
- {e6data_python_connector-2.3.11rc1 → e6data_python_connector-2.3.12rc1}/test/test_all_decimal128_cases.py +0 -0
- {e6data_python_connector-2.3.11rc1 → e6data_python_connector-2.3.12rc1}/test/test_cluster_manager_efficiency.py +0 -0
- {e6data_python_connector-2.3.11rc1 → e6data_python_connector-2.3.12rc1}/test/test_cluster_manager_none_strategy.py +0 -0
- {e6data_python_connector-2.3.11rc1 → e6data_python_connector-2.3.12rc1}/test/test_cluster_manager_strategy.py +0 -0
- {e6data_python_connector-2.3.11rc1 → e6data_python_connector-2.3.12rc1}/test/test_comprehensive.py +0 -0
- {e6data_python_connector-2.3.11rc1 → e6data_python_connector-2.3.12rc1}/test/test_connection_pool.py +0 -0
- {e6data_python_connector-2.3.11rc1 → e6data_python_connector-2.3.12rc1}/test/test_connection_pool_e2e.py +0 -0
- {e6data_python_connector-2.3.11rc1 → e6data_python_connector-2.3.12rc1}/test/test_current_implementation.py +0 -0
- {e6data_python_connector-2.3.11rc1 → e6data_python_connector-2.3.12rc1}/test/test_decimal128_binary_parsing.py +0 -0
- {e6data_python_connector-2.3.11rc1 → e6data_python_connector-2.3.12rc1}/test/test_decimal128_parsing.py +0 -0
- {e6data_python_connector-2.3.11rc1 → e6data_python_connector-2.3.12rc1}/test/test_fix.py +0 -0
- {e6data_python_connector-2.3.11rc1 → e6data_python_connector-2.3.12rc1}/test/test_improved_parsing.py +0 -0
- {e6data_python_connector-2.3.11rc1 → e6data_python_connector-2.3.12rc1}/test/test_known_case.py +0 -0
- {e6data_python_connector-2.3.11rc1 → e6data_python_connector-2.3.12rc1}/test/test_manual_analysis.py +0 -0
- {e6data_python_connector-2.3.11rc1 → e6data_python_connector-2.3.12rc1}/test/test_mock_server.py +0 -0
- {e6data_python_connector-2.3.11rc1 → e6data_python_connector-2.3.12rc1}/test/test_multiprocessing_fix.py +0 -0
- {e6data_python_connector-2.3.11rc1 → e6data_python_connector-2.3.12rc1}/test/test_new_implementation.py +0 -0
- {e6data_python_connector-2.3.11rc1 → e6data_python_connector-2.3.12rc1}/test/test_pool_concurrency_simple.py +0 -0
- {e6data_python_connector-2.3.11rc1 → e6data_python_connector-2.3.12rc1}/test/test_pool_threading_multiprocessing.py +0 -0
- {e6data_python_connector-2.3.11rc1 → e6data_python_connector-2.3.12rc1}/test/test_simple_connection_pool.py +0 -0
- {e6data_python_connector-2.3.11rc1 → e6data_python_connector-2.3.12rc1}/test/test_specific_binary.py +0 -0
- {e6data_python_connector-2.3.11rc1 → e6data_python_connector-2.3.12rc1}/test/test_strategy.py +0 -0
- {e6data_python_connector-2.3.11rc1 → e6data_python_connector-2.3.12rc1}/test/test_strategy_logic.py +0 -0
- {e6data_python_connector-2.3.11rc1 → e6data_python_connector-2.3.12rc1}/test/test_strategy_persistence_fix.py +0 -0
- {e6data_python_connector-2.3.11rc1 → e6data_python_connector-2.3.12rc1}/test/test_strategy_sharing_fix.py +0 -0
- {e6data_python_connector-2.3.11rc1 → e6data_python_connector-2.3.12rc1}/test/test_user_binary_value.py +0 -0
- {e6data_python_connector-2.3.11rc1 → e6data_python_connector-2.3.12rc1}/test/tests.py +0 -0
- {e6data_python_connector-2.3.11rc1 → e6data_python_connector-2.3.12rc1}/test/tests_grpc.py +0 -0
- {e6data_python_connector-2.3.11rc1 → e6data_python_connector-2.3.12rc1}/test/validate_decimal128.py +0 -0
- {e6data_python_connector-2.3.11rc1 → e6data_python_connector-2.3.12rc1}/test/validate_implementation.py +0 -0
- {e6data_python_connector-2.3.11rc1 → e6data_python_connector-2.3.12rc1}/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.12rc1
|
|
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.
|
|
@@ -39,7 +39,7 @@ Dynamic: summary
|
|
|
39
39
|
|
|
40
40
|
# e6data Python Connector
|
|
41
41
|
|
|
42
|
-

|
|
43
43
|
|
|
44
44
|
## Introduction
|
|
45
45
|
|
|
@@ -124,6 +124,7 @@ The `Connection` class supports the following parameters:
|
|
|
124
124
|
| `secure` | bool | No | False | Enable SSL/TLS for secure connections |
|
|
125
125
|
| `auto_resume` | bool | No | True | Automatically resume cluster if suspended |
|
|
126
126
|
| `grpc_options` | dict | No | None | Additional gRPC configuration options |
|
|
127
|
+
| `debug` | bool | No | False | Enable debug logging for troubleshooting |
|
|
127
128
|
|
|
128
129
|
#### Secure Connection Example
|
|
129
130
|
|
|
@@ -637,4 +638,58 @@ conn.close()
|
|
|
637
638
|
- Graceful connection recovery and retry logic
|
|
638
639
|
- Blue-green deployment support with automatic failover
|
|
639
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 |
|
|
694
|
+
|
|
640
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
|
|
|
@@ -85,6 +85,7 @@ The `Connection` class supports the following parameters:
|
|
|
85
85
|
| `secure` | bool | No | False | Enable SSL/TLS for secure connections |
|
|
86
86
|
| `auto_resume` | bool | No | True | Automatically resume cluster if suspended |
|
|
87
87
|
| `grpc_options` | dict | No | None | Additional gRPC configuration options |
|
|
88
|
+
| `debug` | bool | No | False | Enable debug logging for troubleshooting |
|
|
88
89
|
|
|
89
90
|
#### Secure Connection Example
|
|
90
91
|
|
|
@@ -598,4 +599,58 @@ conn.close()
|
|
|
598
599
|
- Graceful connection recovery and retry logic
|
|
599
600
|
- Blue-green deployment support with automatic failover
|
|
600
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 |
|
|
655
|
+
|
|
601
656
|
See [TECH_DOC.md](TECH_DOC.md) for detailed technical documentation.
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import logging
|
|
1
2
|
import threading
|
|
2
3
|
import time
|
|
3
4
|
import e6data_python_connector.cluster_server.cluster_pb2 as cluster_pb2
|
|
@@ -6,6 +7,8 @@ import grpc
|
|
|
6
7
|
from grpc._channel import _InactiveRpcError
|
|
7
8
|
import multiprocessing
|
|
8
9
|
|
|
10
|
+
logger = logging.getLogger(__name__)
|
|
11
|
+
|
|
9
12
|
from e6data_python_connector.strategy import _get_active_strategy, _set_active_strategy, _set_pending_strategy, \
|
|
10
13
|
_get_grpc_header as _get_strategy_header
|
|
11
14
|
|
|
@@ -137,7 +140,7 @@ class ClusterManager:
|
|
|
137
140
|
"""
|
|
138
141
|
|
|
139
142
|
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):
|
|
143
|
+
cluster_uuid=None, grpc_options=None, debug=False):
|
|
141
144
|
"""
|
|
142
145
|
Initializes a new instance of the ClusterManager class.
|
|
143
146
|
|
|
@@ -152,6 +155,7 @@ class ClusterManager:
|
|
|
152
155
|
defaults to 5 minutes.
|
|
153
156
|
cluster_uuid (str, optional): The unique identifier for the target cluster;
|
|
154
157
|
defaults to None.
|
|
158
|
+
debug (bool, optional): Enable debug logging; defaults to False.
|
|
155
159
|
"""
|
|
156
160
|
|
|
157
161
|
self._host = host
|
|
@@ -164,6 +168,7 @@ class ClusterManager:
|
|
|
164
168
|
self._grpc_options = grpc_options
|
|
165
169
|
if grpc_options is None:
|
|
166
170
|
self._grpc_options = dict()
|
|
171
|
+
self._debug = debug
|
|
167
172
|
|
|
168
173
|
@property
|
|
169
174
|
def _get_connection(self):
|
|
@@ -191,20 +196,22 @@ class ClusterManager:
|
|
|
191
196
|
def _try_cluster_request(self, request_type, payload=None):
|
|
192
197
|
"""
|
|
193
198
|
Execute a cluster request with strategy fallback for 456 errors.
|
|
194
|
-
|
|
199
|
+
|
|
195
200
|
For efficiency:
|
|
196
201
|
- If we have an active strategy, use it first
|
|
197
202
|
- Only try authentication sequence (blue -> green) if no active strategy
|
|
198
203
|
- On 456 error, switch to alternative strategy and update active strategy
|
|
199
|
-
|
|
204
|
+
|
|
200
205
|
Args:
|
|
201
206
|
request_type: Type of request ('status' or 'resume')
|
|
202
207
|
payload: Request payload (optional, will be created if not provided)
|
|
203
|
-
|
|
208
|
+
|
|
204
209
|
Returns:
|
|
205
210
|
The response from the successful request
|
|
206
211
|
"""
|
|
207
212
|
current_strategy = _get_active_strategy()
|
|
213
|
+
if self._debug:
|
|
214
|
+
logger.info(f"Attempting {request_type} request with current strategy: {current_strategy}")
|
|
208
215
|
|
|
209
216
|
# Create payload if not provided
|
|
210
217
|
if payload is None:
|
|
@@ -222,6 +229,8 @@ class ClusterManager:
|
|
|
222
229
|
# If we have an active strategy, use it first
|
|
223
230
|
if current_strategy is not None:
|
|
224
231
|
try:
|
|
232
|
+
if self._debug:
|
|
233
|
+
logger.info(f"Executing {request_type} with strategy '{current_strategy}'")
|
|
225
234
|
if request_type == "status":
|
|
226
235
|
response = self._get_connection.status(
|
|
227
236
|
payload,
|
|
@@ -239,16 +248,24 @@ class ClusterManager:
|
|
|
239
248
|
if hasattr(response, 'new_strategy') and response.new_strategy:
|
|
240
249
|
new_strategy = response.new_strategy.lower()
|
|
241
250
|
if new_strategy != current_strategy:
|
|
251
|
+
if self._debug:
|
|
252
|
+
logger.info(f"Server indicated strategy transition from '{current_strategy}' to '{new_strategy}'")
|
|
242
253
|
_set_pending_strategy(new_strategy)
|
|
243
254
|
|
|
255
|
+
if self._debug:
|
|
256
|
+
logger.info(f"{request_type} request successful with strategy '{current_strategy}'")
|
|
244
257
|
return response
|
|
245
258
|
|
|
246
259
|
except _InactiveRpcError as e:
|
|
247
260
|
if e.code() == grpc.StatusCode.UNKNOWN and 'status: 456' in e.details():
|
|
248
261
|
# 456 error - switch to alternative strategy
|
|
249
262
|
alternative_strategy = 'green' if current_strategy == 'blue' else 'blue'
|
|
263
|
+
if self._debug:
|
|
264
|
+
logger.info(f"Received 456 error with strategy '{current_strategy}', switching to '{alternative_strategy}'")
|
|
250
265
|
|
|
251
266
|
try:
|
|
267
|
+
if self._debug:
|
|
268
|
+
logger.info(f"Retrying {request_type} with alternative strategy '{alternative_strategy}'")
|
|
252
269
|
if request_type == "status":
|
|
253
270
|
response = self._get_connection.status(
|
|
254
271
|
payload,
|
|
@@ -261,27 +278,39 @@ class ClusterManager:
|
|
|
261
278
|
)
|
|
262
279
|
|
|
263
280
|
# Update active strategy since the alternative worked
|
|
281
|
+
if self._debug:
|
|
282
|
+
logger.info(f"Successfully switched to strategy '{alternative_strategy}'")
|
|
264
283
|
_set_active_strategy(alternative_strategy)
|
|
265
284
|
|
|
266
285
|
# Check for new strategy in response
|
|
267
286
|
if hasattr(response, 'new_strategy') and response.new_strategy:
|
|
268
287
|
new_strategy = response.new_strategy.lower()
|
|
269
288
|
if new_strategy != alternative_strategy:
|
|
289
|
+
if self._debug:
|
|
290
|
+
logger.info(f"Server indicated future strategy transition to '{new_strategy}'")
|
|
270
291
|
_set_pending_strategy(new_strategy)
|
|
271
292
|
|
|
272
293
|
return response
|
|
273
294
|
|
|
274
295
|
except _InactiveRpcError as e2:
|
|
296
|
+
if self._debug:
|
|
297
|
+
logger.error(f"Failed with alternative strategy '{alternative_strategy}': {e2}")
|
|
275
298
|
raise e # Raise the original error
|
|
276
299
|
else:
|
|
277
300
|
# Non-456 error - don't retry
|
|
301
|
+
if self._debug:
|
|
302
|
+
logger.info(f"Non-456 error received, not retrying: {e}")
|
|
278
303
|
raise e
|
|
279
304
|
|
|
280
305
|
# No active strategy - start with authentication logic (blue first, then green)
|
|
281
306
|
strategies_to_try = ['blue', 'green']
|
|
307
|
+
if self._debug:
|
|
308
|
+
logger.info("No active strategy found, trying authentication sequence: blue -> green")
|
|
282
309
|
|
|
283
310
|
for i, strategy in enumerate(strategies_to_try):
|
|
284
311
|
try:
|
|
312
|
+
if self._debug:
|
|
313
|
+
logger.info(f"Trying {request_type} with strategy '{strategy}' (attempt {i+1}/{len(strategies_to_try)})")
|
|
285
314
|
|
|
286
315
|
if request_type == "status":
|
|
287
316
|
response = self._get_connection.status(
|
|
@@ -297,12 +326,16 @@ class ClusterManager:
|
|
|
297
326
|
raise ValueError(f"Unknown request type: {request_type}")
|
|
298
327
|
|
|
299
328
|
# Set the working strategy as active
|
|
329
|
+
if self._debug:
|
|
330
|
+
logger.info(f"Successfully authenticated with strategy '{strategy}'")
|
|
300
331
|
_set_active_strategy(strategy)
|
|
301
332
|
|
|
302
333
|
# Check for new strategy in response
|
|
303
334
|
if hasattr(response, 'new_strategy') and response.new_strategy:
|
|
304
335
|
new_strategy = response.new_strategy.lower()
|
|
305
336
|
if new_strategy != strategy:
|
|
337
|
+
if self._debug:
|
|
338
|
+
logger.info(f"Server indicated strategy transition to '{new_strategy}'")
|
|
306
339
|
_set_pending_strategy(new_strategy)
|
|
307
340
|
|
|
308
341
|
return response
|
|
@@ -311,14 +344,22 @@ class ClusterManager:
|
|
|
311
344
|
if e.code() == grpc.StatusCode.UNKNOWN and 'status: 456' in e.details():
|
|
312
345
|
# 456 error - try next strategy
|
|
313
346
|
if i < len(strategies_to_try) - 1:
|
|
347
|
+
if self._debug:
|
|
348
|
+
logger.info(f"Strategy '{strategy}' returned 456 error, trying next strategy")
|
|
314
349
|
continue
|
|
315
350
|
else:
|
|
351
|
+
if self._debug:
|
|
352
|
+
logger.error(f"All strategies failed with 456 errors")
|
|
316
353
|
raise e
|
|
317
354
|
else:
|
|
318
355
|
# Non-456 error - don't retry
|
|
356
|
+
if self._debug:
|
|
357
|
+
logger.error(f"Strategy '{strategy}' failed with non-456 error: {e}")
|
|
319
358
|
raise e
|
|
320
359
|
|
|
321
360
|
# If we get here, all strategies failed
|
|
361
|
+
if self._debug:
|
|
362
|
+
logger.error("All authentication strategies exhausted")
|
|
322
363
|
raise e
|
|
323
364
|
|
|
324
365
|
def _check_cluster_status(self):
|
|
@@ -326,8 +367,12 @@ class ClusterManager:
|
|
|
326
367
|
try:
|
|
327
368
|
# Use the unified strategy-aware request method
|
|
328
369
|
response = self._try_cluster_request("status")
|
|
370
|
+
if self._debug:
|
|
371
|
+
logger.info(f"Cluster status check returned: {response.status}")
|
|
329
372
|
yield response.status
|
|
330
|
-
except _InactiveRpcError:
|
|
373
|
+
except _InactiveRpcError as e:
|
|
374
|
+
if self._debug:
|
|
375
|
+
logger.warning(f"Status check failed with error: {e}")
|
|
331
376
|
yield None
|
|
332
377
|
|
|
333
378
|
def resume(self) -> bool:
|
|
@@ -357,38 +402,77 @@ class ClusterManager:
|
|
|
357
402
|
- The operation is subject to the `_timeout` threshold;
|
|
358
403
|
if the timeout expires, the method returns False.
|
|
359
404
|
"""
|
|
405
|
+
if self._debug:
|
|
406
|
+
logger.info(f"Starting auto-resume for cluster {self.cluster_uuid} at {self._host}:{self._port}")
|
|
360
407
|
|
|
361
408
|
with status_lock as lock:
|
|
362
409
|
if lock.is_active:
|
|
410
|
+
if self._debug:
|
|
411
|
+
logger.info("Lock is already active, cluster appears to be running")
|
|
363
412
|
return True
|
|
364
413
|
|
|
365
414
|
# Retrieve the current cluster status with strategy header
|
|
415
|
+
if self._debug:
|
|
416
|
+
logger.info("Checking current cluster status")
|
|
366
417
|
try:
|
|
367
418
|
current_status = self._try_cluster_request("status")
|
|
368
|
-
|
|
419
|
+
if self._debug:
|
|
420
|
+
logger.info(f"Current cluster status: {current_status.status}")
|
|
421
|
+
except _InactiveRpcError as e:
|
|
422
|
+
if self._debug:
|
|
423
|
+
logger.error(f"Failed to get cluster status: {e}")
|
|
369
424
|
return False
|
|
425
|
+
|
|
370
426
|
if current_status.status == 'suspended':
|
|
371
427
|
# Send the resume request with strategy header
|
|
428
|
+
if self._debug:
|
|
429
|
+
logger.info("Cluster is suspended, sending resume request")
|
|
372
430
|
try:
|
|
373
431
|
response = self._try_cluster_request("resume")
|
|
374
|
-
|
|
432
|
+
if self._debug:
|
|
433
|
+
logger.info("Resume request sent successfully")
|
|
434
|
+
except _InactiveRpcError as e:
|
|
435
|
+
if self._debug:
|
|
436
|
+
logger.error(f"Failed to send resume request: {e}")
|
|
375
437
|
return False
|
|
376
438
|
elif current_status.status == 'active':
|
|
439
|
+
if self._debug:
|
|
440
|
+
logger.info("Cluster is already active, no action needed")
|
|
377
441
|
return True
|
|
378
442
|
elif current_status.status != 'resuming':
|
|
379
443
|
"""
|
|
380
|
-
If cluster cannot be resumed due to its current state,
|
|
444
|
+
If cluster cannot be resumed due to its current state,
|
|
381
445
|
or already in a process of resuming, terminate the operation.
|
|
382
446
|
"""
|
|
447
|
+
if self._debug:
|
|
448
|
+
logger.warning(f"Cluster is in state '{current_status.status}' which cannot be resumed")
|
|
383
449
|
return False
|
|
384
450
|
|
|
451
|
+
if self._debug:
|
|
452
|
+
logger.info("Monitoring cluster status until it becomes active...")
|
|
453
|
+
check_count = 0
|
|
385
454
|
for status in self._check_cluster_status():
|
|
455
|
+
check_count += 1
|
|
386
456
|
if status == 'active':
|
|
457
|
+
if self._debug:
|
|
458
|
+
logger.info(f"Cluster became active after {check_count} status checks")
|
|
387
459
|
return True
|
|
388
|
-
elif status == 'failed'
|
|
460
|
+
elif status == 'failed':
|
|
461
|
+
if self._debug:
|
|
462
|
+
logger.error(f"Cluster failed to resume after {check_count} status checks")
|
|
463
|
+
return False
|
|
464
|
+
elif time.time() > self._timeout:
|
|
465
|
+
if self._debug:
|
|
466
|
+
logger.error(f"Resume operation timed out after {check_count} status checks")
|
|
389
467
|
return False
|
|
468
|
+
|
|
469
|
+
if self._debug:
|
|
470
|
+
logger.info(f"Status check #{check_count}: {status}, waiting 5 seconds before next check")
|
|
390
471
|
# Wait for 5 seconds before the next status check
|
|
391
472
|
time.sleep(5)
|
|
473
|
+
|
|
474
|
+
if self._debug:
|
|
475
|
+
logger.warning("Resume operation completed without reaching active state")
|
|
392
476
|
return False
|
|
393
477
|
|
|
394
478
|
def suspend(self):
|
|
@@ -9,6 +9,7 @@ from __future__ import unicode_literals
|
|
|
9
9
|
import datetime
|
|
10
10
|
import logging
|
|
11
11
|
import os
|
|
12
|
+
|
|
12
13
|
import re
|
|
13
14
|
import sys
|
|
14
15
|
import time
|
|
@@ -114,6 +115,9 @@ class HiveParamEscaper(ParamEscaper):
|
|
|
114
115
|
|
|
115
116
|
_escaper = HiveParamEscaper()
|
|
116
117
|
|
|
118
|
+
# Logger for the module
|
|
119
|
+
logger = logging.getLogger(__name__)
|
|
120
|
+
|
|
117
121
|
# Thread-safe and process-safe storage for active deployment strategy
|
|
118
122
|
_strategy_lock = threading.Lock()
|
|
119
123
|
_strategy_manager = None
|
|
@@ -136,7 +140,7 @@ _debug_connections = set()
|
|
|
136
140
|
def _strategy_debug_log(message):
|
|
137
141
|
"""Log strategy debug messages if any connection has debug enabled."""
|
|
138
142
|
if _debug_connections:
|
|
139
|
-
|
|
143
|
+
logger.info(f"[E6DATA_STRATEGY_DEBUG] {time.strftime('%Y-%m-%d %H:%M:%S')} - {message}")
|
|
140
144
|
|
|
141
145
|
|
|
142
146
|
def _get_shared_strategy():
|
|
@@ -384,6 +388,7 @@ class Connection(object):
|
|
|
384
388
|
self._auto_resume = auto_resume
|
|
385
389
|
|
|
386
390
|
self._grpc_options = grpc_options
|
|
391
|
+
|
|
387
392
|
if self._grpc_options is None:
|
|
388
393
|
self._grpc_options = dict()
|
|
389
394
|
self.grpc_prepare_timeout = self._grpc_options.get('grpc_prepare_timeout') or 10 * 60 # 10 minutes
|
|
@@ -393,7 +398,15 @@ class Connection(object):
|
|
|
393
398
|
The default maximum time on client side to wait for the cluster to resume is 5 minutes.
|
|
394
399
|
"""
|
|
395
400
|
self.grpc_auto_resume_timeout_seconds = self._grpc_options.pop('grpc_auto_resume_timeout_seconds')
|
|
396
|
-
|
|
401
|
+
|
|
402
|
+
self.timeout = 500
|
|
403
|
+
if 'timeout' in self._grpc_options:
|
|
404
|
+
"""
|
|
405
|
+
The default maximum time on client side to wait for the cluster to resume is 5 minutes.
|
|
406
|
+
"""
|
|
407
|
+
self.timeout = self._grpc_options.pop('timeout')
|
|
408
|
+
|
|
409
|
+
|
|
397
410
|
# Store debug flag and register with debug connections
|
|
398
411
|
self._debug = debug
|
|
399
412
|
if self._debug:
|
|
@@ -409,21 +422,39 @@ class Connection(object):
|
|
|
409
422
|
force=True # Force reconfiguration even if logging is already configured
|
|
410
423
|
)
|
|
411
424
|
|
|
412
|
-
#
|
|
413
|
-
|
|
414
|
-
|
|
425
|
+
# Note: gRPC C++ core tracing (GRPC_VERBOSITY and GRPC_TRACE) must be set
|
|
426
|
+
# BEFORE the gRPC module is imported to take effect. Setting them at runtime
|
|
427
|
+
# will not enable HTTP/2 frame logs or low-level tracing.
|
|
428
|
+
#
|
|
429
|
+
# To enable full gRPC network tracing, set these environment variables
|
|
430
|
+
# before starting your Python script:
|
|
431
|
+
# export GRPC_VERBOSITY=DEBUG
|
|
432
|
+
# export GRPC_TRACE=client_channel,http2
|
|
433
|
+
#
|
|
434
|
+
# The following runtime settings only affect Python-level logging:
|
|
435
|
+
|
|
436
|
+
# Enable gRPC Python logging (this works at runtime)
|
|
437
|
+
os.environ['GRPC_PYTHON_LOG_LEVEL'] = 'DEBUG'
|
|
438
|
+
os.environ['GRPC_PYTHON_LOG_STDERR'] = '1'
|
|
415
439
|
|
|
416
440
|
# Ensure gRPC logger is at DEBUG level
|
|
417
441
|
grpc_logger = logging.getLogger('grpc')
|
|
418
442
|
grpc_logger.setLevel(logging.DEBUG)
|
|
419
443
|
|
|
444
|
+
# Enable gRPC transport logger
|
|
445
|
+
grpc_transport_logger = logging.getLogger('grpc._channel')
|
|
446
|
+
grpc_transport_logger.setLevel(logging.DEBUG)
|
|
447
|
+
|
|
448
|
+
# Enable gRPC server logger
|
|
449
|
+
grpc_server_logger = logging.getLogger('grpc._server')
|
|
450
|
+
grpc_server_logger.setLevel(logging.DEBUG)
|
|
451
|
+
|
|
420
452
|
# Set e6data connector logger to DEBUG
|
|
421
453
|
e6data_logger = logging.getLogger('e6data_python_connector')
|
|
422
454
|
e6data_logger.setLevel(logging.DEBUG)
|
|
423
455
|
|
|
424
456
|
_strategy_debug_log(f"Debug mode enabled for connection {id(self)}")
|
|
425
|
-
_strategy_debug_log(f"
|
|
426
|
-
_strategy_debug_log(f"gRPC debugging enabled with GRPC_VERBOSITY=DEBUG and GRPC_TRACE=all")
|
|
457
|
+
_strategy_debug_log(f"GRPC_TRACE={os.environ.get('GRPC_TRACE')}")
|
|
427
458
|
|
|
428
459
|
self._create_client()
|
|
429
460
|
|
|
@@ -578,7 +609,12 @@ class Connection(object):
|
|
|
578
609
|
_clear_strategy_cache()
|
|
579
610
|
active_strategy = None
|
|
580
611
|
else:
|
|
581
|
-
|
|
612
|
+
if self._perform_auto_resume(e):
|
|
613
|
+
# Cluster resumed, retry with cached strategy
|
|
614
|
+
_strategy_debug_log(f"Cluster resumed, retrying with cached strategy {active_strategy}")
|
|
615
|
+
active_strategy = None # Force retry
|
|
616
|
+
else:
|
|
617
|
+
raise e
|
|
582
618
|
elif pending_strategy:
|
|
583
619
|
# If there's a pending strategy, force re-authentication with new strategy
|
|
584
620
|
active_strategy = None
|
|
@@ -596,7 +632,7 @@ class Connection(object):
|
|
|
596
632
|
_strategy_debug_log(f"No cached strategy, will try strategies in order: {strategies}")
|
|
597
633
|
last_error = None
|
|
598
634
|
for strategy in strategies:
|
|
599
|
-
_strategy_debug_log(f"Attempting authentication with strategy: {strategy}")
|
|
635
|
+
_strategy_debug_log(f"Attempting authentication with strategy: {strategy}.")
|
|
600
636
|
try:
|
|
601
637
|
authenticate_response = self._client.authenticate(
|
|
602
638
|
authenticate_request,
|
|
@@ -626,8 +662,13 @@ class Connection(object):
|
|
|
626
662
|
last_error = e
|
|
627
663
|
continue
|
|
628
664
|
else:
|
|
629
|
-
|
|
630
|
-
|
|
665
|
+
if self._perform_auto_resume(e):
|
|
666
|
+
# Cluster resumed successfully, retry authentication with current strategy
|
|
667
|
+
_strategy_debug_log(f"Cluster resumed, retrying authentication with {strategy}")
|
|
668
|
+
continue
|
|
669
|
+
else:
|
|
670
|
+
# Auto-resume failed, raise the error
|
|
671
|
+
raise e
|
|
631
672
|
|
|
632
673
|
if not self._session_id and last_error:
|
|
633
674
|
# Neither strategy worked
|
|
@@ -636,30 +677,31 @@ class Connection(object):
|
|
|
636
677
|
if not self._session_id:
|
|
637
678
|
raise ValueError("Invalid credentials.")
|
|
638
679
|
except _InactiveRpcError as e:
|
|
639
|
-
|
|
640
|
-
if e.code() == grpc.StatusCode.UNAVAILABLE and 'status: 503' in e.details():
|
|
641
|
-
status = ClusterManager(
|
|
642
|
-
host=self._host,
|
|
643
|
-
port=self._port,
|
|
644
|
-
user=self.__username,
|
|
645
|
-
password=self.__password,
|
|
646
|
-
secure_channel=self._secure_channel,
|
|
647
|
-
cluster_uuid=self.cluster_name,
|
|
648
|
-
timeout=self.grpc_auto_resume_timeout_seconds
|
|
649
|
-
).resume()
|
|
650
|
-
if status:
|
|
651
|
-
return self.get_session_id
|
|
652
|
-
else:
|
|
653
|
-
raise e
|
|
654
|
-
else:
|
|
655
|
-
raise e
|
|
656
|
-
else:
|
|
657
|
-
raise e
|
|
680
|
+
self._perform_auto_resume(e)
|
|
658
681
|
except Exception as e:
|
|
659
682
|
self._channel.close()
|
|
660
683
|
raise e
|
|
661
684
|
return self._session_id
|
|
662
685
|
|
|
686
|
+
def _perform_auto_resume(self, e: _InactiveRpcError):
|
|
687
|
+
if self._auto_resume:
|
|
688
|
+
if e.code() == grpc.StatusCode.UNAVAILABLE and 'status: 503' in e.details():
|
|
689
|
+
status = ClusterManager(
|
|
690
|
+
host=self._host,
|
|
691
|
+
port=self._port,
|
|
692
|
+
user=self.__username,
|
|
693
|
+
password=self.__password,
|
|
694
|
+
secure_channel=self._secure_channel,
|
|
695
|
+
cluster_uuid=self.cluster_name,
|
|
696
|
+
timeout=self.grpc_auto_resume_timeout_seconds,
|
|
697
|
+
debug=self._debug
|
|
698
|
+
).resume()
|
|
699
|
+
return status # Return boolean status directly
|
|
700
|
+
else:
|
|
701
|
+
return False # Non-503 error, cannot auto-resume
|
|
702
|
+
else:
|
|
703
|
+
return False # Auto-resume disabled
|
|
704
|
+
|
|
663
705
|
def __enter__(self):
|
|
664
706
|
"""
|
|
665
707
|
Enters the runtime context related to this object.
|
|
@@ -1410,13 +1452,14 @@ class Cursor(DBAPICursor):
|
|
|
1410
1452
|
get_next_result_batch_request = e6x_engine_pb2.GetNextResultBatchRequest(
|
|
1411
1453
|
engineIP=self._engine_ip,
|
|
1412
1454
|
sessionId=self.connection.get_session_id,
|
|
1413
|
-
queryId=self._query_id
|
|
1455
|
+
queryId=self._query_id,
|
|
1414
1456
|
)
|
|
1415
1457
|
# Get fresh client after session access (may have been invalidated)
|
|
1416
1458
|
client = self.connection.client
|
|
1417
1459
|
get_next_result_batch_response = client.getNextResultBatch(
|
|
1418
1460
|
get_next_result_batch_request,
|
|
1419
|
-
metadata=self.metadata
|
|
1461
|
+
metadata=self.metadata,
|
|
1462
|
+
timeout=self.connection.timeout
|
|
1420
1463
|
)
|
|
1421
1464
|
|
|
1422
1465
|
# Check for new strategy in batch response
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.2
|
|
2
2
|
Name: e6data-python-connector
|
|
3
|
-
Version: 2.3.
|
|
3
|
+
Version: 2.3.12rc1
|
|
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.
|
|
@@ -39,7 +39,7 @@ Dynamic: summary
|
|
|
39
39
|
|
|
40
40
|
# e6data Python Connector
|
|
41
41
|
|
|
42
|
-

|
|
43
43
|
|
|
44
44
|
## Introduction
|
|
45
45
|
|
|
@@ -124,6 +124,7 @@ The `Connection` class supports the following parameters:
|
|
|
124
124
|
| `secure` | bool | No | False | Enable SSL/TLS for secure connections |
|
|
125
125
|
| `auto_resume` | bool | No | True | Automatically resume cluster if suspended |
|
|
126
126
|
| `grpc_options` | dict | No | None | Additional gRPC configuration options |
|
|
127
|
+
| `debug` | bool | No | False | Enable debug logging for troubleshooting |
|
|
127
128
|
|
|
128
129
|
#### Secure Connection Example
|
|
129
130
|
|
|
@@ -637,4 +638,58 @@ conn.close()
|
|
|
637
638
|
- Graceful connection recovery and retry logic
|
|
638
639
|
- Blue-green deployment support with automatic failover
|
|
639
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 |
|
|
694
|
+
|
|
640
695
|
See [TECH_DOC.md](TECH_DOC.md) for detailed technical documentation.
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{e6data_python_connector-2.3.11rc1 → e6data_python_connector-2.3.12rc1}/gen-py/e6x_vector/ttypes.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{e6data_python_connector-2.3.11rc1 → e6data_python_connector-2.3.12rc1}/test/analyze_38_nines.py
RENAMED
|
File without changes
|
{e6data_python_connector-2.3.11rc1 → e6data_python_connector-2.3.12rc1}/test/analyze_all_cases.py
RENAMED
|
File without changes
|
{e6data_python_connector-2.3.11rc1 → e6data_python_connector-2.3.12rc1}/test/analyze_binary.py
RENAMED
|
File without changes
|
|
File without changes
|
{e6data_python_connector-2.3.11rc1 → e6data_python_connector-2.3.12rc1}/test/analyze_fields.py
RENAMED
|
File without changes
|
{e6data_python_connector-2.3.11rc1 → e6data_python_connector-2.3.12rc1}/test/check_decimal_errors.py
RENAMED
|
File without changes
|
{e6data_python_connector-2.3.11rc1 → e6data_python_connector-2.3.12rc1}/test/cleanup_test_files.py
RENAMED
|
File without changes
|
{e6data_python_connector-2.3.11rc1 → e6data_python_connector-2.3.12rc1}/test/debug_38_nines.py
RENAMED
|
File without changes
|
{e6data_python_connector-2.3.11rc1 → e6data_python_connector-2.3.12rc1}/test/debug_binary.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{e6data_python_connector-2.3.11rc1 → e6data_python_connector-2.3.12rc1}/test/test_38_nines.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{e6data_python_connector-2.3.11rc1 → e6data_python_connector-2.3.12rc1}/test/test_comprehensive.py
RENAMED
|
File without changes
|
{e6data_python_connector-2.3.11rc1 → e6data_python_connector-2.3.12rc1}/test/test_connection_pool.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{e6data_python_connector-2.3.11rc1 → e6data_python_connector-2.3.12rc1}/test/test_known_case.py
RENAMED
|
File without changes
|
{e6data_python_connector-2.3.11rc1 → e6data_python_connector-2.3.12rc1}/test/test_manual_analysis.py
RENAMED
|
File without changes
|
{e6data_python_connector-2.3.11rc1 → e6data_python_connector-2.3.12rc1}/test/test_mock_server.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{e6data_python_connector-2.3.11rc1 → e6data_python_connector-2.3.12rc1}/test/test_specific_binary.py
RENAMED
|
File without changes
|
{e6data_python_connector-2.3.11rc1 → e6data_python_connector-2.3.12rc1}/test/test_strategy.py
RENAMED
|
File without changes
|
{e6data_python_connector-2.3.11rc1 → e6data_python_connector-2.3.12rc1}/test/test_strategy_logic.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{e6data_python_connector-2.3.11rc1 → e6data_python_connector-2.3.12rc1}/test/validate_decimal128.py
RENAMED
|
File without changes
|
|
File without changes
|
{e6data_python_connector-2.3.11rc1 → e6data_python_connector-2.3.12rc1}/test/verify_decimal_fix.py
RENAMED
|
File without changes
|