e6data-python-connector 2.3.11rc2__tar.gz → 2.3.12__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.
Files changed (92) hide show
  1. {e6data_python_connector-2.3.11rc2/e6data_python_connector.egg-info → e6data_python_connector-2.3.12}/PKG-INFO +171 -2
  2. {e6data_python_connector-2.3.11rc2 → e6data_python_connector-2.3.12}/README.md +170 -1
  3. {e6data_python_connector-2.3.11rc2 → e6data_python_connector-2.3.12}/e6data_python_connector/cluster_manager.py +7 -22
  4. {e6data_python_connector-2.3.11rc2 → e6data_python_connector-2.3.12}/e6data_python_connector/common.py +44 -0
  5. e6data_python_connector-2.3.12/e6data_python_connector/constants.py +56 -0
  6. {e6data_python_connector-2.3.11rc2 → e6data_python_connector-2.3.12}/e6data_python_connector/datainputstream.py +55 -26
  7. {e6data_python_connector-2.3.11rc2 → e6data_python_connector-2.3.12}/e6data_python_connector/e6data_grpc.py +100 -21
  8. {e6data_python_connector-2.3.11rc2 → e6data_python_connector-2.3.12/e6data_python_connector.egg-info}/PKG-INFO +171 -2
  9. {e6data_python_connector-2.3.11rc2 → e6data_python_connector-2.3.12}/e6data_python_connector.egg-info/SOURCES.txt +4 -0
  10. {e6data_python_connector-2.3.11rc2/e6data_python_connector → e6data_python_connector-2.3.12/gen-py}/e6x_vector/ttypes.py +26 -2
  11. {e6data_python_connector-2.3.11rc2 → e6data_python_connector-2.3.12}/setup.py +1 -1
  12. e6data_python_connector-2.3.12/test/benchmark_fastbinary_performance.py +303 -0
  13. e6data_python_connector-2.3.12/test/test_code_optimizations.py +322 -0
  14. e6data_python_connector-2.3.12/test/test_optimizations_integration.py +443 -0
  15. e6data_python_connector-2.3.12/test/test_ssl_cert.py +548 -0
  16. e6data_python_connector-2.3.11rc2/e6data_python_connector/constants.py +0 -32
  17. {e6data_python_connector-2.3.11rc2 → e6data_python_connector-2.3.12}/LICENSE +0 -0
  18. {e6data_python_connector-2.3.11rc2 → e6data_python_connector-2.3.12}/MANIFEST.in +0 -0
  19. {e6data_python_connector-2.3.11rc2 → e6data_python_connector-2.3.12}/e6data_python_connector/__init__.py +0 -0
  20. {e6data_python_connector-2.3.11rc2 → e6data_python_connector-2.3.12}/e6data_python_connector/cluster_server/__init__.py +0 -0
  21. {e6data_python_connector-2.3.11rc2 → e6data_python_connector-2.3.12}/e6data_python_connector/cluster_server/cluster_pb2.py +0 -0
  22. {e6data_python_connector-2.3.11rc2 → e6data_python_connector-2.3.12}/e6data_python_connector/cluster_server/cluster_pb2_grpc.py +0 -0
  23. {e6data_python_connector-2.3.11rc2 → e6data_python_connector-2.3.12}/e6data_python_connector/connection_pool.py +0 -0
  24. {e6data_python_connector-2.3.11rc2 → e6data_python_connector-2.3.12}/e6data_python_connector/date_time_utils.py +0 -0
  25. {e6data_python_connector-2.3.11rc2 → e6data_python_connector-2.3.12}/e6data_python_connector/dialect.py +0 -0
  26. {e6data_python_connector-2.3.11rc2 → e6data_python_connector-2.3.12}/e6data_python_connector/e6x_vector/__init__.py +0 -0
  27. {e6data_python_connector-2.3.11rc2 → e6data_python_connector-2.3.12}/e6data_python_connector/e6x_vector/constants.py +0 -0
  28. {e6data_python_connector-2.3.11rc2/gen-py → e6data_python_connector-2.3.12/e6data_python_connector}/e6x_vector/ttypes.py +0 -0
  29. {e6data_python_connector-2.3.11rc2 → e6data_python_connector-2.3.12}/e6data_python_connector/exceptions.py +0 -0
  30. {e6data_python_connector-2.3.11rc2 → e6data_python_connector-2.3.12}/e6data_python_connector/server/QueryEngineService.py +0 -0
  31. {e6data_python_connector-2.3.11rc2 → e6data_python_connector-2.3.12}/e6data_python_connector/server/__init__.py +0 -0
  32. {e6data_python_connector-2.3.11rc2 → e6data_python_connector-2.3.12}/e6data_python_connector/server/constants.py +0 -0
  33. {e6data_python_connector-2.3.11rc2 → e6data_python_connector-2.3.12}/e6data_python_connector/server/e6x_engine_pb2.py +0 -0
  34. {e6data_python_connector-2.3.11rc2 → e6data_python_connector-2.3.12}/e6data_python_connector/server/e6x_engine_pb2.pyi +0 -0
  35. {e6data_python_connector-2.3.11rc2 → e6data_python_connector-2.3.12}/e6data_python_connector/server/e6x_engine_pb2_grpc.py +0 -0
  36. {e6data_python_connector-2.3.11rc2 → e6data_python_connector-2.3.12}/e6data_python_connector/server/ttypes.py +0 -0
  37. {e6data_python_connector-2.3.11rc2 → e6data_python_connector-2.3.12}/e6data_python_connector/strategy.py +0 -0
  38. {e6data_python_connector-2.3.11rc2 → e6data_python_connector-2.3.12}/e6data_python_connector/typeId.py +0 -0
  39. {e6data_python_connector-2.3.11rc2 → e6data_python_connector-2.3.12}/e6data_python_connector.egg-info/dependency_links.txt +0 -0
  40. {e6data_python_connector-2.3.11rc2 → e6data_python_connector-2.3.12}/e6data_python_connector.egg-info/entry_points.txt +0 -0
  41. {e6data_python_connector-2.3.11rc2 → e6data_python_connector-2.3.12}/e6data_python_connector.egg-info/requires.txt +0 -0
  42. {e6data_python_connector-2.3.11rc2 → e6data_python_connector-2.3.12}/e6data_python_connector.egg-info/top_level.txt +0 -0
  43. {e6data_python_connector-2.3.11rc2 → e6data_python_connector-2.3.12}/gen-py/__init__.py +0 -0
  44. {e6data_python_connector-2.3.11rc2 → e6data_python_connector-2.3.12}/gen-py/e6x_vector/__init__.py +0 -0
  45. {e6data_python_connector-2.3.11rc2 → e6data_python_connector-2.3.12}/gen-py/e6x_vector/constants.py +0 -0
  46. {e6data_python_connector-2.3.11rc2 → e6data_python_connector-2.3.12}/pyproject.toml +0 -0
  47. {e6data_python_connector-2.3.11rc2 → e6data_python_connector-2.3.12}/setup.cfg +0 -0
  48. {e6data_python_connector-2.3.11rc2 → e6data_python_connector-2.3.12}/test/__init__.py +0 -0
  49. {e6data_python_connector-2.3.11rc2 → e6data_python_connector-2.3.12}/test/analyze_38_nines.py +0 -0
  50. {e6data_python_connector-2.3.11rc2 → e6data_python_connector-2.3.12}/test/analyze_all_cases.py +0 -0
  51. {e6data_python_connector-2.3.11rc2 → e6data_python_connector-2.3.12}/test/analyze_binary.py +0 -0
  52. {e6data_python_connector-2.3.11rc2 → e6data_python_connector-2.3.12}/test/analyze_correct_value.py +0 -0
  53. {e6data_python_connector-2.3.11rc2 → e6data_python_connector-2.3.12}/test/analyze_fields.py +0 -0
  54. {e6data_python_connector-2.3.11rc2 → e6data_python_connector-2.3.12}/test/check_decimal_errors.py +0 -0
  55. {e6data_python_connector-2.3.11rc2 → e6data_python_connector-2.3.12}/test/cleanup_test_files.py +0 -0
  56. {e6data_python_connector-2.3.11rc2 → e6data_python_connector-2.3.12}/test/debug_38_nines.py +0 -0
  57. {e6data_python_connector-2.3.11rc2 → e6data_python_connector-2.3.12}/test/debug_binary.py +0 -0
  58. {e6data_python_connector-2.3.11rc2 → e6data_python_connector-2.3.12}/test/final_test.py +0 -0
  59. {e6data_python_connector-2.3.11rc2 → e6data_python_connector-2.3.12}/test/move_tests.py +0 -0
  60. {e6data_python_connector-2.3.11rc2 → e6data_python_connector-2.3.12}/test/quick_test.py +0 -0
  61. {e6data_python_connector-2.3.11rc2 → e6data_python_connector-2.3.12}/test/test_38_nines.py +0 -0
  62. {e6data_python_connector-2.3.11rc2 → e6data_python_connector-2.3.12}/test/test_all_decimal128_cases.py +0 -0
  63. {e6data_python_connector-2.3.11rc2 → e6data_python_connector-2.3.12}/test/test_cluster_manager_efficiency.py +0 -0
  64. {e6data_python_connector-2.3.11rc2 → e6data_python_connector-2.3.12}/test/test_cluster_manager_none_strategy.py +0 -0
  65. {e6data_python_connector-2.3.11rc2 → e6data_python_connector-2.3.12}/test/test_cluster_manager_strategy.py +0 -0
  66. {e6data_python_connector-2.3.11rc2 → e6data_python_connector-2.3.12}/test/test_comprehensive.py +0 -0
  67. {e6data_python_connector-2.3.11rc2 → e6data_python_connector-2.3.12}/test/test_connection_pool.py +0 -0
  68. {e6data_python_connector-2.3.11rc2 → e6data_python_connector-2.3.12}/test/test_connection_pool_e2e.py +0 -0
  69. {e6data_python_connector-2.3.11rc2 → e6data_python_connector-2.3.12}/test/test_current_implementation.py +0 -0
  70. {e6data_python_connector-2.3.11rc2 → e6data_python_connector-2.3.12}/test/test_decimal128_binary_parsing.py +0 -0
  71. {e6data_python_connector-2.3.11rc2 → e6data_python_connector-2.3.12}/test/test_decimal128_parsing.py +0 -0
  72. {e6data_python_connector-2.3.11rc2 → e6data_python_connector-2.3.12}/test/test_fix.py +0 -0
  73. {e6data_python_connector-2.3.11rc2 → e6data_python_connector-2.3.12}/test/test_improved_parsing.py +0 -0
  74. {e6data_python_connector-2.3.11rc2 → e6data_python_connector-2.3.12}/test/test_known_case.py +0 -0
  75. {e6data_python_connector-2.3.11rc2 → e6data_python_connector-2.3.12}/test/test_manual_analysis.py +0 -0
  76. {e6data_python_connector-2.3.11rc2 → e6data_python_connector-2.3.12}/test/test_mock_server.py +0 -0
  77. {e6data_python_connector-2.3.11rc2 → e6data_python_connector-2.3.12}/test/test_multiprocessing_fix.py +0 -0
  78. {e6data_python_connector-2.3.11rc2 → e6data_python_connector-2.3.12}/test/test_new_implementation.py +0 -0
  79. {e6data_python_connector-2.3.11rc2 → e6data_python_connector-2.3.12}/test/test_pool_concurrency_simple.py +0 -0
  80. {e6data_python_connector-2.3.11rc2 → e6data_python_connector-2.3.12}/test/test_pool_threading_multiprocessing.py +0 -0
  81. {e6data_python_connector-2.3.11rc2 → e6data_python_connector-2.3.12}/test/test_simple_connection_pool.py +0 -0
  82. {e6data_python_connector-2.3.11rc2 → e6data_python_connector-2.3.12}/test/test_specific_binary.py +0 -0
  83. {e6data_python_connector-2.3.11rc2 → e6data_python_connector-2.3.12}/test/test_strategy.py +0 -0
  84. {e6data_python_connector-2.3.11rc2 → e6data_python_connector-2.3.12}/test/test_strategy_logic.py +0 -0
  85. {e6data_python_connector-2.3.11rc2 → e6data_python_connector-2.3.12}/test/test_strategy_persistence_fix.py +0 -0
  86. {e6data_python_connector-2.3.11rc2 → e6data_python_connector-2.3.12}/test/test_strategy_sharing_fix.py +0 -0
  87. {e6data_python_connector-2.3.11rc2 → e6data_python_connector-2.3.12}/test/test_user_binary_value.py +0 -0
  88. {e6data_python_connector-2.3.11rc2 → e6data_python_connector-2.3.12}/test/tests.py +0 -0
  89. {e6data_python_connector-2.3.11rc2 → e6data_python_connector-2.3.12}/test/tests_grpc.py +0 -0
  90. {e6data_python_connector-2.3.11rc2 → e6data_python_connector-2.3.12}/test/validate_decimal128.py +0 -0
  91. {e6data_python_connector-2.3.11rc2 → e6data_python_connector-2.3.12}/test/validate_implementation.py +0 -0
  92. {e6data_python_connector-2.3.11rc2 → e6data_python_connector-2.3.12}/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.11rc2
3
+ Version: 2.3.12
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
- ![version](https://img.shields.io/badge/version-2.3.11rc2-blue.svg)
42
+ ![version](https://img.shields.io/badge/version-2.3.12-blue.svg)
43
43
 
44
44
  ## Introduction
45
45
 
@@ -122,8 +122,11 @@ The `Connection` class supports the following parameters:
122
122
  | `catalog` | str | No | None | Catalog name |
123
123
  | `cluster_name` | str | No | None | Name of the cluster for cluster-specific operations |
124
124
  | `secure` | bool | No | False | Enable SSL/TLS for secure connections |
125
+ | `ssl_cert` | str/bytes | No | None | Path to CA certificate (PEM) or certificate bytes for HTTPS connections |
125
126
  | `auto_resume` | bool | No | True | Automatically resume cluster if suspended |
126
127
  | `grpc_options` | dict | No | None | Additional gRPC configuration options |
128
+ | `debug` | bool | No | False | Enable debug logging for troubleshooting |
129
+ | `require_fastbinary` | bool | No | True | Require fastbinary module for Thrift deserialization. Set to False to use pure Python implementation if system dependencies cannot be installed |
127
130
 
128
131
  #### Secure Connection Example
129
132
 
@@ -157,6 +160,117 @@ conn = Connection(
157
160
  )
158
161
  ```
159
162
 
163
+ #### HTTPS Connection with HAProxy
164
+
165
+ When connecting through HAProxy with HTTPS, you can provide a custom CA certificate for secure connections. The `ssl_cert` parameter accepts either a file path to a PEM certificate or the certificate content as bytes.
166
+
167
+ **Using a CA certificate file path:**
168
+
169
+ ```python
170
+ conn = Connection(
171
+ host=host,
172
+ port=443,
173
+ username=username,
174
+ password=password,
175
+ database=database,
176
+ secure=True,
177
+ ssl_cert='/path/to/ca-cert.pem' # Path to your CA certificate
178
+ )
179
+ ```
180
+
181
+ **Reading certificate content as bytes:**
182
+
183
+ ```python
184
+ # Read certificate file and pass as bytes
185
+ with open('/path/to/ca-cert.pem', 'rb') as cert_file:
186
+ cert_data = cert_file.read()
187
+
188
+ conn = Connection(
189
+ host=host,
190
+ port=443,
191
+ username=username,
192
+ password=password,
193
+ database=database,
194
+ secure=True,
195
+ ssl_cert=cert_data # Certificate content as bytes
196
+ )
197
+ ```
198
+
199
+ **Using system CA bundle for publicly signed certificates:**
200
+
201
+ ```python
202
+ # When ssl_cert is None, system default CA bundle is used
203
+ conn = Connection(
204
+ host=host,
205
+ port=443,
206
+ username=username,
207
+ password=password,
208
+ database=database,
209
+ secure=True # Uses system CA bundle by default
210
+ )
211
+ ```
212
+
213
+ **Connection pooling with custom CA certificate:**
214
+
215
+ ```python
216
+ pool = ConnectionPool(
217
+ min_size=2,
218
+ max_size=10,
219
+ host=host,
220
+ port=443,
221
+ username=username,
222
+ password=password,
223
+ database=database,
224
+ secure=True,
225
+ ssl_cert='/path/to/ca-cert.pem' # Custom CA certificate for pool connections
226
+ )
227
+ ```
228
+
229
+ #### Handling Missing System Dependencies
230
+
231
+ The e6data connector uses the `fastbinary` module (from Apache Thrift) for optimal performance when deserializing data. This module requires system-level dependencies (`python3-devel` and `gcc-c++`) to be installed.
232
+
233
+ **Default Behavior (Recommended):**
234
+ By default, the connector requires `fastbinary` to be available. If it's not found, the connection will fail immediately with a clear error message:
235
+
236
+ ```python
237
+ conn = Connection(
238
+ host=host,
239
+ port=port,
240
+ username=username,
241
+ password=password,
242
+ database=database
243
+ )
244
+ # Raises exception if fastbinary is not available
245
+ ```
246
+
247
+ **Fallback to Pure Python:**
248
+ If you cannot install system dependencies (e.g., in restricted environments, serverless platforms, or containers without build tools), you can disable the `fastbinary` requirement. The connector will fall back to a pure Python implementation with a performance penalty:
249
+
250
+ ```python
251
+ conn = Connection(
252
+ host=host,
253
+ port=port,
254
+ username=username,
255
+ password=password,
256
+ database=database,
257
+ require_fastbinary=False # Allow operation without fastbinary
258
+ )
259
+ # Logs warning but continues with pure Python implementation
260
+ ```
261
+
262
+ **When to use `require_fastbinary=False`:**
263
+ - Running in AWS Lambda or other serverless environments
264
+ - Docker containers built without compilation tools
265
+ - Restricted environments where system packages cannot be installed
266
+ - Development/testing environments where performance is not critical
267
+
268
+ **Performance Impact:**
269
+ - With `fastbinary`: Optimal performance for data deserialization
270
+ - Without `fastbinary` (pure Python): ~2-3x slower deserialization, but otherwise fully functional
271
+
272
+ **Note:** It's strongly recommended to install system dependencies when possible for best performance. The `require_fastbinary=False` option should only be used when system dependencies cannot be installed.
273
+
160
274
  ### Perform a Queries & Get Results
161
275
 
162
276
  ```python
@@ -637,4 +751,59 @@ conn.close()
637
751
  - Graceful connection recovery and retry logic
638
752
  - Blue-green deployment support with automatic failover
639
753
 
754
+ ## Debugging and Troubleshooting
755
+
756
+ ### Enable Debug Mode
757
+
758
+ Enable comprehensive debugging to troubleshoot connection and query issues:
759
+
760
+ ```python
761
+ from e6data_python_connector import Connection
762
+
763
+ conn = Connection(
764
+ host=host,
765
+ port=port,
766
+ username=username,
767
+ password=password,
768
+ database=database,
769
+ debug=True # Enable debug logging
770
+ )
771
+ ```
772
+
773
+ When `debug=True`, the following features are enabled:
774
+ - Python logging at DEBUG level for all operations
775
+ - Blue-green strategy transition logging
776
+ - Connection lifecycle logging
777
+ - Query execution detailed logging
778
+
779
+ ### gRPC Network Tracing
780
+
781
+ For low-level gRPC network debugging (HTTP/2 frames, TCP events), set environment variables **before** running your Python script:
782
+
783
+ ```bash
784
+ # Enable gRPC network tracing
785
+ export GRPC_VERBOSITY=DEBUG
786
+ export GRPC_TRACE=client_channel,http2
787
+
788
+ # For comprehensive tracing
789
+ export GRPC_TRACE=api,call_error,channel,client_channel,connectivity_state,http,http2_stream,tcp,transport_security
790
+
791
+ # Run your script
792
+ python your_script.py
793
+ ```
794
+
795
+ **Note**: These environment variables must be set before Python starts, as the gRPC C++ core reads them at module import time.
796
+
797
+ ### Common Issues and Solutions
798
+
799
+ | Issue | Solution |
800
+ |-------|----------|
801
+ | Connection timeout | Check network connectivity, firewall rules, and ensure port 80/443 is open |
802
+ | Authentication failure | Verify username (email) and access token are correct |
803
+ | 503 Service Unavailable | Cluster may be suspended; enable `auto_resume=True` |
804
+ | 456 Strategy Error | Automatic blue-green failover will handle this |
805
+ | Memory issues with large results | Use `fetchall_buffer()` instead of `fetchall()` |
806
+ | gRPC message size errors | Configure `grpc_options` with appropriate message size limits |
807
+ | fastbinary import error | Install system dependencies (`python3-devel`, `gcc-c++`) or set `require_fastbinary=False` |
808
+
640
809
  See [TECH_DOC.md](TECH_DOC.md) for detailed technical documentation.
@@ -1,6 +1,6 @@
1
1
  # e6data Python Connector
2
2
 
3
- ![version](https://img.shields.io/badge/version-2.3.11rc2-blue.svg)
3
+ ![version](https://img.shields.io/badge/version-2.3.12-blue.svg)
4
4
 
5
5
  ## Introduction
6
6
 
@@ -83,8 +83,11 @@ The `Connection` class supports the following parameters:
83
83
  | `catalog` | str | No | None | Catalog name |
84
84
  | `cluster_name` | str | No | None | Name of the cluster for cluster-specific operations |
85
85
  | `secure` | bool | No | False | Enable SSL/TLS for secure connections |
86
+ | `ssl_cert` | str/bytes | No | None | Path to CA certificate (PEM) or certificate bytes for HTTPS connections |
86
87
  | `auto_resume` | bool | No | True | Automatically resume cluster if suspended |
87
88
  | `grpc_options` | dict | No | None | Additional gRPC configuration options |
89
+ | `debug` | bool | No | False | Enable debug logging for troubleshooting |
90
+ | `require_fastbinary` | bool | No | True | Require fastbinary module for Thrift deserialization. Set to False to use pure Python implementation if system dependencies cannot be installed |
88
91
 
89
92
  #### Secure Connection Example
90
93
 
@@ -118,6 +121,117 @@ conn = Connection(
118
121
  )
119
122
  ```
120
123
 
124
+ #### HTTPS Connection with HAProxy
125
+
126
+ When connecting through HAProxy with HTTPS, you can provide a custom CA certificate for secure connections. The `ssl_cert` parameter accepts either a file path to a PEM certificate or the certificate content as bytes.
127
+
128
+ **Using a CA certificate file path:**
129
+
130
+ ```python
131
+ conn = Connection(
132
+ host=host,
133
+ port=443,
134
+ username=username,
135
+ password=password,
136
+ database=database,
137
+ secure=True,
138
+ ssl_cert='/path/to/ca-cert.pem' # Path to your CA certificate
139
+ )
140
+ ```
141
+
142
+ **Reading certificate content as bytes:**
143
+
144
+ ```python
145
+ # Read certificate file and pass as bytes
146
+ with open('/path/to/ca-cert.pem', 'rb') as cert_file:
147
+ cert_data = cert_file.read()
148
+
149
+ conn = Connection(
150
+ host=host,
151
+ port=443,
152
+ username=username,
153
+ password=password,
154
+ database=database,
155
+ secure=True,
156
+ ssl_cert=cert_data # Certificate content as bytes
157
+ )
158
+ ```
159
+
160
+ **Using system CA bundle for publicly signed certificates:**
161
+
162
+ ```python
163
+ # When ssl_cert is None, system default CA bundle is used
164
+ conn = Connection(
165
+ host=host,
166
+ port=443,
167
+ username=username,
168
+ password=password,
169
+ database=database,
170
+ secure=True # Uses system CA bundle by default
171
+ )
172
+ ```
173
+
174
+ **Connection pooling with custom CA certificate:**
175
+
176
+ ```python
177
+ pool = ConnectionPool(
178
+ min_size=2,
179
+ max_size=10,
180
+ host=host,
181
+ port=443,
182
+ username=username,
183
+ password=password,
184
+ database=database,
185
+ secure=True,
186
+ ssl_cert='/path/to/ca-cert.pem' # Custom CA certificate for pool connections
187
+ )
188
+ ```
189
+
190
+ #### Handling Missing System Dependencies
191
+
192
+ The e6data connector uses the `fastbinary` module (from Apache Thrift) for optimal performance when deserializing data. This module requires system-level dependencies (`python3-devel` and `gcc-c++`) to be installed.
193
+
194
+ **Default Behavior (Recommended):**
195
+ By default, the connector requires `fastbinary` to be available. If it's not found, the connection will fail immediately with a clear error message:
196
+
197
+ ```python
198
+ conn = Connection(
199
+ host=host,
200
+ port=port,
201
+ username=username,
202
+ password=password,
203
+ database=database
204
+ )
205
+ # Raises exception if fastbinary is not available
206
+ ```
207
+
208
+ **Fallback to Pure Python:**
209
+ If you cannot install system dependencies (e.g., in restricted environments, serverless platforms, or containers without build tools), you can disable the `fastbinary` requirement. The connector will fall back to a pure Python implementation with a performance penalty:
210
+
211
+ ```python
212
+ conn = Connection(
213
+ host=host,
214
+ port=port,
215
+ username=username,
216
+ password=password,
217
+ database=database,
218
+ require_fastbinary=False # Allow operation without fastbinary
219
+ )
220
+ # Logs warning but continues with pure Python implementation
221
+ ```
222
+
223
+ **When to use `require_fastbinary=False`:**
224
+ - Running in AWS Lambda or other serverless environments
225
+ - Docker containers built without compilation tools
226
+ - Restricted environments where system packages cannot be installed
227
+ - Development/testing environments where performance is not critical
228
+
229
+ **Performance Impact:**
230
+ - With `fastbinary`: Optimal performance for data deserialization
231
+ - Without `fastbinary` (pure Python): ~2-3x slower deserialization, but otherwise fully functional
232
+
233
+ **Note:** It's strongly recommended to install system dependencies when possible for best performance. The `require_fastbinary=False` option should only be used when system dependencies cannot be installed.
234
+
121
235
  ### Perform a Queries & Get Results
122
236
 
123
237
  ```python
@@ -598,4 +712,59 @@ conn.close()
598
712
  - Graceful connection recovery and retry logic
599
713
  - Blue-green deployment support with automatic failover
600
714
 
715
+ ## Debugging and Troubleshooting
716
+
717
+ ### Enable Debug Mode
718
+
719
+ Enable comprehensive debugging to troubleshoot connection and query issues:
720
+
721
+ ```python
722
+ from e6data_python_connector import Connection
723
+
724
+ conn = Connection(
725
+ host=host,
726
+ port=port,
727
+ username=username,
728
+ password=password,
729
+ database=database,
730
+ debug=True # Enable debug logging
731
+ )
732
+ ```
733
+
734
+ When `debug=True`, the following features are enabled:
735
+ - Python logging at DEBUG level for all operations
736
+ - Blue-green strategy transition logging
737
+ - Connection lifecycle logging
738
+ - Query execution detailed logging
739
+
740
+ ### gRPC Network Tracing
741
+
742
+ For low-level gRPC network debugging (HTTP/2 frames, TCP events), set environment variables **before** running your Python script:
743
+
744
+ ```bash
745
+ # Enable gRPC network tracing
746
+ export GRPC_VERBOSITY=DEBUG
747
+ export GRPC_TRACE=client_channel,http2
748
+
749
+ # For comprehensive tracing
750
+ export GRPC_TRACE=api,call_error,channel,client_channel,connectivity_state,http,http2_stream,tcp,transport_security
751
+
752
+ # Run your script
753
+ python your_script.py
754
+ ```
755
+
756
+ **Note**: These environment variables must be set before Python starts, as the gRPC C++ core reads them at module import time.
757
+
758
+ ### Common Issues and Solutions
759
+
760
+ | Issue | Solution |
761
+ |-------|----------|
762
+ | Connection timeout | Check network connectivity, firewall rules, and ensure port 80/443 is open |
763
+ | Authentication failure | Verify username (email) and access token are correct |
764
+ | 503 Service Unavailable | Cluster may be suspended; enable `auto_resume=True` |
765
+ | 456 Strategy Error | Automatic blue-green failover will handle this |
766
+ | Memory issues with large results | Use `fetchall_buffer()` instead of `fetchall()` |
767
+ | gRPC message size errors | Configure `grpc_options` with appropriate message size limits |
768
+ | fastbinary import error | Install system dependencies (`python3-devel`, `gcc-c++`) or set `require_fastbinary=False` |
769
+
601
770
  See [TECH_DOC.md](TECH_DOC.md) for detailed technical documentation.
@@ -9,27 +9,9 @@ import multiprocessing
9
9
 
10
10
  logger = logging.getLogger(__name__)
11
11
 
12
+ from e6data_python_connector.common import get_ssl_credentials
12
13
  from e6data_python_connector.strategy import _get_active_strategy, _set_active_strategy, _set_pending_strategy, \
13
- _get_grpc_header as _get_strategy_header
14
-
15
-
16
- def _get_grpc_header(engine_ip=None, cluster=None, strategy=None):
17
- """
18
- Generate gRPC metadata headers for the request.
19
-
20
- This function creates a list of metadata headers to be used in gRPC requests.
21
- It includes optional headers for the engine IP, cluster UUID, and deployment strategy.
22
-
23
- Args:
24
- engine_ip (str, optional): The IP address of the engine. Defaults to None.
25
- cluster (str, optional): The UUID of the cluster. Defaults to None.
26
- strategy (str, optional): The deployment strategy (blue/green). Defaults to None.
27
-
28
- Returns:
29
- list: A list of tuples representing the gRPC metadata headers.
30
- """
31
- # Use the strategy module's implementation
32
- return _get_strategy_header(engine_ip=engine_ip, cluster=cluster, strategy=strategy)
14
+ _get_grpc_header
33
15
 
34
16
 
35
17
  class _StatusLock:
@@ -140,7 +122,7 @@ class ClusterManager:
140
122
  """
141
123
 
142
124
  def __init__(self, host: str, port: int, user: str, password: str, secure_channel: bool = False, timeout=60 * 5,
143
- cluster_uuid=None, grpc_options=None, debug=False):
125
+ cluster_uuid=None, grpc_options=None, debug=False, ssl_cert=None):
144
126
  """
145
127
  Initializes a new instance of the ClusterManager class.
146
128
 
@@ -156,6 +138,8 @@ class ClusterManager:
156
138
  cluster_uuid (str, optional): The unique identifier for the target cluster;
157
139
  defaults to None.
158
140
  debug (bool, optional): Enable debug logging; defaults to False.
141
+ ssl_cert (str or bytes, optional): Path to CA certificate file (PEM format) or
142
+ certificate content as bytes for secure connections; defaults to None.
159
143
  """
160
144
 
161
145
  self._host = host
@@ -169,6 +153,7 @@ class ClusterManager:
169
153
  if grpc_options is None:
170
154
  self._grpc_options = dict()
171
155
  self._debug = debug
156
+ self._ssl_cert = ssl_cert
172
157
 
173
158
  @property
174
159
  def _get_connection(self):
@@ -184,7 +169,7 @@ class ClusterManager:
184
169
  self._channel = grpc.secure_channel(
185
170
  target='{}:{}'.format(self._host, self._port),
186
171
  options=self._grpc_options,
187
- credentials=grpc.ssl_channel_credentials()
172
+ credentials=get_ssl_credentials(self._ssl_cert)
188
173
  )
189
174
  else:
190
175
  self._channel = grpc.insecure_channel(
@@ -15,6 +15,7 @@ from builtins import int
15
15
  from builtins import object
16
16
  from builtins import str
17
17
 
18
+ import grpc
18
19
  from future.utils import with_metaclass
19
20
  from past.builtins import basestring
20
21
 
@@ -267,3 +268,46 @@ class UniversalSet(object):
267
268
 
268
269
  def __contains__(self, item):
269
270
  return True
271
+
272
+
273
+ def get_ssl_credentials(ssl_cert):
274
+ """
275
+ Get SSL credentials for secure gRPC channel.
276
+
277
+ Handles three scenarios:
278
+ 1. ssl_cert is a string (file path): Read the PEM certificate from the file
279
+ 2. ssl_cert is bytes: Use the certificate content directly
280
+ 3. ssl_cert is None: Use system default CA bundle
281
+
282
+ Parameters:
283
+ ssl_cert (str or bytes or None): SSL certificate as file path, bytes, or None
284
+
285
+ Returns:
286
+ grpc.ChannelCredentials: SSL credentials for gRPC channel
287
+
288
+ Raises:
289
+ FileNotFoundError: If ssl_cert is a file path but the file doesn't exist
290
+ IOError: If ssl_cert file cannot be read
291
+ """
292
+ if ssl_cert is None:
293
+ # Use system default CA bundle
294
+ return grpc.ssl_channel_credentials()
295
+ elif isinstance(ssl_cert, str):
296
+ # ssl_cert is a file path - read the certificate from file
297
+ try:
298
+ with open(ssl_cert, 'rb') as cert_file:
299
+ root_ca_cert = cert_file.read()
300
+ return grpc.ssl_channel_credentials(root_certificates=root_ca_cert)
301
+ except FileNotFoundError:
302
+ _logger.error("SSL certificate file not found: {}".format(ssl_cert))
303
+ raise
304
+ except IOError as e:
305
+ _logger.error("Failed to read SSL certificate file {}: {}".format(ssl_cert, e))
306
+ raise
307
+ elif isinstance(ssl_cert, bytes):
308
+ # ssl_cert is certificate content as bytes
309
+ return grpc.ssl_channel_credentials(root_certificates=ssl_cert)
310
+ else:
311
+ # Invalid type - fall back to system default CA bundle with warning
312
+ _logger.warning("Invalid ssl_cert type: {}. Using system default CA bundle.".format(type(ssl_cert)))
313
+ return grpc.ssl_channel_credentials()
@@ -0,0 +1,56 @@
1
+ import pytz
2
+
3
+ # Type definitions
4
+ PRIMITIVE_TYPES = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 15, 16, 17, 18, 19, 20, 21}
5
+ COMPLEX_TYPES = {10, 11, 12, 13, 14}
6
+ COLLECTION_TYPES = {10, 11}
7
+ TYPE_NAMES = {
8
+ 0: "BOOLEAN",
9
+ 1: "TINYINT",
10
+ 2: "SMALLINT",
11
+ 3: "INT",
12
+ 4: "BIGINT",
13
+ 5: "FLOAT",
14
+ 6: "DOUBLE",
15
+ 7: "STRING",
16
+ 8: "TIMESTAMP",
17
+ 9: "BINARY",
18
+ 10: "ARRAY",
19
+ 11: "MAP",
20
+ 12: "STRUCT",
21
+ 13: "UNIONTYPE",
22
+ 15: "DECIMAL",
23
+ 16: "NULL",
24
+ 17: "DATE",
25
+ 18: "VARCHAR",
26
+ 19: "CHAR",
27
+ 20: "INTERVAL_YEAR_MONTH",
28
+ 21: "INTERVAL_DAY_TIME",
29
+ }
30
+ CHARACTER_MAXIMUM_LENGTH = "characterMaximumLength"
31
+ PRECISION = "precision"
32
+ SCALE = "scale"
33
+ ZONE = pytz.timezone('UTC')
34
+
35
+ # Retry and timeout constants
36
+ MAX_RETRY_ATTEMPTS = 5
37
+ RETRY_SLEEP_SECONDS = 0.2
38
+ STRATEGY_CACHE_TIMEOUT_SECONDS = 300 # 5 minutes
39
+ DEFAULT_GRPC_PREPARE_TIMEOUT_SECONDS = 600 # 10 minutes
40
+ DEFAULT_AUTO_RESUME_TIMEOUT_SECONDS = 300 # 5 minutes
41
+ CLUSTER_STATUS_CHECK_SLEEP_SECONDS = 5
42
+ LOCK_TIMEOUT_MS = 500
43
+
44
+ # Connection pool constants
45
+ POOL_GET_TIMEOUT_SECONDS = 0.1
46
+ POOL_RETRY_SLEEP_SECONDS = 0.1
47
+
48
+ # Blue-green deployment strategy constants
49
+ STRATEGY_BLUE = 'blue'
50
+ STRATEGY_GREEN = 'green'
51
+ VALID_STRATEGIES = {STRATEGY_BLUE, STRATEGY_GREEN}
52
+
53
+ # gRPC error codes and messages
54
+ GRPC_ERROR_STRATEGY_MISMATCH = 'status: 456'
55
+ GRPC_ERROR_SERVICE_UNAVAILABLE = 'status: 503'
56
+ GRPC_ERROR_ACCESS_DENIED = 'Access denied'