e6data-python-connector 2.3.10rc2__tar.gz → 2.3.10rc4__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 (77) hide show
  1. {e6data_python_connector-2.3.10rc2/e6data_python_connector.egg-info → e6data_python_connector-2.3.10rc4}/PKG-INFO +2 -2
  2. {e6data_python_connector-2.3.10rc2 → e6data_python_connector-2.3.10rc4}/README.md +1 -1
  3. {e6data_python_connector-2.3.10rc2 → e6data_python_connector-2.3.10rc4}/e6data_python_connector/e6data_grpc.py +81 -37
  4. {e6data_python_connector-2.3.10rc2 → e6data_python_connector-2.3.10rc4/e6data_python_connector.egg-info}/PKG-INFO +2 -2
  5. {e6data_python_connector-2.3.10rc2 → e6data_python_connector-2.3.10rc4}/setup.py +1 -1
  6. {e6data_python_connector-2.3.10rc2 → e6data_python_connector-2.3.10rc4}/LICENSE +0 -0
  7. {e6data_python_connector-2.3.10rc2 → e6data_python_connector-2.3.10rc4}/MANIFEST.in +0 -0
  8. {e6data_python_connector-2.3.10rc2 → e6data_python_connector-2.3.10rc4}/e6data_python_connector/__init__.py +0 -0
  9. {e6data_python_connector-2.3.10rc2 → e6data_python_connector-2.3.10rc4}/e6data_python_connector/cluster_manager.py +0 -0
  10. {e6data_python_connector-2.3.10rc2 → e6data_python_connector-2.3.10rc4}/e6data_python_connector/cluster_server/__init__.py +0 -0
  11. {e6data_python_connector-2.3.10rc2 → e6data_python_connector-2.3.10rc4}/e6data_python_connector/cluster_server/cluster_pb2.py +0 -0
  12. {e6data_python_connector-2.3.10rc2 → e6data_python_connector-2.3.10rc4}/e6data_python_connector/cluster_server/cluster_pb2_grpc.py +0 -0
  13. {e6data_python_connector-2.3.10rc2 → e6data_python_connector-2.3.10rc4}/e6data_python_connector/common.py +0 -0
  14. {e6data_python_connector-2.3.10rc2 → e6data_python_connector-2.3.10rc4}/e6data_python_connector/constants.py +0 -0
  15. {e6data_python_connector-2.3.10rc2 → e6data_python_connector-2.3.10rc4}/e6data_python_connector/datainputstream.py +0 -0
  16. {e6data_python_connector-2.3.10rc2 → e6data_python_connector-2.3.10rc4}/e6data_python_connector/date_time_utils.py +0 -0
  17. {e6data_python_connector-2.3.10rc2 → e6data_python_connector-2.3.10rc4}/e6data_python_connector/dialect.py +0 -0
  18. {e6data_python_connector-2.3.10rc2 → e6data_python_connector-2.3.10rc4}/e6data_python_connector/e6x_vector/__init__.py +0 -0
  19. {e6data_python_connector-2.3.10rc2 → e6data_python_connector-2.3.10rc4}/e6data_python_connector/e6x_vector/constants.py +0 -0
  20. {e6data_python_connector-2.3.10rc2 → e6data_python_connector-2.3.10rc4}/e6data_python_connector/e6x_vector/ttypes.py +0 -0
  21. {e6data_python_connector-2.3.10rc2 → e6data_python_connector-2.3.10rc4}/e6data_python_connector/exceptions.py +0 -0
  22. {e6data_python_connector-2.3.10rc2 → e6data_python_connector-2.3.10rc4}/e6data_python_connector/server/QueryEngineService.py +0 -0
  23. {e6data_python_connector-2.3.10rc2 → e6data_python_connector-2.3.10rc4}/e6data_python_connector/server/__init__.py +0 -0
  24. {e6data_python_connector-2.3.10rc2 → e6data_python_connector-2.3.10rc4}/e6data_python_connector/server/constants.py +0 -0
  25. {e6data_python_connector-2.3.10rc2 → e6data_python_connector-2.3.10rc4}/e6data_python_connector/server/e6x_engine_pb2.py +0 -0
  26. {e6data_python_connector-2.3.10rc2 → e6data_python_connector-2.3.10rc4}/e6data_python_connector/server/e6x_engine_pb2.pyi +0 -0
  27. {e6data_python_connector-2.3.10rc2 → e6data_python_connector-2.3.10rc4}/e6data_python_connector/server/e6x_engine_pb2_grpc.py +0 -0
  28. {e6data_python_connector-2.3.10rc2 → e6data_python_connector-2.3.10rc4}/e6data_python_connector/server/ttypes.py +0 -0
  29. {e6data_python_connector-2.3.10rc2 → e6data_python_connector-2.3.10rc4}/e6data_python_connector/strategy.py +0 -0
  30. {e6data_python_connector-2.3.10rc2 → e6data_python_connector-2.3.10rc4}/e6data_python_connector/typeId.py +0 -0
  31. {e6data_python_connector-2.3.10rc2 → e6data_python_connector-2.3.10rc4}/e6data_python_connector.egg-info/SOURCES.txt +0 -0
  32. {e6data_python_connector-2.3.10rc2 → e6data_python_connector-2.3.10rc4}/e6data_python_connector.egg-info/dependency_links.txt +0 -0
  33. {e6data_python_connector-2.3.10rc2 → e6data_python_connector-2.3.10rc4}/e6data_python_connector.egg-info/entry_points.txt +0 -0
  34. {e6data_python_connector-2.3.10rc2 → e6data_python_connector-2.3.10rc4}/e6data_python_connector.egg-info/requires.txt +0 -0
  35. {e6data_python_connector-2.3.10rc2 → e6data_python_connector-2.3.10rc4}/e6data_python_connector.egg-info/top_level.txt +0 -0
  36. {e6data_python_connector-2.3.10rc2 → e6data_python_connector-2.3.10rc4}/pyproject.toml +0 -0
  37. {e6data_python_connector-2.3.10rc2 → e6data_python_connector-2.3.10rc4}/setup.cfg +0 -0
  38. {e6data_python_connector-2.3.10rc2 → e6data_python_connector-2.3.10rc4}/test/__init__.py +0 -0
  39. {e6data_python_connector-2.3.10rc2 → e6data_python_connector-2.3.10rc4}/test/analyze_38_nines.py +0 -0
  40. {e6data_python_connector-2.3.10rc2 → e6data_python_connector-2.3.10rc4}/test/analyze_all_cases.py +0 -0
  41. {e6data_python_connector-2.3.10rc2 → e6data_python_connector-2.3.10rc4}/test/analyze_binary.py +0 -0
  42. {e6data_python_connector-2.3.10rc2 → e6data_python_connector-2.3.10rc4}/test/analyze_correct_value.py +0 -0
  43. {e6data_python_connector-2.3.10rc2 → e6data_python_connector-2.3.10rc4}/test/analyze_fields.py +0 -0
  44. {e6data_python_connector-2.3.10rc2 → e6data_python_connector-2.3.10rc4}/test/check_decimal_errors.py +0 -0
  45. {e6data_python_connector-2.3.10rc2 → e6data_python_connector-2.3.10rc4}/test/cleanup_test_files.py +0 -0
  46. {e6data_python_connector-2.3.10rc2 → e6data_python_connector-2.3.10rc4}/test/debug_38_nines.py +0 -0
  47. {e6data_python_connector-2.3.10rc2 → e6data_python_connector-2.3.10rc4}/test/debug_binary.py +0 -0
  48. {e6data_python_connector-2.3.10rc2 → e6data_python_connector-2.3.10rc4}/test/final_test.py +0 -0
  49. {e6data_python_connector-2.3.10rc2 → e6data_python_connector-2.3.10rc4}/test/move_tests.py +0 -0
  50. {e6data_python_connector-2.3.10rc2 → e6data_python_connector-2.3.10rc4}/test/quick_test.py +0 -0
  51. {e6data_python_connector-2.3.10rc2 → e6data_python_connector-2.3.10rc4}/test/test_38_nines.py +0 -0
  52. {e6data_python_connector-2.3.10rc2 → e6data_python_connector-2.3.10rc4}/test/test_all_decimal128_cases.py +0 -0
  53. {e6data_python_connector-2.3.10rc2 → e6data_python_connector-2.3.10rc4}/test/test_cluster_manager_efficiency.py +0 -0
  54. {e6data_python_connector-2.3.10rc2 → e6data_python_connector-2.3.10rc4}/test/test_cluster_manager_none_strategy.py +0 -0
  55. {e6data_python_connector-2.3.10rc2 → e6data_python_connector-2.3.10rc4}/test/test_cluster_manager_strategy.py +0 -0
  56. {e6data_python_connector-2.3.10rc2 → e6data_python_connector-2.3.10rc4}/test/test_comprehensive.py +0 -0
  57. {e6data_python_connector-2.3.10rc2 → e6data_python_connector-2.3.10rc4}/test/test_current_implementation.py +0 -0
  58. {e6data_python_connector-2.3.10rc2 → e6data_python_connector-2.3.10rc4}/test/test_decimal128_binary_parsing.py +0 -0
  59. {e6data_python_connector-2.3.10rc2 → e6data_python_connector-2.3.10rc4}/test/test_decimal128_parsing.py +0 -0
  60. {e6data_python_connector-2.3.10rc2 → e6data_python_connector-2.3.10rc4}/test/test_fix.py +0 -0
  61. {e6data_python_connector-2.3.10rc2 → e6data_python_connector-2.3.10rc4}/test/test_improved_parsing.py +0 -0
  62. {e6data_python_connector-2.3.10rc2 → e6data_python_connector-2.3.10rc4}/test/test_known_case.py +0 -0
  63. {e6data_python_connector-2.3.10rc2 → e6data_python_connector-2.3.10rc4}/test/test_manual_analysis.py +0 -0
  64. {e6data_python_connector-2.3.10rc2 → e6data_python_connector-2.3.10rc4}/test/test_mock_server.py +0 -0
  65. {e6data_python_connector-2.3.10rc2 → e6data_python_connector-2.3.10rc4}/test/test_multiprocessing_fix.py +0 -0
  66. {e6data_python_connector-2.3.10rc2 → e6data_python_connector-2.3.10rc4}/test/test_new_implementation.py +0 -0
  67. {e6data_python_connector-2.3.10rc2 → e6data_python_connector-2.3.10rc4}/test/test_specific_binary.py +0 -0
  68. {e6data_python_connector-2.3.10rc2 → e6data_python_connector-2.3.10rc4}/test/test_strategy.py +0 -0
  69. {e6data_python_connector-2.3.10rc2 → e6data_python_connector-2.3.10rc4}/test/test_strategy_logic.py +0 -0
  70. {e6data_python_connector-2.3.10rc2 → e6data_python_connector-2.3.10rc4}/test/test_strategy_persistence_fix.py +0 -0
  71. {e6data_python_connector-2.3.10rc2 → e6data_python_connector-2.3.10rc4}/test/test_strategy_sharing_fix.py +0 -0
  72. {e6data_python_connector-2.3.10rc2 → e6data_python_connector-2.3.10rc4}/test/test_user_binary_value.py +0 -0
  73. {e6data_python_connector-2.3.10rc2 → e6data_python_connector-2.3.10rc4}/test/tests.py +0 -0
  74. {e6data_python_connector-2.3.10rc2 → e6data_python_connector-2.3.10rc4}/test/tests_grpc.py +0 -0
  75. {e6data_python_connector-2.3.10rc2 → e6data_python_connector-2.3.10rc4}/test/validate_decimal128.py +0 -0
  76. {e6data_python_connector-2.3.10rc2 → e6data_python_connector-2.3.10rc4}/test/validate_implementation.py +0 -0
  77. {e6data_python_connector-2.3.10rc2 → e6data_python_connector-2.3.10rc4}/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.10rc2
3
+ Version: 2.3.10rc4
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
- ![version](https://img.shields.io/badge/version-2.3.10,rc2-blue.svg)
41
+ ![version](https://img.shields.io/badge/version-2.3.10,rc4-blue.svg)
42
42
 
43
43
  ## Introduction
44
44
 
@@ -1,6 +1,6 @@
1
1
  # e6data Python Connector
2
2
 
3
- ![version](https://img.shields.io/badge/version-2.3.10,rc2-blue.svg)
3
+ ![version](https://img.shields.io/badge/version-2.3.10,rc4-blue.svg)
4
4
 
5
5
  ## Introduction
6
6
 
@@ -80,10 +80,12 @@ def re_auth(func):
80
80
  if current_retry == max_retry:
81
81
  raise e
82
82
  if e.code() == grpc.StatusCode.INTERNAL and 'Access denied' in e.details():
83
+ _logger.debug(f'Re-authenticating due to {e}')
83
84
  time.sleep(0.2)
84
85
  self.connection.get_re_authenticate_session_id()
85
86
  elif 'status: 456' in e.details():
86
87
  # Strategy changed, clear cache and retry
88
+ _logger.debug(f'Re-authenticating due to status: 456')
87
89
  _clear_strategy_cache()
88
90
  # Force re-authentication which will detect new strategy
89
91
  self.connection.get_re_authenticate_session_id()
@@ -243,7 +245,7 @@ def _invalidate_all_sessions():
243
245
  shared_strategy['session_invalidated'] = True
244
246
 
245
247
 
246
- def _register_query_strategy(query_id, strategy):
248
+ def _register_query_strategy(query_id, strategy, session_id=None):
247
249
  """Register the strategy used for a specific query."""
248
250
  if not query_id or not strategy:
249
251
  return
@@ -257,7 +259,8 @@ def _register_query_strategy(query_id, strategy):
257
259
  query_map = shared_strategy.get('query_strategy_map', {})
258
260
  query_map[query_id] = normalized_strategy
259
261
  shared_strategy['query_strategy_map'] = query_map
260
- _strategy_debug_log(f"Query {query_id} registered with strategy: {normalized_strategy}")
262
+ session_info = f", session: {session_id}" if session_id else ""
263
+ _strategy_debug_log(f"Query {query_id} registered with strategy: {normalized_strategy}{session_info}")
261
264
 
262
265
 
263
266
  def _get_query_strategy(query_id):
@@ -271,7 +274,7 @@ def _get_query_strategy(query_id):
271
274
  return query_map.get(query_id, current_active_strategy)
272
275
 
273
276
 
274
- def _cleanup_query_strategy(query_id):
277
+ def _cleanup_query_strategy(query_id, session_id=None):
275
278
  """Remove the strategy mapping for a completed query."""
276
279
  if not query_id:
277
280
  return
@@ -283,7 +286,8 @@ def _cleanup_query_strategy(query_id):
283
286
  del query_map[query_id]
284
287
  shared_strategy['query_strategy_map'] = query_map
285
288
  remaining_queries = len(query_map)
286
- _strategy_debug_log(f"Query {query_id} completed (was using {strategy}). Remaining active queries: {remaining_queries}")
289
+ session_info = f", session: {session_id}" if session_id else ""
290
+ _strategy_debug_log(f"Query {query_id} completed (was using {strategy}{session_info}). Remaining active queries: {remaining_queries}")
287
291
 
288
292
 
289
293
  def _get_strategy_debug_info():
@@ -300,10 +304,18 @@ def _get_strategy_debug_info():
300
304
  }
301
305
 
302
306
 
303
- def _get_grpc_header(engine_ip=None, cluster=None, strategy=None):
307
+ def _get_grpc_header(engine_ip=None, cluster=None, strategy=None, session_id=None, request_type=None):
304
308
  """Generate gRPC metadata headers for the request."""
305
309
  # Use the strategy module's implementation
306
- return _get_strategy_header(engine_ip=engine_ip, cluster=cluster, strategy=strategy)
310
+ headers = _get_strategy_header(engine_ip=engine_ip, cluster=cluster, strategy=strategy)
311
+
312
+ # Log headers being sent if debug connections exist
313
+ if _debug_connections:
314
+ session_info = f", session: {session_id}" if session_id else ""
315
+ request_info = f" [{request_type}]" if request_type else ""
316
+ _strategy_debug_log(f"Sending headers{request_info}: strategy={strategy}, cluster={cluster}, engine_ip={engine_ip}{session_info}")
317
+
318
+ return headers
307
319
 
308
320
 
309
321
  def connect(*args, **kwargs):
@@ -518,9 +530,11 @@ class Connection(object):
518
530
  # Check if session strategy matches current active strategy
519
531
  elif self._session_id and self._session_strategy and self._session_strategy != _get_active_strategy():
520
532
  # Session was created with different strategy, need to re-authenticate
521
- _strategy_debug_log(f"Session strategy mismatch: session created with {self._session_strategy}, current active is {_get_active_strategy()}")
533
+ _strategy_debug_log(f"Session strategy mismatch: session {self._session_id} created with {self._session_strategy}, current active is {_get_active_strategy()}")
534
+ old_session_id = self._session_id
522
535
  self._session_id = None
523
536
  self._session_strategy = None
537
+ _strategy_debug_log(f"Invalidating session {old_session_id} due to strategy mismatch")
524
538
  self.close()
525
539
  self._create_client()
526
540
 
@@ -538,32 +552,37 @@ class Connection(object):
538
552
 
539
553
  if active_strategy and not pending_strategy:
540
554
  # Use cached strategy only if there's no pending strategy
541
- _strategy_debug_log(f"Authenticating with cached strategy: {active_strategy}")
555
+ _strategy_debug_log(f"Authenticating with cached strategy: {active_strategy} (session: {self._session_id})")
542
556
  try:
543
557
  authenticate_response = self._client.authenticate(
544
558
  authenticate_request,
545
- metadata=_get_grpc_header(cluster=self.cluster_name, strategy=active_strategy)
559
+ metadata=_get_grpc_header(cluster=self.cluster_name, strategy=active_strategy,
560
+ session_id=self._session_id, request_type="authenticate")
546
561
  )
547
562
  self._session_id = authenticate_response.sessionId
548
563
  if not self._session_id:
549
564
  raise ValueError("Invalid credentials.")
550
565
  # Track which strategy was used for this session
551
566
  self._session_strategy = active_strategy
567
+ _strategy_debug_log(f"Authentication successful with strategy: {active_strategy}, session: {self._session_id}")
552
568
  # Check for new strategy in authenticate response
553
569
  if hasattr(authenticate_response, 'new_strategy') and authenticate_response.new_strategy:
554
570
  new_strategy = authenticate_response.new_strategy.lower()
555
571
  if new_strategy != active_strategy:
572
+ _strategy_debug_log(f"Strategy changed from {active_strategy} to {new_strategy} in authenticate response, session: {self._session_id}")
556
573
  _set_pending_strategy(new_strategy)
557
574
  _apply_pending_strategy()
575
+ old_session_id = self._session_id
558
576
  self._session_id = None
559
577
  self._session_strategy = None
578
+ _strategy_debug_log(f"Resetting session {old_session_id} due to new strategy: {new_strategy}")
560
579
  self.close()
561
580
  self._create_client()
562
581
  return self.get_session_id
563
582
  except _InactiveRpcError as e:
564
583
  if e.code() == grpc.StatusCode.UNKNOWN and 'status: 456' in e.details():
565
584
  # Strategy changed, clear cache and retry
566
- _strategy_debug_log(f"Got 456 error with strategy {active_strategy}, clearing cache and retrying")
585
+ _strategy_debug_log(f"Got 456 error with strategy {active_strategy}, session: {self._session_id}, clearing cache and retrying")
567
586
  _clear_strategy_cache()
568
587
  active_strategy = None
569
588
  else:
@@ -585,16 +604,17 @@ class Connection(object):
585
604
  _strategy_debug_log(f"No cached strategy, will try strategies in order: {strategies}")
586
605
  last_error = None
587
606
  for strategy in strategies:
588
- _strategy_debug_log(f"Attempting authentication with strategy: {strategy}")
607
+ _strategy_debug_log(f"Attempting authentication with strategy: {strategy} (no session yet)")
589
608
  try:
590
609
  authenticate_response = self._client.authenticate(
591
610
  authenticate_request,
592
- metadata=_get_grpc_header(cluster=self.cluster_name, strategy=strategy)
611
+ metadata=_get_grpc_header(cluster=self.cluster_name, strategy=strategy,
612
+ session_id=None, request_type="authenticate")
593
613
  )
594
614
  self._session_id = authenticate_response.sessionId
595
615
  if self._session_id:
596
616
  # Success! Cache this strategy
597
- _strategy_debug_log(f"Authentication successful with strategy: {strategy}")
617
+ _strategy_debug_log(f"Authentication successful with strategy: {strategy}, session: {self._session_id}")
598
618
  _set_active_strategy(strategy)
599
619
  # Track which strategy was used for this session
600
620
  self._session_strategy = strategy
@@ -603,9 +623,12 @@ class Connection(object):
603
623
  if hasattr(authenticate_response, 'new_strategy') and authenticate_response.new_strategy:
604
624
  new_strategy = authenticate_response.new_strategy.lower()
605
625
  if new_strategy != strategy:
626
+ _strategy_debug_log(f"Strategy changed from {strategy} to {new_strategy} in authenticate response, session: {self._session_id}")
606
627
  _set_pending_strategy(new_strategy)
607
628
  _apply_pending_strategy()
629
+ old_session_id = self._session_id
608
630
  self.close()
631
+ _strategy_debug_log(f"Resetting session {old_session_id} due to new strategy: {new_strategy}")
609
632
  self._create_client()
610
633
  self._session_id = None
611
634
  self._session_strategy = None
@@ -614,7 +637,7 @@ class Connection(object):
614
637
  except _InactiveRpcError as e:
615
638
  if e.code() == grpc.StatusCode.UNKNOWN and 'status: 456' in e.details():
616
639
  # Wrong strategy, try the next one
617
- _strategy_debug_log(f"Strategy {strategy} failed with 456 error, trying next")
640
+ _strategy_debug_log(f"Strategy {strategy} failed with 456 error (session attempt failed), trying next")
618
641
  last_error = e
619
642
  continue
620
643
  else:
@@ -682,6 +705,7 @@ class Connection(object):
682
705
 
683
706
  This method ensures that the gRPC channel is properly closed and the session ID is reset to None.
684
707
  """
708
+ old_session = self._session_id
685
709
  if self._channel is not None:
686
710
  self._channel.close()
687
711
  self._channel = None
@@ -691,7 +715,8 @@ class Connection(object):
691
715
  # Remove from debug connections if debug was enabled
692
716
  if self._debug:
693
717
  _debug_connections.discard(id(self))
694
- _strategy_debug_log(f"Debug mode disabled for connection {id(self)}")
718
+ session_info = f", session was: {old_session}" if old_session else ""
719
+ _strategy_debug_log(f"Debug mode disabled for connection {id(self)}{session_info}")
695
720
 
696
721
  def check_connection(self):
697
722
  """
@@ -754,7 +779,8 @@ class Connection(object):
754
779
  )
755
780
  clear_response = self._client.clear(
756
781
  clear_request,
757
- metadata=_get_grpc_header(engine_ip=engine_ip, cluster=self.cluster_name, strategy=_get_active_strategy())
782
+ metadata=_get_grpc_header(engine_ip=engine_ip, cluster=self.cluster_name, strategy=_get_active_strategy(),
783
+ session_id=self.connection.get_session_id, request_type="clear")
758
784
  )
759
785
 
760
786
  # Check for new strategy in clear response
@@ -785,7 +811,8 @@ class Connection(object):
785
811
  )
786
812
  cancel_response = self._client.cancelQuery(
787
813
  cancel_query_request,
788
- metadata=_get_grpc_header(engine_ip=engine_ip, cluster=self.cluster_name, strategy=_get_active_strategy())
814
+ metadata=_get_grpc_header(engine_ip=engine_ip, cluster=self.cluster_name, strategy=_get_active_strategy(),
815
+ session_id=self._session_id, request_type="query_cancel")
789
816
  )
790
817
 
791
818
  # Check for new strategy in cancel response
@@ -809,7 +836,8 @@ class Connection(object):
809
836
  )
810
837
  dry_run_response = self._client.dryRun(
811
838
  dry_run_request,
812
- metadata=_get_grpc_header(cluster=self.cluster_name, strategy=_get_active_strategy())
839
+ metadata=_get_grpc_header(cluster=self.cluster_name, strategy=_get_active_strategy(),
840
+ session_id=self.get_session_id, request_type="dry_run")
813
841
  )
814
842
  return dry_run_response.dryrunValue
815
843
 
@@ -831,7 +859,8 @@ class Connection(object):
831
859
  )
832
860
  get_table_response = self._client.getTablesV2(
833
861
  get_table_request,
834
- metadata=_get_grpc_header(cluster=self.cluster_name, strategy=_get_active_strategy())
862
+ metadata=_get_grpc_header(cluster=self.cluster_name, strategy=_get_active_strategy(),
863
+ session_id=self.get_session_id, request_type="get_tables")
835
864
  )
836
865
 
837
866
  # Check for new strategy in get tables response
@@ -859,7 +888,8 @@ class Connection(object):
859
888
  )
860
889
  get_columns_response = self._client.getColumnsV2(
861
890
  get_columns_request,
862
- metadata=_get_grpc_header(cluster=self.cluster_name, strategy=_get_active_strategy())
891
+ metadata=_get_grpc_header(cluster=self.cluster_name, strategy=_get_active_strategy(),
892
+ session_id=self.get_session_id, request_type="get_columns")
863
893
  )
864
894
 
865
895
  # Check for new strategy in get columns response
@@ -883,7 +913,8 @@ class Connection(object):
883
913
  )
884
914
  get_schema_response = self._client.getSchemaNamesV2(
885
915
  get_schema_request,
886
- metadata=_get_grpc_header(cluster=self.cluster_name, strategy=_get_active_strategy())
916
+ metadata=_get_grpc_header(cluster=self.cluster_name, strategy=_get_active_strategy(),
917
+ session_id=self.get_session_id, request_type="get_schema")
887
918
  )
888
919
 
889
920
  # Check for new strategy in get schema names response
@@ -970,10 +1001,12 @@ class Cursor(DBAPICursor):
970
1001
  """Reset state about the previous query in preparation for running another query"""
971
1002
  pass
972
1003
 
973
- @property
974
- def metadata(self):
1004
+ def _get_metadata(self, request_type="query_operation"):
975
1005
  """
976
- Get the gRPC metadata for the current query.
1006
+ Get the gRPC metadata for the current query with specific request type.
1007
+
1008
+ Args:
1009
+ request_type (str): The type of request being made
977
1010
 
978
1011
  Returns:
979
1012
  list: A list of tuples containing gRPC metadata.
@@ -986,7 +1019,18 @@ class Cursor(DBAPICursor):
986
1019
  strategy = self.connection._session_strategy
987
1020
  else:
988
1021
  strategy = _get_active_strategy()
989
- return _get_grpc_header(engine_ip=self._engine_ip, cluster=self.connection.cluster_name, strategy=strategy)
1022
+ return _get_grpc_header(engine_ip=self._engine_ip, cluster=self.connection.cluster_name, strategy=strategy,
1023
+ session_id=self.connection.get_session_id, request_type=request_type)
1024
+
1025
+ @property
1026
+ def metadata(self):
1027
+ """
1028
+ Get the gRPC metadata for the current query.
1029
+
1030
+ Returns:
1031
+ list: A list of tuples containing gRPC metadata.
1032
+ """
1033
+ return self._get_metadata("query_operation")
990
1034
 
991
1035
  @property
992
1036
  def arraysize(self):
@@ -1128,7 +1172,7 @@ class Cursor(DBAPICursor):
1128
1172
  )
1129
1173
  # Get fresh client after session access (may have been invalidated)
1130
1174
  client = self.connection.client
1131
- clear_response = client.clearOrCancelQuery(clear_request, metadata=self.metadata)
1175
+ clear_response = client.clearOrCancelQuery(clear_request, metadata=self._get_metadata("clear_or_cancel"))
1132
1176
 
1133
1177
  # Check for new strategy in clear response
1134
1178
  if hasattr(clear_response, 'new_strategy') and clear_response.new_strategy:
@@ -1136,7 +1180,7 @@ class Cursor(DBAPICursor):
1136
1180
 
1137
1181
  # Clean up query strategy mapping
1138
1182
  if query_id:
1139
- _cleanup_query_strategy(query_id)
1183
+ _cleanup_query_strategy(query_id, self.connection.get_session_id)
1140
1184
 
1141
1185
  # Check if this was the last query and we have a pending strategy
1142
1186
  shared_strategy = _get_shared_strategy()
@@ -1144,7 +1188,7 @@ class Cursor(DBAPICursor):
1144
1188
  query_map = shared_strategy.get('query_strategy_map', {})
1145
1189
 
1146
1190
  if pending_strategy and len(query_map) == 0:
1147
- _strategy_debug_log(f"Last query cleared, triggering pending strategy transition")
1191
+ _strategy_debug_log(f"Last query cleared for session {self.connection.get_session_id}, triggering pending strategy transition from {_get_active_strategy()} to {pending_strategy}")
1148
1192
  _apply_pending_strategy()
1149
1193
 
1150
1194
  return clear_response
@@ -1160,7 +1204,7 @@ class Cursor(DBAPICursor):
1160
1204
  self.connection.query_cancel(engine_ip=self._engine_ip, query_id=query_id)
1161
1205
 
1162
1206
  if query_id:
1163
- _cleanup_query_strategy(query_id)
1207
+ _cleanup_query_strategy(query_id, self.connection.get_session_id)
1164
1208
 
1165
1209
  # Check if this was the last query and we have a pending strategy
1166
1210
  shared_strategy = _get_shared_strategy()
@@ -1168,7 +1212,7 @@ class Cursor(DBAPICursor):
1168
1212
  query_map = shared_strategy.get('query_strategy_map', {})
1169
1213
 
1170
1214
  if pending_strategy and len(query_map) == 0:
1171
- _strategy_debug_log(f"Last query cleared, triggering pending strategy transition")
1215
+ _strategy_debug_log(f"Last query cleared for session {self.connection.get_session_id}, triggering pending strategy transition from {_get_active_strategy()} to {pending_strategy}")
1172
1216
  _apply_pending_strategy()
1173
1217
 
1174
1218
  def status(self, query_id):
@@ -1186,7 +1230,7 @@ class Cursor(DBAPICursor):
1186
1230
  queryId=query_id,
1187
1231
  engineIP=self._engine_ip
1188
1232
  )
1189
- status_response = self.connection.client.status(status_request, metadata=self.metadata)
1233
+ status_response = self.connection.client.status(status_request, metadata=self._get_metadata("status"))
1190
1234
 
1191
1235
  # Check for new strategy in status response
1192
1236
  if hasattr(status_response, 'new_strategy') and status_response.new_strategy:
@@ -1227,7 +1271,7 @@ class Cursor(DBAPICursor):
1227
1271
  client = self.connection.client
1228
1272
  prepare_statement_response = client.prepareStatement(
1229
1273
  prepare_statement_request,
1230
- metadata=self.metadata
1274
+ metadata=self._get_metadata("prepare_statement")
1231
1275
  )
1232
1276
 
1233
1277
  self._query_id = prepare_statement_response.queryId
@@ -1242,7 +1286,7 @@ class Cursor(DBAPICursor):
1242
1286
  # Register this query with the current strategy
1243
1287
  current_strategy = _get_active_strategy()
1244
1288
  if current_strategy:
1245
- _register_query_strategy(self._query_id, current_strategy)
1289
+ _register_query_strategy(self._query_id, current_strategy, self.connection.get_session_id)
1246
1290
 
1247
1291
  execute_statement_request = e6x_engine_pb2.ExecuteStatementRequest(
1248
1292
  engineIP=self._engine_ip,
@@ -1253,7 +1297,7 @@ class Cursor(DBAPICursor):
1253
1297
  client = self.connection.client
1254
1298
  execute_response = client.executeStatement(
1255
1299
  execute_statement_request,
1256
- metadata=self.metadata
1300
+ metadata=self._get_metadata("execute_statement")
1257
1301
  )
1258
1302
 
1259
1303
  # Check for new strategy in execute response
@@ -1272,7 +1316,7 @@ class Cursor(DBAPICursor):
1272
1316
  client = self.connection.client
1273
1317
  prepare_statement_response = client.prepareStatementV2(
1274
1318
  prepare_statement_request,
1275
- metadata=self.metadata,
1319
+ metadata=self._get_metadata("prepare_statement_v2"),
1276
1320
  timeout=self.connection.grpc_prepare_timeout
1277
1321
  )
1278
1322
 
@@ -1289,7 +1333,7 @@ class Cursor(DBAPICursor):
1289
1333
  current_strategy = _get_active_strategy()
1290
1334
 
1291
1335
  if current_strategy:
1292
- _register_query_strategy(self._query_id, current_strategy)
1336
+ _register_query_strategy(self._query_id, current_strategy, self.connection.get_session_id)
1293
1337
 
1294
1338
  execute_statement_request = e6x_engine_pb2.ExecuteStatementV2Request(
1295
1339
  engineIP=self._engine_ip,
@@ -1300,7 +1344,7 @@ class Cursor(DBAPICursor):
1300
1344
  client = self.connection.client
1301
1345
  execute_response = client.executeStatementV2(
1302
1346
  execute_statement_request,
1303
- metadata=self.metadata
1347
+ metadata=self._get_metadata("execute_statement_v2")
1304
1348
  )
1305
1349
 
1306
1350
  # Check for new strategy in execute response
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: e6data-python-connector
3
- Version: 2.3.10rc2
3
+ Version: 2.3.10rc4
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
- ![version](https://img.shields.io/badge/version-2.3.10,rc2-blue.svg)
41
+ ![version](https://img.shields.io/badge/version-2.3.10,rc4-blue.svg)
42
42
 
43
43
  ## Introduction
44
44
 
@@ -12,7 +12,7 @@
12
12
 
13
13
  import setuptools
14
14
 
15
- VERSION = (2, 3, 10, 'rc2')
15
+ VERSION = (2, 3, 10, 'rc4')
16
16
 
17
17
 
18
18
  def get_long_desc():