e6data-python-connector 2.3.9rc8__tar.gz → 2.3.9rc10__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.9rc8/e6data_python_connector.egg-info → e6data_python_connector-2.3.9rc10}/PKG-INFO +2 -2
- {e6data_python_connector-2.3.9rc8 → e6data_python_connector-2.3.9rc10}/README.md +1 -1
- {e6data_python_connector-2.3.9rc8 → e6data_python_connector-2.3.9rc10}/e6data_python_connector/cluster_manager.py +1 -1
- {e6data_python_connector-2.3.9rc8 → e6data_python_connector-2.3.9rc10}/e6data_python_connector/dialect.py +3 -1
- {e6data_python_connector-2.3.9rc8 → e6data_python_connector-2.3.9rc10}/e6data_python_connector/e6data_grpc.py +73 -36
- {e6data_python_connector-2.3.9rc8 → e6data_python_connector-2.3.9rc10}/e6data_python_connector/strategy.py +1 -18
- {e6data_python_connector-2.3.9rc8 → e6data_python_connector-2.3.9rc10/e6data_python_connector.egg-info}/PKG-INFO +2 -2
- {e6data_python_connector-2.3.9rc8 → e6data_python_connector-2.3.9rc10}/setup.py +1 -1
- {e6data_python_connector-2.3.9rc8 → e6data_python_connector-2.3.9rc10}/LICENSE +0 -0
- {e6data_python_connector-2.3.9rc8 → e6data_python_connector-2.3.9rc10}/MANIFEST.in +0 -0
- {e6data_python_connector-2.3.9rc8 → e6data_python_connector-2.3.9rc10}/e6data_python_connector/__init__.py +0 -0
- {e6data_python_connector-2.3.9rc8 → e6data_python_connector-2.3.9rc10}/e6data_python_connector/cluster_server/__init__.py +0 -0
- {e6data_python_connector-2.3.9rc8 → e6data_python_connector-2.3.9rc10}/e6data_python_connector/cluster_server/cluster_pb2.py +0 -0
- {e6data_python_connector-2.3.9rc8 → e6data_python_connector-2.3.9rc10}/e6data_python_connector/cluster_server/cluster_pb2_grpc.py +0 -0
- {e6data_python_connector-2.3.9rc8 → e6data_python_connector-2.3.9rc10}/e6data_python_connector/common.py +0 -0
- {e6data_python_connector-2.3.9rc8 → e6data_python_connector-2.3.9rc10}/e6data_python_connector/constants.py +0 -0
- {e6data_python_connector-2.3.9rc8 → e6data_python_connector-2.3.9rc10}/e6data_python_connector/datainputstream.py +0 -0
- {e6data_python_connector-2.3.9rc8 → e6data_python_connector-2.3.9rc10}/e6data_python_connector/date_time_utils.py +0 -0
- {e6data_python_connector-2.3.9rc8 → e6data_python_connector-2.3.9rc10}/e6data_python_connector/e6x_vector/__init__.py +0 -0
- {e6data_python_connector-2.3.9rc8 → e6data_python_connector-2.3.9rc10}/e6data_python_connector/e6x_vector/constants.py +0 -0
- {e6data_python_connector-2.3.9rc8 → e6data_python_connector-2.3.9rc10}/e6data_python_connector/e6x_vector/ttypes.py +0 -0
- {e6data_python_connector-2.3.9rc8 → e6data_python_connector-2.3.9rc10}/e6data_python_connector/exceptions.py +0 -0
- {e6data_python_connector-2.3.9rc8 → e6data_python_connector-2.3.9rc10}/e6data_python_connector/server/QueryEngineService.py +0 -0
- {e6data_python_connector-2.3.9rc8 → e6data_python_connector-2.3.9rc10}/e6data_python_connector/server/__init__.py +0 -0
- {e6data_python_connector-2.3.9rc8 → e6data_python_connector-2.3.9rc10}/e6data_python_connector/server/constants.py +0 -0
- {e6data_python_connector-2.3.9rc8 → e6data_python_connector-2.3.9rc10}/e6data_python_connector/server/e6x_engine_pb2.py +0 -0
- {e6data_python_connector-2.3.9rc8 → e6data_python_connector-2.3.9rc10}/e6data_python_connector/server/e6x_engine_pb2.pyi +0 -0
- {e6data_python_connector-2.3.9rc8 → e6data_python_connector-2.3.9rc10}/e6data_python_connector/server/e6x_engine_pb2_grpc.py +0 -0
- {e6data_python_connector-2.3.9rc8 → e6data_python_connector-2.3.9rc10}/e6data_python_connector/server/ttypes.py +0 -0
- {e6data_python_connector-2.3.9rc8 → e6data_python_connector-2.3.9rc10}/e6data_python_connector/typeId.py +0 -0
- {e6data_python_connector-2.3.9rc8 → e6data_python_connector-2.3.9rc10}/e6data_python_connector.egg-info/SOURCES.txt +0 -0
- {e6data_python_connector-2.3.9rc8 → e6data_python_connector-2.3.9rc10}/e6data_python_connector.egg-info/dependency_links.txt +0 -0
- {e6data_python_connector-2.3.9rc8 → e6data_python_connector-2.3.9rc10}/e6data_python_connector.egg-info/entry_points.txt +0 -0
- {e6data_python_connector-2.3.9rc8 → e6data_python_connector-2.3.9rc10}/e6data_python_connector.egg-info/requires.txt +0 -0
- {e6data_python_connector-2.3.9rc8 → e6data_python_connector-2.3.9rc10}/e6data_python_connector.egg-info/top_level.txt +0 -0
- {e6data_python_connector-2.3.9rc8 → e6data_python_connector-2.3.9rc10}/pyproject.toml +0 -0
- {e6data_python_connector-2.3.9rc8 → e6data_python_connector-2.3.9rc10}/setup.cfg +0 -0
- {e6data_python_connector-2.3.9rc8 → e6data_python_connector-2.3.9rc10}/test/__init__.py +0 -0
- {e6data_python_connector-2.3.9rc8 → e6data_python_connector-2.3.9rc10}/test/analyze_38_nines.py +0 -0
- {e6data_python_connector-2.3.9rc8 → e6data_python_connector-2.3.9rc10}/test/analyze_all_cases.py +0 -0
- {e6data_python_connector-2.3.9rc8 → e6data_python_connector-2.3.9rc10}/test/analyze_binary.py +0 -0
- {e6data_python_connector-2.3.9rc8 → e6data_python_connector-2.3.9rc10}/test/analyze_correct_value.py +0 -0
- {e6data_python_connector-2.3.9rc8 → e6data_python_connector-2.3.9rc10}/test/analyze_fields.py +0 -0
- {e6data_python_connector-2.3.9rc8 → e6data_python_connector-2.3.9rc10}/test/check_decimal_errors.py +0 -0
- {e6data_python_connector-2.3.9rc8 → e6data_python_connector-2.3.9rc10}/test/cleanup_test_files.py +0 -0
- {e6data_python_connector-2.3.9rc8 → e6data_python_connector-2.3.9rc10}/test/debug_38_nines.py +0 -0
- {e6data_python_connector-2.3.9rc8 → e6data_python_connector-2.3.9rc10}/test/debug_binary.py +0 -0
- {e6data_python_connector-2.3.9rc8 → e6data_python_connector-2.3.9rc10}/test/final_test.py +0 -0
- {e6data_python_connector-2.3.9rc8 → e6data_python_connector-2.3.9rc10}/test/move_tests.py +0 -0
- {e6data_python_connector-2.3.9rc8 → e6data_python_connector-2.3.9rc10}/test/quick_test.py +0 -0
- {e6data_python_connector-2.3.9rc8 → e6data_python_connector-2.3.9rc10}/test/test_38_nines.py +0 -0
- {e6data_python_connector-2.3.9rc8 → e6data_python_connector-2.3.9rc10}/test/test_all_decimal128_cases.py +0 -0
- {e6data_python_connector-2.3.9rc8 → e6data_python_connector-2.3.9rc10}/test/test_cluster_manager_efficiency.py +0 -0
- {e6data_python_connector-2.3.9rc8 → e6data_python_connector-2.3.9rc10}/test/test_cluster_manager_none_strategy.py +0 -0
- {e6data_python_connector-2.3.9rc8 → e6data_python_connector-2.3.9rc10}/test/test_cluster_manager_strategy.py +0 -0
- {e6data_python_connector-2.3.9rc8 → e6data_python_connector-2.3.9rc10}/test/test_comprehensive.py +0 -0
- {e6data_python_connector-2.3.9rc8 → e6data_python_connector-2.3.9rc10}/test/test_current_implementation.py +0 -0
- {e6data_python_connector-2.3.9rc8 → e6data_python_connector-2.3.9rc10}/test/test_decimal128_binary_parsing.py +0 -0
- {e6data_python_connector-2.3.9rc8 → e6data_python_connector-2.3.9rc10}/test/test_decimal128_parsing.py +0 -0
- {e6data_python_connector-2.3.9rc8 → e6data_python_connector-2.3.9rc10}/test/test_fix.py +0 -0
- {e6data_python_connector-2.3.9rc8 → e6data_python_connector-2.3.9rc10}/test/test_improved_parsing.py +0 -0
- {e6data_python_connector-2.3.9rc8 → e6data_python_connector-2.3.9rc10}/test/test_known_case.py +0 -0
- {e6data_python_connector-2.3.9rc8 → e6data_python_connector-2.3.9rc10}/test/test_manual_analysis.py +0 -0
- {e6data_python_connector-2.3.9rc8 → e6data_python_connector-2.3.9rc10}/test/test_mock_server.py +0 -0
- {e6data_python_connector-2.3.9rc8 → e6data_python_connector-2.3.9rc10}/test/test_multiprocessing_fix.py +0 -0
- {e6data_python_connector-2.3.9rc8 → e6data_python_connector-2.3.9rc10}/test/test_new_implementation.py +0 -0
- {e6data_python_connector-2.3.9rc8 → e6data_python_connector-2.3.9rc10}/test/test_specific_binary.py +0 -0
- {e6data_python_connector-2.3.9rc8 → e6data_python_connector-2.3.9rc10}/test/test_strategy.py +0 -0
- {e6data_python_connector-2.3.9rc8 → e6data_python_connector-2.3.9rc10}/test/test_strategy_logic.py +0 -0
- {e6data_python_connector-2.3.9rc8 → e6data_python_connector-2.3.9rc10}/test/test_strategy_persistence_fix.py +0 -0
- {e6data_python_connector-2.3.9rc8 → e6data_python_connector-2.3.9rc10}/test/test_strategy_sharing_fix.py +0 -0
- {e6data_python_connector-2.3.9rc8 → e6data_python_connector-2.3.9rc10}/test/test_user_binary_value.py +0 -0
- {e6data_python_connector-2.3.9rc8 → e6data_python_connector-2.3.9rc10}/test/tests.py +0 -0
- {e6data_python_connector-2.3.9rc8 → e6data_python_connector-2.3.9rc10}/test/tests_grpc.py +0 -0
- {e6data_python_connector-2.3.9rc8 → e6data_python_connector-2.3.9rc10}/test/validate_decimal128.py +0 -0
- {e6data_python_connector-2.3.9rc8 → e6data_python_connector-2.3.9rc10}/test/validate_implementation.py +0 -0
- {e6data_python_connector-2.3.9rc8 → e6data_python_connector-2.3.9rc10}/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.9rc10
|
|
4
4
|
Summary: Client for the e6data distributed SQL Engine.
|
|
5
5
|
Home-page: https://github.com/e6x-labs/e6data-python-connector
|
|
6
6
|
Author: e6data, Inc.
|
|
@@ -38,7 +38,7 @@ Dynamic: summary
|
|
|
38
38
|
|
|
39
39
|
# e6data Python Connector
|
|
40
40
|
|
|
41
|
-

|
|
42
42
|
|
|
43
43
|
## Introduction
|
|
44
44
|
|
|
@@ -365,7 +365,7 @@ class ClusterManager:
|
|
|
365
365
|
# Retrieve the current cluster status with strategy header
|
|
366
366
|
try:
|
|
367
367
|
current_status = self._try_cluster_request("status")
|
|
368
|
-
except _InactiveRpcError:
|
|
368
|
+
except _InactiveRpcError as e:
|
|
369
369
|
return False
|
|
370
370
|
if current_status.status == 'suspended':
|
|
371
371
|
# Send the resume request with strategy header
|
|
@@ -229,6 +229,7 @@ class E6dataDialect(default.DefaultDialect):
|
|
|
229
229
|
self.cluster_name = url.query.get("cluster-name")
|
|
230
230
|
self.secure = url.query.get("secure") == "true"
|
|
231
231
|
self.auto_resume = url.query.get("auto-resume", "true") == "true" # default to True
|
|
232
|
+
self.debug = url.query.get("debug", "false") == "true" # default to True
|
|
232
233
|
if not self.catalog_name:
|
|
233
234
|
raise Exception('Please specify catalog in query parameter.')
|
|
234
235
|
|
|
@@ -250,7 +251,8 @@ class E6dataDialect(default.DefaultDialect):
|
|
|
250
251
|
"cluster_name": self.cluster_name,
|
|
251
252
|
'secure': self.secure,
|
|
252
253
|
'auto_resume': self.auto_resume,
|
|
253
|
-
'grpc_options': grpc_options
|
|
254
|
+
'grpc_options': grpc_options,
|
|
255
|
+
'debug': self.debug
|
|
254
256
|
}
|
|
255
257
|
return [], kwargs
|
|
256
258
|
|
|
@@ -128,26 +128,28 @@ _local_strategy_cache = {
|
|
|
128
128
|
# Strategy cache timeout in seconds (5 minutes)
|
|
129
129
|
STRATEGY_CACHE_TIMEOUT = 300
|
|
130
130
|
|
|
131
|
+
# Global set to track debug-enabled connections
|
|
132
|
+
_debug_connections = set()
|
|
133
|
+
|
|
134
|
+
def _strategy_debug_log(message):
|
|
135
|
+
"""Log strategy debug messages if any connection has debug enabled."""
|
|
136
|
+
if _debug_connections:
|
|
137
|
+
print(f"[E6DATA_STRATEGY_DEBUG] {time.strftime('%Y-%m-%d %H:%M:%S')} - {message}")
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
def _get_normalised_strategy_with_validate(strategy):
|
|
141
|
+
if not strategy:
|
|
142
|
+
return False
|
|
143
|
+
# Normalize strategy to lowercase and validate
|
|
144
|
+
normalized_strategy = strategy.lower()
|
|
145
|
+
if normalized_strategy not in ['blue', 'green']:
|
|
146
|
+
return False
|
|
147
|
+
return normalized_strategy
|
|
148
|
+
|
|
131
149
|
|
|
132
150
|
def _get_shared_strategy():
|
|
133
151
|
"""Get or create the shared strategy storage."""
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
# Try to use multiprocessing.Manager for process-safe storage
|
|
137
|
-
try:
|
|
138
|
-
if _strategy_manager is None:
|
|
139
|
-
_strategy_manager = multiprocessing.Manager()
|
|
140
|
-
_shared_strategy = _strategy_manager.dict()
|
|
141
|
-
_shared_strategy['active_strategy'] = None
|
|
142
|
-
_shared_strategy['last_check_time'] = 0
|
|
143
|
-
_shared_strategy['pending_strategy'] = None
|
|
144
|
-
_shared_strategy['query_strategy_map'] = _strategy_manager.dict()
|
|
145
|
-
_shared_strategy['last_transition_time'] = 0
|
|
146
|
-
_shared_strategy['session_invalidated'] = False
|
|
147
|
-
return _shared_strategy
|
|
148
|
-
except:
|
|
149
|
-
# Fall back to thread-local storage if Manager fails
|
|
150
|
-
return _local_strategy_cache
|
|
152
|
+
return _local_strategy_cache
|
|
151
153
|
|
|
152
154
|
|
|
153
155
|
def _get_active_strategy():
|
|
@@ -165,20 +167,21 @@ def _get_active_strategy():
|
|
|
165
167
|
|
|
166
168
|
def _set_active_strategy(strategy):
|
|
167
169
|
"""Set the active deployment strategy in shared memory."""
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
# Normalize strategy to lowercase and validate
|
|
171
|
-
normalized_strategy = strategy.lower()
|
|
172
|
-
if normalized_strategy not in ['blue', 'green']:
|
|
170
|
+
normalized_strategy = _get_normalised_strategy_with_validate(strategy)
|
|
171
|
+
if not normalized_strategy:
|
|
173
172
|
return
|
|
174
173
|
|
|
175
174
|
with _strategy_lock:
|
|
176
175
|
shared_strategy = _get_shared_strategy()
|
|
177
176
|
current_time = time.time()
|
|
177
|
+
old_strategy = shared_strategy.get('active_strategy')
|
|
178
178
|
|
|
179
179
|
# Only update transition time if strategy actually changed
|
|
180
|
-
if
|
|
180
|
+
if old_strategy != normalized_strategy:
|
|
181
181
|
shared_strategy['last_transition_time'] = current_time
|
|
182
|
+
_strategy_debug_log(f"Setting active strategy: {old_strategy} -> {normalized_strategy}")
|
|
183
|
+
else:
|
|
184
|
+
_strategy_debug_log(f"Active strategy unchanged: {normalized_strategy}")
|
|
182
185
|
|
|
183
186
|
shared_strategy['active_strategy'] = normalized_strategy
|
|
184
187
|
shared_strategy['last_check_time'] = current_time
|
|
@@ -188,6 +191,9 @@ def _clear_strategy_cache():
|
|
|
188
191
|
"""Clear the cached strategy to force re-detection."""
|
|
189
192
|
with _strategy_lock:
|
|
190
193
|
shared_strategy = _get_shared_strategy()
|
|
194
|
+
old_strategy = shared_strategy.get('active_strategy')
|
|
195
|
+
if old_strategy:
|
|
196
|
+
_strategy_debug_log(f"Clearing strategy cache (was: {old_strategy})")
|
|
191
197
|
shared_strategy['active_strategy'] = None
|
|
192
198
|
shared_strategy['last_check_time'] = 0
|
|
193
199
|
shared_strategy['pending_strategy'] = None
|
|
@@ -195,11 +201,8 @@ def _clear_strategy_cache():
|
|
|
195
201
|
|
|
196
202
|
def _set_pending_strategy(strategy):
|
|
197
203
|
"""Set the pending strategy to be used for the next query."""
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
# Normalize strategy to lowercase and validate
|
|
201
|
-
normalized_strategy = strategy.lower()
|
|
202
|
-
if normalized_strategy not in ['blue', 'green']:
|
|
204
|
+
normalized_strategy = _get_normalised_strategy_with_validate(strategy)
|
|
205
|
+
if not normalized_strategy:
|
|
203
206
|
return
|
|
204
207
|
|
|
205
208
|
with _strategy_lock:
|
|
@@ -208,6 +211,8 @@ def _set_pending_strategy(strategy):
|
|
|
208
211
|
|
|
209
212
|
if normalized_strategy != current_active:
|
|
210
213
|
shared_strategy['pending_strategy'] = normalized_strategy
|
|
214
|
+
query_count = len(shared_strategy.get('query_strategy_map', {}))
|
|
215
|
+
_strategy_debug_log(f"Setting pending strategy: {normalized_strategy} (current: {current_active}, active queries: {query_count})")
|
|
211
216
|
|
|
212
217
|
|
|
213
218
|
def _apply_pending_strategy():
|
|
@@ -215,14 +220,19 @@ def _apply_pending_strategy():
|
|
|
215
220
|
with _strategy_lock:
|
|
216
221
|
shared_strategy = _get_shared_strategy()
|
|
217
222
|
if shared_strategy['pending_strategy']:
|
|
223
|
+
old_strategy = shared_strategy['active_strategy']
|
|
218
224
|
new_strategy = shared_strategy['pending_strategy']
|
|
219
225
|
current_time = time.time()
|
|
220
226
|
|
|
227
|
+
_strategy_debug_log(f"Applying pending strategy: {old_strategy} -> {new_strategy}")
|
|
228
|
+
|
|
221
229
|
shared_strategy['active_strategy'] = new_strategy
|
|
222
230
|
shared_strategy['pending_strategy'] = None
|
|
223
231
|
shared_strategy['last_check_time'] = current_time
|
|
224
232
|
shared_strategy['last_transition_time'] = current_time
|
|
225
233
|
shared_strategy['session_invalidated'] = True # Invalidate all sessions
|
|
234
|
+
|
|
235
|
+
_strategy_debug_log(f"Strategy transition complete. All sessions invalidated.")
|
|
226
236
|
|
|
227
237
|
return new_strategy
|
|
228
238
|
return None
|
|
@@ -237,11 +247,8 @@ def _invalidate_all_sessions():
|
|
|
237
247
|
|
|
238
248
|
def _register_query_strategy(query_id, strategy):
|
|
239
249
|
"""Register the strategy used for a specific query."""
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
# Normalize strategy to lowercase and validate
|
|
243
|
-
normalized_strategy = strategy.lower()
|
|
244
|
-
if normalized_strategy not in ['blue', 'green']:
|
|
250
|
+
normalized_strategy = _get_normalised_strategy_with_validate(strategy)
|
|
251
|
+
if not normalized_strategy:
|
|
245
252
|
return
|
|
246
253
|
|
|
247
254
|
with _strategy_lock:
|
|
@@ -249,6 +256,7 @@ def _register_query_strategy(query_id, strategy):
|
|
|
249
256
|
query_map = shared_strategy.get('query_strategy_map', {})
|
|
250
257
|
query_map[query_id] = normalized_strategy
|
|
251
258
|
shared_strategy['query_strategy_map'] = query_map
|
|
259
|
+
_strategy_debug_log(f"Query {query_id} registered with strategy: {normalized_strategy}")
|
|
252
260
|
|
|
253
261
|
|
|
254
262
|
def _get_query_strategy(query_id):
|
|
@@ -270,8 +278,11 @@ def _cleanup_query_strategy(query_id):
|
|
|
270
278
|
shared_strategy = _get_shared_strategy()
|
|
271
279
|
query_map = shared_strategy.get('query_strategy_map', {})
|
|
272
280
|
if query_id in query_map:
|
|
281
|
+
strategy = query_map[query_id]
|
|
273
282
|
del query_map[query_id]
|
|
274
283
|
shared_strategy['query_strategy_map'] = query_map
|
|
284
|
+
remaining_queries = len(query_map)
|
|
285
|
+
_strategy_debug_log(f"Query {query_id} completed (was using {strategy}). Remaining active queries: {remaining_queries}")
|
|
275
286
|
|
|
276
287
|
|
|
277
288
|
def _get_strategy_debug_info():
|
|
@@ -318,6 +329,7 @@ class Connection(object):
|
|
|
318
329
|
auto_resume: bool = True,
|
|
319
330
|
scheme: str = 'e6data',
|
|
320
331
|
grpc_options: dict = None,
|
|
332
|
+
debug: bool = False,
|
|
321
333
|
):
|
|
322
334
|
"""
|
|
323
335
|
Parameters
|
|
@@ -349,6 +361,8 @@ class Connection(object):
|
|
|
349
361
|
- max_send_message_length: Similar to max_receive_message_length, this parameter sets the maximum allowed size (in bytes) for outgoing messages from the gRPC client
|
|
350
362
|
- grpc_prepare_timeout: Timeout for prepare statement API call (default to 10 minutes).
|
|
351
363
|
- keepalive_time_ms: This parameter defines the time, in milliseconds, Default to 30 seconds
|
|
364
|
+
debug: bool, Optional
|
|
365
|
+
Flag to enable debug logging for blue-green deployment strategy changes
|
|
352
366
|
"""
|
|
353
367
|
if not username or not password:
|
|
354
368
|
raise ValueError("username or password cannot be empty.")
|
|
@@ -378,6 +392,13 @@ class Connection(object):
|
|
|
378
392
|
The default maximum time on client side to wait for the cluster to resume is 5 minutes.
|
|
379
393
|
"""
|
|
380
394
|
self.grpc_auto_resume_timeout_seconds = self._grpc_options.pop('grpc_auto_resume_timeout_seconds')
|
|
395
|
+
|
|
396
|
+
# Store debug flag and register with debug connections
|
|
397
|
+
self._debug = debug
|
|
398
|
+
if self._debug:
|
|
399
|
+
_debug_connections.add(id(self))
|
|
400
|
+
_strategy_debug_log(f"Debug mode enabled for connection {id(self)}")
|
|
401
|
+
|
|
381
402
|
self._create_client()
|
|
382
403
|
|
|
383
404
|
@property
|
|
@@ -504,6 +525,7 @@ class Connection(object):
|
|
|
504
525
|
|
|
505
526
|
if active_strategy and not pending_strategy:
|
|
506
527
|
# Use cached strategy only if there's no pending strategy
|
|
528
|
+
_strategy_debug_log(f"Authenticating with cached strategy: {active_strategy}")
|
|
507
529
|
try:
|
|
508
530
|
authenticate_response = self._client.authenticate(
|
|
509
531
|
authenticate_request,
|
|
@@ -515,8 +537,9 @@ class Connection(object):
|
|
|
515
537
|
# Check for new strategy in authenticate response
|
|
516
538
|
if hasattr(authenticate_response, 'new_strategy') and authenticate_response.new_strategy:
|
|
517
539
|
new_strategy = authenticate_response.new_strategy.lower()
|
|
518
|
-
|
|
519
|
-
|
|
540
|
+
normalized_strategy = _get_normalised_strategy_with_validate(new_strategy)
|
|
541
|
+
if normalized_strategy and normalized_strategy != active_strategy:
|
|
542
|
+
_set_pending_strategy(normalized_strategy)
|
|
520
543
|
_apply_pending_strategy()
|
|
521
544
|
self._session_id = None
|
|
522
545
|
self.close()
|
|
@@ -525,6 +548,7 @@ class Connection(object):
|
|
|
525
548
|
except _InactiveRpcError as e:
|
|
526
549
|
if e.code() == grpc.StatusCode.UNKNOWN and 'status: 456' in e.details():
|
|
527
550
|
# Strategy changed, clear cache and retry
|
|
551
|
+
_strategy_debug_log(f"Got 456 error with strategy {active_strategy}, clearing cache and retrying")
|
|
528
552
|
_clear_strategy_cache()
|
|
529
553
|
active_strategy = None
|
|
530
554
|
else:
|
|
@@ -543,8 +567,10 @@ class Connection(object):
|
|
|
543
567
|
else:
|
|
544
568
|
# Always try blue first, then green if it fails with 456
|
|
545
569
|
strategies = ['blue', 'green']
|
|
570
|
+
_strategy_debug_log(f"No cached strategy, will try strategies in order: {strategies}")
|
|
546
571
|
last_error = None
|
|
547
572
|
for strategy in strategies:
|
|
573
|
+
_strategy_debug_log(f"Attempting authentication with strategy: {strategy}")
|
|
548
574
|
try:
|
|
549
575
|
authenticate_response = self._client.authenticate(
|
|
550
576
|
authenticate_request,
|
|
@@ -553,12 +579,14 @@ class Connection(object):
|
|
|
553
579
|
self._session_id = authenticate_response.sessionId
|
|
554
580
|
if self._session_id:
|
|
555
581
|
# Success! Cache this strategy
|
|
582
|
+
_strategy_debug_log(f"Authentication successful with strategy: {strategy}")
|
|
556
583
|
_set_active_strategy(strategy)
|
|
557
584
|
|
|
558
585
|
# Check for new strategy in authenticate response
|
|
559
586
|
if hasattr(authenticate_response, 'new_strategy') and authenticate_response.new_strategy:
|
|
560
587
|
new_strategy = authenticate_response.new_strategy.lower()
|
|
561
|
-
|
|
588
|
+
normalized_strategy = _get_normalised_strategy_with_validate(new_strategy)
|
|
589
|
+
if normalized_strategy and normalized_strategy != active_strategy:
|
|
562
590
|
_set_pending_strategy(new_strategy)
|
|
563
591
|
_apply_pending_strategy()
|
|
564
592
|
self.close()
|
|
@@ -569,6 +597,7 @@ class Connection(object):
|
|
|
569
597
|
except _InactiveRpcError as e:
|
|
570
598
|
if e.code() == grpc.StatusCode.UNKNOWN and 'status: 456' in e.details():
|
|
571
599
|
# Wrong strategy, try the next one
|
|
600
|
+
_strategy_debug_log(f"Strategy {strategy} failed with 456 error, trying next")
|
|
572
601
|
last_error = e
|
|
573
602
|
continue
|
|
574
603
|
else:
|
|
@@ -593,6 +622,7 @@ class Connection(object):
|
|
|
593
622
|
cluster_uuid=self.cluster_name,
|
|
594
623
|
timeout=self.grpc_auto_resume_timeout_seconds
|
|
595
624
|
).resume()
|
|
625
|
+
print('starus', status)
|
|
596
626
|
if status:
|
|
597
627
|
return self.get_session_id
|
|
598
628
|
else:
|
|
@@ -640,6 +670,11 @@ class Connection(object):
|
|
|
640
670
|
self._channel.close()
|
|
641
671
|
self._channel = None
|
|
642
672
|
self._session_id = None
|
|
673
|
+
|
|
674
|
+
# Remove from debug connections if debug was enabled
|
|
675
|
+
if self._debug:
|
|
676
|
+
_debug_connections.discard(id(self))
|
|
677
|
+
_strategy_debug_log(f"Debug mode disabled for connection {id(self)}")
|
|
643
678
|
|
|
644
679
|
def check_connection(self):
|
|
645
680
|
"""
|
|
@@ -1086,6 +1121,7 @@ class Cursor(DBAPICursor):
|
|
|
1086
1121
|
query_map = shared_strategy.get('query_strategy_map', {})
|
|
1087
1122
|
|
|
1088
1123
|
if pending_strategy and len(query_map) == 0:
|
|
1124
|
+
_strategy_debug_log(f"Last query cleared, triggering pending strategy transition")
|
|
1089
1125
|
_apply_pending_strategy()
|
|
1090
1126
|
|
|
1091
1127
|
return clear_response
|
|
@@ -1109,6 +1145,7 @@ class Cursor(DBAPICursor):
|
|
|
1109
1145
|
query_map = shared_strategy.get('query_strategy_map', {})
|
|
1110
1146
|
|
|
1111
1147
|
if pending_strategy and len(query_map) == 0:
|
|
1148
|
+
_strategy_debug_log(f"Last query cleared, triggering pending strategy transition")
|
|
1112
1149
|
_apply_pending_strategy()
|
|
1113
1150
|
|
|
1114
1151
|
def status(self, query_id):
|
|
@@ -40,24 +40,7 @@ def _initialize_shared_state():
|
|
|
40
40
|
with _initialization_lock:
|
|
41
41
|
if _shared_strategy is not None:
|
|
42
42
|
return _shared_strategy
|
|
43
|
-
|
|
44
|
-
try:
|
|
45
|
-
# Try to create multiprocessing Manager
|
|
46
|
-
_manager = multiprocessing.Manager()
|
|
47
|
-
_shared_strategy = _manager.dict({
|
|
48
|
-
'active_strategy': None,
|
|
49
|
-
'last_check_time': 0,
|
|
50
|
-
'pending_strategy': None,
|
|
51
|
-
'query_strategy_map': _manager.dict(),
|
|
52
|
-
'last_transition_time': 0,
|
|
53
|
-
'session_invalidated': False
|
|
54
|
-
})
|
|
55
|
-
_logger.debug("Successfully initialized multiprocessing Manager for strategy sharing")
|
|
56
|
-
return _shared_strategy
|
|
57
|
-
except Exception as e:
|
|
58
|
-
# Fall back to thread-local storage if Manager fails
|
|
59
|
-
_logger.warning(f"Failed to initialize multiprocessing Manager: {e}. Using thread-local storage.")
|
|
60
|
-
return _local_strategy_cache
|
|
43
|
+
return _local_strategy_cache
|
|
61
44
|
|
|
62
45
|
|
|
63
46
|
def _get_shared_strategy():
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.2
|
|
2
2
|
Name: e6data-python-connector
|
|
3
|
-
Version: 2.3.
|
|
3
|
+
Version: 2.3.9rc10
|
|
4
4
|
Summary: Client for the e6data distributed SQL Engine.
|
|
5
5
|
Home-page: https://github.com/e6x-labs/e6data-python-connector
|
|
6
6
|
Author: e6data, Inc.
|
|
@@ -38,7 +38,7 @@ Dynamic: summary
|
|
|
38
38
|
|
|
39
39
|
# e6data Python Connector
|
|
40
40
|
|
|
41
|
-

|
|
42
42
|
|
|
43
43
|
## Introduction
|
|
44
44
|
|
|
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.9rc8 → e6data_python_connector-2.3.9rc10}/test/analyze_38_nines.py
RENAMED
|
File without changes
|
{e6data_python_connector-2.3.9rc8 → e6data_python_connector-2.3.9rc10}/test/analyze_all_cases.py
RENAMED
|
File without changes
|
{e6data_python_connector-2.3.9rc8 → e6data_python_connector-2.3.9rc10}/test/analyze_binary.py
RENAMED
|
File without changes
|
{e6data_python_connector-2.3.9rc8 → e6data_python_connector-2.3.9rc10}/test/analyze_correct_value.py
RENAMED
|
File without changes
|
{e6data_python_connector-2.3.9rc8 → e6data_python_connector-2.3.9rc10}/test/analyze_fields.py
RENAMED
|
File without changes
|
{e6data_python_connector-2.3.9rc8 → e6data_python_connector-2.3.9rc10}/test/check_decimal_errors.py
RENAMED
|
File without changes
|
{e6data_python_connector-2.3.9rc8 → e6data_python_connector-2.3.9rc10}/test/cleanup_test_files.py
RENAMED
|
File without changes
|
{e6data_python_connector-2.3.9rc8 → e6data_python_connector-2.3.9rc10}/test/debug_38_nines.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{e6data_python_connector-2.3.9rc8 → e6data_python_connector-2.3.9rc10}/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.9rc8 → e6data_python_connector-2.3.9rc10}/test/test_comprehensive.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{e6data_python_connector-2.3.9rc8 → e6data_python_connector-2.3.9rc10}/test/test_improved_parsing.py
RENAMED
|
File without changes
|
{e6data_python_connector-2.3.9rc8 → e6data_python_connector-2.3.9rc10}/test/test_known_case.py
RENAMED
|
File without changes
|
{e6data_python_connector-2.3.9rc8 → e6data_python_connector-2.3.9rc10}/test/test_manual_analysis.py
RENAMED
|
File without changes
|
{e6data_python_connector-2.3.9rc8 → e6data_python_connector-2.3.9rc10}/test/test_mock_server.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{e6data_python_connector-2.3.9rc8 → e6data_python_connector-2.3.9rc10}/test/test_specific_binary.py
RENAMED
|
File without changes
|
{e6data_python_connector-2.3.9rc8 → e6data_python_connector-2.3.9rc10}/test/test_strategy.py
RENAMED
|
File without changes
|
{e6data_python_connector-2.3.9rc8 → e6data_python_connector-2.3.9rc10}/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.9rc8 → e6data_python_connector-2.3.9rc10}/test/validate_decimal128.py
RENAMED
|
File without changes
|
|
File without changes
|
{e6data_python_connector-2.3.9rc8 → e6data_python_connector-2.3.9rc10}/test/verify_decimal_fix.py
RENAMED
|
File without changes
|