e6data-python-connector 2.3.10rc8__tar.gz → 2.3.11__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 (89) hide show
  1. {e6data_python_connector-2.3.10rc8/e6data_python_connector.egg-info → e6data_python_connector-2.3.11}/PKG-INFO +259 -10
  2. {e6data_python_connector-2.3.10rc8 → e6data_python_connector-2.3.11}/README.md +250 -2
  3. e6data_python_connector-2.3.11/e6data_python_connector/__init__.py +4 -0
  4. {e6data_python_connector-2.3.10rc8 → e6data_python_connector-2.3.11}/e6data_python_connector/cluster_manager.py +93 -9
  5. e6data_python_connector-2.3.11/e6data_python_connector/connection_pool.py +495 -0
  6. {e6data_python_connector-2.3.10rc8 → e6data_python_connector-2.3.11}/e6data_python_connector/datainputstream.py +31 -19
  7. {e6data_python_connector-2.3.10rc8 → e6data_python_connector-2.3.11}/e6data_python_connector/e6data_grpc.py +87 -26
  8. {e6data_python_connector-2.3.10rc8 → e6data_python_connector-2.3.11}/e6data_python_connector/e6x_vector/ttypes.py +26 -2
  9. {e6data_python_connector-2.3.10rc8 → e6data_python_connector-2.3.11/e6data_python_connector.egg-info}/PKG-INFO +259 -10
  10. {e6data_python_connector-2.3.10rc8 → e6data_python_connector-2.3.11}/e6data_python_connector.egg-info/SOURCES.txt +10 -0
  11. e6data_python_connector-2.3.11/e6data_python_connector.egg-info/requires.txt +8 -0
  12. {e6data_python_connector-2.3.10rc8 → e6data_python_connector-2.3.11}/e6data_python_connector.egg-info/top_level.txt +1 -0
  13. e6data_python_connector-2.3.11/gen-py/__init__.py +0 -0
  14. e6data_python_connector-2.3.11/gen-py/e6x_vector/__init__.py +1 -0
  15. e6data_python_connector-2.3.11/gen-py/e6x_vector/constants.py +15 -0
  16. e6data_python_connector-2.3.11/gen-py/e6x_vector/ttypes.py +1875 -0
  17. {e6data_python_connector-2.3.10rc8 → e6data_python_connector-2.3.11}/setup.py +9 -8
  18. e6data_python_connector-2.3.11/test/test_connection_pool.py +410 -0
  19. e6data_python_connector-2.3.11/test/test_connection_pool_e2e.py +564 -0
  20. e6data_python_connector-2.3.11/test/test_pool_concurrency_simple.py +573 -0
  21. e6data_python_connector-2.3.11/test/test_pool_threading_multiprocessing.py +679 -0
  22. e6data_python_connector-2.3.11/test/test_simple_connection_pool.py +278 -0
  23. e6data_python_connector-2.3.10rc8/e6data_python_connector/__init__.py +0 -3
  24. e6data_python_connector-2.3.10rc8/e6data_python_connector.egg-info/requires.txt +0 -8
  25. {e6data_python_connector-2.3.10rc8 → e6data_python_connector-2.3.11}/LICENSE +0 -0
  26. {e6data_python_connector-2.3.10rc8 → e6data_python_connector-2.3.11}/MANIFEST.in +0 -0
  27. {e6data_python_connector-2.3.10rc8 → e6data_python_connector-2.3.11}/e6data_python_connector/cluster_server/__init__.py +0 -0
  28. {e6data_python_connector-2.3.10rc8 → e6data_python_connector-2.3.11}/e6data_python_connector/cluster_server/cluster_pb2.py +0 -0
  29. {e6data_python_connector-2.3.10rc8 → e6data_python_connector-2.3.11}/e6data_python_connector/cluster_server/cluster_pb2_grpc.py +0 -0
  30. {e6data_python_connector-2.3.10rc8 → e6data_python_connector-2.3.11}/e6data_python_connector/common.py +0 -0
  31. {e6data_python_connector-2.3.10rc8 → e6data_python_connector-2.3.11}/e6data_python_connector/constants.py +0 -0
  32. {e6data_python_connector-2.3.10rc8 → e6data_python_connector-2.3.11}/e6data_python_connector/date_time_utils.py +0 -0
  33. {e6data_python_connector-2.3.10rc8 → e6data_python_connector-2.3.11}/e6data_python_connector/dialect.py +0 -0
  34. {e6data_python_connector-2.3.10rc8 → e6data_python_connector-2.3.11}/e6data_python_connector/e6x_vector/__init__.py +0 -0
  35. {e6data_python_connector-2.3.10rc8 → e6data_python_connector-2.3.11}/e6data_python_connector/e6x_vector/constants.py +0 -0
  36. {e6data_python_connector-2.3.10rc8 → e6data_python_connector-2.3.11}/e6data_python_connector/exceptions.py +0 -0
  37. {e6data_python_connector-2.3.10rc8 → e6data_python_connector-2.3.11}/e6data_python_connector/server/QueryEngineService.py +0 -0
  38. {e6data_python_connector-2.3.10rc8 → e6data_python_connector-2.3.11}/e6data_python_connector/server/__init__.py +0 -0
  39. {e6data_python_connector-2.3.10rc8 → e6data_python_connector-2.3.11}/e6data_python_connector/server/constants.py +0 -0
  40. {e6data_python_connector-2.3.10rc8 → e6data_python_connector-2.3.11}/e6data_python_connector/server/e6x_engine_pb2.py +0 -0
  41. {e6data_python_connector-2.3.10rc8 → e6data_python_connector-2.3.11}/e6data_python_connector/server/e6x_engine_pb2.pyi +0 -0
  42. {e6data_python_connector-2.3.10rc8 → e6data_python_connector-2.3.11}/e6data_python_connector/server/e6x_engine_pb2_grpc.py +0 -0
  43. {e6data_python_connector-2.3.10rc8 → e6data_python_connector-2.3.11}/e6data_python_connector/server/ttypes.py +0 -0
  44. {e6data_python_connector-2.3.10rc8 → e6data_python_connector-2.3.11}/e6data_python_connector/strategy.py +0 -0
  45. {e6data_python_connector-2.3.10rc8 → e6data_python_connector-2.3.11}/e6data_python_connector/typeId.py +0 -0
  46. {e6data_python_connector-2.3.10rc8 → e6data_python_connector-2.3.11}/e6data_python_connector.egg-info/dependency_links.txt +0 -0
  47. {e6data_python_connector-2.3.10rc8 → e6data_python_connector-2.3.11}/e6data_python_connector.egg-info/entry_points.txt +0 -0
  48. {e6data_python_connector-2.3.10rc8 → e6data_python_connector-2.3.11}/pyproject.toml +0 -0
  49. {e6data_python_connector-2.3.10rc8 → e6data_python_connector-2.3.11}/setup.cfg +0 -0
  50. {e6data_python_connector-2.3.10rc8 → e6data_python_connector-2.3.11}/test/__init__.py +0 -0
  51. {e6data_python_connector-2.3.10rc8 → e6data_python_connector-2.3.11}/test/analyze_38_nines.py +0 -0
  52. {e6data_python_connector-2.3.10rc8 → e6data_python_connector-2.3.11}/test/analyze_all_cases.py +0 -0
  53. {e6data_python_connector-2.3.10rc8 → e6data_python_connector-2.3.11}/test/analyze_binary.py +0 -0
  54. {e6data_python_connector-2.3.10rc8 → e6data_python_connector-2.3.11}/test/analyze_correct_value.py +0 -0
  55. {e6data_python_connector-2.3.10rc8 → e6data_python_connector-2.3.11}/test/analyze_fields.py +0 -0
  56. {e6data_python_connector-2.3.10rc8 → e6data_python_connector-2.3.11}/test/check_decimal_errors.py +0 -0
  57. {e6data_python_connector-2.3.10rc8 → e6data_python_connector-2.3.11}/test/cleanup_test_files.py +0 -0
  58. {e6data_python_connector-2.3.10rc8 → e6data_python_connector-2.3.11}/test/debug_38_nines.py +0 -0
  59. {e6data_python_connector-2.3.10rc8 → e6data_python_connector-2.3.11}/test/debug_binary.py +0 -0
  60. {e6data_python_connector-2.3.10rc8 → e6data_python_connector-2.3.11}/test/final_test.py +0 -0
  61. {e6data_python_connector-2.3.10rc8 → e6data_python_connector-2.3.11}/test/move_tests.py +0 -0
  62. {e6data_python_connector-2.3.10rc8 → e6data_python_connector-2.3.11}/test/quick_test.py +0 -0
  63. {e6data_python_connector-2.3.10rc8 → e6data_python_connector-2.3.11}/test/test_38_nines.py +0 -0
  64. {e6data_python_connector-2.3.10rc8 → e6data_python_connector-2.3.11}/test/test_all_decimal128_cases.py +0 -0
  65. {e6data_python_connector-2.3.10rc8 → e6data_python_connector-2.3.11}/test/test_cluster_manager_efficiency.py +0 -0
  66. {e6data_python_connector-2.3.10rc8 → e6data_python_connector-2.3.11}/test/test_cluster_manager_none_strategy.py +0 -0
  67. {e6data_python_connector-2.3.10rc8 → e6data_python_connector-2.3.11}/test/test_cluster_manager_strategy.py +0 -0
  68. {e6data_python_connector-2.3.10rc8 → e6data_python_connector-2.3.11}/test/test_comprehensive.py +0 -0
  69. {e6data_python_connector-2.3.10rc8 → e6data_python_connector-2.3.11}/test/test_current_implementation.py +0 -0
  70. {e6data_python_connector-2.3.10rc8 → e6data_python_connector-2.3.11}/test/test_decimal128_binary_parsing.py +0 -0
  71. {e6data_python_connector-2.3.10rc8 → e6data_python_connector-2.3.11}/test/test_decimal128_parsing.py +0 -0
  72. {e6data_python_connector-2.3.10rc8 → e6data_python_connector-2.3.11}/test/test_fix.py +0 -0
  73. {e6data_python_connector-2.3.10rc8 → e6data_python_connector-2.3.11}/test/test_improved_parsing.py +0 -0
  74. {e6data_python_connector-2.3.10rc8 → e6data_python_connector-2.3.11}/test/test_known_case.py +0 -0
  75. {e6data_python_connector-2.3.10rc8 → e6data_python_connector-2.3.11}/test/test_manual_analysis.py +0 -0
  76. {e6data_python_connector-2.3.10rc8 → e6data_python_connector-2.3.11}/test/test_mock_server.py +0 -0
  77. {e6data_python_connector-2.3.10rc8 → e6data_python_connector-2.3.11}/test/test_multiprocessing_fix.py +0 -0
  78. {e6data_python_connector-2.3.10rc8 → e6data_python_connector-2.3.11}/test/test_new_implementation.py +0 -0
  79. {e6data_python_connector-2.3.10rc8 → e6data_python_connector-2.3.11}/test/test_specific_binary.py +0 -0
  80. {e6data_python_connector-2.3.10rc8 → e6data_python_connector-2.3.11}/test/test_strategy.py +0 -0
  81. {e6data_python_connector-2.3.10rc8 → e6data_python_connector-2.3.11}/test/test_strategy_logic.py +0 -0
  82. {e6data_python_connector-2.3.10rc8 → e6data_python_connector-2.3.11}/test/test_strategy_persistence_fix.py +0 -0
  83. {e6data_python_connector-2.3.10rc8 → e6data_python_connector-2.3.11}/test/test_strategy_sharing_fix.py +0 -0
  84. {e6data_python_connector-2.3.10rc8 → e6data_python_connector-2.3.11}/test/test_user_binary_value.py +0 -0
  85. {e6data_python_connector-2.3.10rc8 → e6data_python_connector-2.3.11}/test/tests.py +0 -0
  86. {e6data_python_connector-2.3.10rc8 → e6data_python_connector-2.3.11}/test/tests_grpc.py +0 -0
  87. {e6data_python_connector-2.3.10rc8 → e6data_python_connector-2.3.11}/test/validate_decimal128.py +0 -0
  88. {e6data_python_connector-2.3.10rc8 → e6data_python_connector-2.3.11}/test/validate_implementation.py +0 -0
  89. {e6data_python_connector-2.3.10rc8 → e6data_python_connector-2.3.11}/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.10rc8
3
+ Version: 2.3.11
4
4
  Summary: Client for the e6data distributed SQL Engine.
5
5
  Home-page: https://github.com/e6x-labs/e6data-python-connector
6
6
  Author: e6data, Inc.
@@ -16,16 +16,17 @@ Classifier: Programming Language :: Python :: 3.9
16
16
  Classifier: Programming Language :: Python :: 3.10
17
17
  Classifier: Programming Language :: Python :: 3.11
18
18
  Classifier: Programming Language :: Python :: 3.12
19
+ Classifier: Programming Language :: Python :: 3.13
19
20
  Description-Content-Type: text/markdown
20
21
  License-File: LICENSE
21
22
  Requires-Dist: sqlalchemy>=1.0.0
22
- Requires-Dist: future==1.0.0
23
- Requires-Dist: python-dateutil==2.9.0.post0
24
- Requires-Dist: pycryptodome==3.19.1
25
- Requires-Dist: pytz==2024.1
26
- Requires-Dist: thrift==0.20.0
27
- Requires-Dist: grpcio==1.65.1
28
- Requires-Dist: grpcio-tools
23
+ Requires-Dist: future>=1.0.0
24
+ Requires-Dist: python-dateutil>=2.9.0
25
+ Requires-Dist: pycryptodome>=3.19.1
26
+ Requires-Dist: pytz>=2024.1
27
+ Requires-Dist: thrift>=0.20.0
28
+ Requires-Dist: grpcio>=1.65.1
29
+ Requires-Dist: grpcio-tools>=1.65.1
29
30
  Dynamic: author
30
31
  Dynamic: author-email
31
32
  Dynamic: classifier
@@ -38,7 +39,7 @@ Dynamic: summary
38
39
 
39
40
  # e6data Python Connector
40
41
 
41
- ![version](https://img.shields.io/badge/version-2.3.10rc8-blue.svg)
42
+ ![version](https://img.shields.io/badge/version-2.3.11-blue.svg)
42
43
 
43
44
  ## Introduction
44
45
 
@@ -75,6 +76,8 @@ Use your e6data Email ID as the username and your access token as the password.
75
76
 
76
77
  ```python
77
78
  from e6data_python_connector import Connection
79
+ # For connection pooling (recommended for concurrent operations)
80
+ from e6data_python_connector import ConnectionPool
78
81
 
79
82
  username = '<username>' # Your e6data Email ID.
80
83
  password = '<password>' # Access Token generated in the e6data console.
@@ -84,6 +87,7 @@ database = '<database>' # Database to perform the query on.
84
87
  port = 80 # Port of the e6data engine.
85
88
  catalog_name = '<catalog_name>'
86
89
 
90
+ # Single connection (for simple, single-threaded use)
87
91
  conn = Connection(
88
92
  host=host,
89
93
  port=port,
@@ -91,6 +95,17 @@ conn = Connection(
91
95
  database=database,
92
96
  password=password
93
97
  )
98
+
99
+ # Or use connection pool (for concurrent/multi-threaded use)
100
+ pool = ConnectionPool(
101
+ min_size=2,
102
+ max_size=10,
103
+ host=host,
104
+ port=port,
105
+ username=username,
106
+ database=database,
107
+ password=password
108
+ )
94
109
  ```
95
110
 
96
111
  #### Connection Parameters
@@ -109,6 +124,7 @@ The `Connection` class supports the following parameters:
109
124
  | `secure` | bool | No | False | Enable SSL/TLS for secure connections |
110
125
  | `auto_resume` | bool | No | True | Automatically resume cluster if suspended |
111
126
  | `grpc_options` | dict | No | None | Additional gRPC configuration options |
127
+ | `debug` | bool | No | False | Enable debug logging for troubleshooting |
112
128
 
113
129
  #### Secure Connection Example
114
130
 
@@ -439,8 +455,241 @@ For detailed migration instructions, see the [Migration Guide](docs/zero-downtim
439
455
  - Message size optimization for large queries
440
456
 
441
457
  ### Connection Management
442
- - Enable connection pooling for better resource utilization
458
+
459
+ #### Connection Pooling
460
+
461
+ The e6data Python connector now includes a built-in connection pool for efficient connection management and reuse across multiple threads. The `ConnectionPool` class provides:
462
+
463
+ - **Thread-safe connection reuse**: Each thread automatically reuses its assigned connection
464
+ - **Automatic lifecycle management**: Handles connection creation, health checks, and cleanup
465
+ - **Overflow connections**: Creates temporary connections when pool is exhausted
466
+ - **Connection health monitoring**: Automatic detection and replacement of broken connections
467
+ - **Statistics tracking**: Monitor pool usage and performance
468
+
469
+ ##### Basic Connection Pool Usage
470
+
471
+ ```python
472
+ from e6data_python_connector import ConnectionPool
473
+
474
+ # Create a connection pool
475
+ pool = ConnectionPool(
476
+ min_size=2, # Minimum connections to maintain
477
+ max_size=10, # Maximum connections in pool
478
+ max_overflow=5, # Additional temporary connections allowed
479
+ timeout=30.0, # Timeout for getting connection (seconds)
480
+ recycle=3600, # Maximum age before recycling (seconds)
481
+ debug=False, # Enable debug logging
482
+ pre_ping=True, # Check connection health before use
483
+ # Connection parameters
484
+ host=host,
485
+ port=port,
486
+ username=username,
487
+ password=password,
488
+ database=database,
489
+ catalog=catalog_name,
490
+ cluster_name=cluster_name,
491
+ secure=True
492
+ )
493
+
494
+ # Get connection and execute query
495
+ conn = pool.get_connection()
496
+ cursor = conn.cursor()
497
+ cursor.execute("SELECT * FROM table")
498
+ results = cursor.fetchall()
499
+
500
+ # Return connection to pool (important!)
501
+ pool.return_connection(conn)
502
+
503
+ # Clean up when done
504
+ pool.close_all()
505
+ ```
506
+
507
+ ##### Using Context Manager (Recommended)
508
+
509
+ The context manager pattern ensures connections are automatically returned to the pool:
510
+
511
+ ```python
512
+ from e6data_python_connector import ConnectionPool
513
+
514
+ pool = ConnectionPool(
515
+ min_size=2,
516
+ max_size=10,
517
+ host=host,
518
+ port=port,
519
+ username=username,
520
+ password=password,
521
+ database=database
522
+ )
523
+
524
+ # Connection automatically returned to pool after use
525
+ with pool.get_connection_context() as conn:
526
+ cursor = conn.cursor()
527
+ cursor.execute("SELECT * FROM table")
528
+ results = cursor.fetchall()
529
+ print(results)
530
+ ```
531
+
532
+ ##### Concurrent Query Execution
533
+
534
+ Connection pooling is especially beneficial for concurrent query execution:
535
+
536
+ ```python
537
+ import concurrent.futures
538
+ from e6data_python_connector import ConnectionPool
539
+
540
+ def execute_query(pool, query_id, query):
541
+ """Execute a query using a pooled connection."""
542
+ # Each thread will reuse its assigned connection
543
+ conn = pool.get_connection()
544
+ try:
545
+ cursor = conn.cursor()
546
+ cursor.execute(query)
547
+ results = cursor.fetchall()
548
+ return f"Query {query_id}: {len(results)} rows"
549
+ finally:
550
+ pool.return_connection(conn)
551
+
552
+ # Create pool
553
+ pool = ConnectionPool(
554
+ min_size=3,
555
+ max_size=10,
556
+ host=host,
557
+ port=port,
558
+ username=username,
559
+ password=password,
560
+ database=database
561
+ )
562
+
563
+ # Execute multiple queries concurrently
564
+ queries = [
565
+ "SELECT COUNT(*) FROM table1",
566
+ "SELECT AVG(value) FROM table2",
567
+ "SELECT MAX(date) FROM table3"
568
+ ]
569
+
570
+ with concurrent.futures.ThreadPoolExecutor(max_workers=5) as executor:
571
+ futures = [
572
+ executor.submit(execute_query, pool, i, query)
573
+ for i, query in enumerate(queries)
574
+ ]
575
+
576
+ for future in concurrent.futures.as_completed(futures):
577
+ print(future.result())
578
+
579
+ # Clean up
580
+ pool.close_all()
581
+ ```
582
+
583
+ ##### Connection Pool Configuration
584
+
585
+ | Parameter | Type | Default | Description |
586
+ |-----------|------|---------|-------------|
587
+ | `min_size` | int | 2 | Minimum number of connections to maintain |
588
+ | `max_size` | int | 10 | Maximum number of connections in pool |
589
+ | `max_overflow` | int | 5 | Additional temporary connections allowed |
590
+ | `timeout` | float | 30.0 | Timeout for getting connection (seconds) |
591
+ | `recycle` | int | 3600 | Maximum connection age before recycling (seconds) |
592
+ | `debug` | bool | False | Enable debug logging for pool operations |
593
+ | `pre_ping` | bool | True | Check connection health before returning from pool |
594
+
595
+ ##### Monitoring Pool Statistics
596
+
597
+ ```python
598
+ # Get pool statistics
599
+ stats = pool.get_statistics()
600
+ print(f"Active connections: {stats['active_connections']}")
601
+ print(f"Idle connections: {stats['idle_connections']}")
602
+ print(f"Total requests: {stats['total_requests']}")
603
+ print(f"Failed connections: {stats['failed_connections']}")
604
+ ```
605
+
606
+ ##### When to Use Connection Pooling
607
+
608
+ Connection pooling is recommended when:
609
+ - Executing multiple queries concurrently
610
+ - Building web applications or APIs
611
+ - Running batch processing jobs
612
+ - Reducing connection overhead
613
+ - Improving application performance
614
+
615
+ ##### Direct Connection Usage (Without Pool)
616
+
617
+ For simple, single-threaded applications, you can still use direct connections:
618
+
619
+ ```python
620
+ from e6data_python_connector import Connection
621
+
622
+ conn = Connection(
623
+ host=host,
624
+ port=port,
625
+ username=username,
626
+ password=password,
627
+ database=database
628
+ )
629
+
630
+ cursor = conn.cursor()
631
+ cursor.execute("SELECT * FROM table")
632
+ results = cursor.fetchall()
633
+ conn.close()
634
+ ```
635
+
636
+ #### Additional Connection Management Features
443
637
  - Automatic connection health monitoring
444
638
  - Graceful connection recovery and retry logic
639
+ - Blue-green deployment support with automatic failover
640
+
641
+ ## Debugging and Troubleshooting
642
+
643
+ ### Enable Debug Mode
644
+
645
+ Enable comprehensive debugging to troubleshoot connection and query issues:
646
+
647
+ ```python
648
+ from e6data_python_connector import Connection
649
+
650
+ conn = Connection(
651
+ host=host,
652
+ port=port,
653
+ username=username,
654
+ password=password,
655
+ database=database,
656
+ debug=True # Enable debug logging
657
+ )
658
+ ```
659
+
660
+ When `debug=True`, the following features are enabled:
661
+ - Python logging at DEBUG level for all operations
662
+ - Blue-green strategy transition logging
663
+ - Connection lifecycle logging
664
+ - Query execution detailed logging
665
+
666
+ ### gRPC Network Tracing
667
+
668
+ For low-level gRPC network debugging (HTTP/2 frames, TCP events), set environment variables **before** running your Python script:
669
+
670
+ ```bash
671
+ # Enable gRPC network tracing
672
+ export GRPC_VERBOSITY=DEBUG
673
+ export GRPC_TRACE=client_channel,http2
674
+
675
+ # For comprehensive tracing
676
+ export GRPC_TRACE=api,call_error,channel,client_channel,connectivity_state,http,http2_stream,tcp,transport_security
677
+
678
+ # Run your script
679
+ python your_script.py
680
+ ```
681
+
682
+ **Note**: These environment variables must be set before Python starts, as the gRPC C++ core reads them at module import time.
683
+
684
+ ### Common Issues and Solutions
685
+
686
+ | Issue | Solution |
687
+ |-------|----------|
688
+ | Connection timeout | Check network connectivity, firewall rules, and ensure port 80/443 is open |
689
+ | Authentication failure | Verify username (email) and access token are correct |
690
+ | 503 Service Unavailable | Cluster may be suspended; enable `auto_resume=True` |
691
+ | 456 Strategy Error | Automatic blue-green failover will handle this |
692
+ | Memory issues with large results | Use `fetchall_buffer()` instead of `fetchall()` |
693
+ | gRPC message size errors | Configure `grpc_options` with appropriate message size limits |
445
694
 
446
695
  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.10rc8-blue.svg)
3
+ ![version](https://img.shields.io/badge/version-2.3.11-blue.svg)
4
4
 
5
5
  ## Introduction
6
6
 
@@ -37,6 +37,8 @@ Use your e6data Email ID as the username and your access token as the password.
37
37
 
38
38
  ```python
39
39
  from e6data_python_connector import Connection
40
+ # For connection pooling (recommended for concurrent operations)
41
+ from e6data_python_connector import ConnectionPool
40
42
 
41
43
  username = '<username>' # Your e6data Email ID.
42
44
  password = '<password>' # Access Token generated in the e6data console.
@@ -46,6 +48,7 @@ database = '<database>' # Database to perform the query on.
46
48
  port = 80 # Port of the e6data engine.
47
49
  catalog_name = '<catalog_name>'
48
50
 
51
+ # Single connection (for simple, single-threaded use)
49
52
  conn = Connection(
50
53
  host=host,
51
54
  port=port,
@@ -53,6 +56,17 @@ conn = Connection(
53
56
  database=database,
54
57
  password=password
55
58
  )
59
+
60
+ # Or use connection pool (for concurrent/multi-threaded use)
61
+ pool = ConnectionPool(
62
+ min_size=2,
63
+ max_size=10,
64
+ host=host,
65
+ port=port,
66
+ username=username,
67
+ database=database,
68
+ password=password
69
+ )
56
70
  ```
57
71
 
58
72
  #### Connection Parameters
@@ -71,6 +85,7 @@ The `Connection` class supports the following parameters:
71
85
  | `secure` | bool | No | False | Enable SSL/TLS for secure connections |
72
86
  | `auto_resume` | bool | No | True | Automatically resume cluster if suspended |
73
87
  | `grpc_options` | dict | No | None | Additional gRPC configuration options |
88
+ | `debug` | bool | No | False | Enable debug logging for troubleshooting |
74
89
 
75
90
  #### Secure Connection Example
76
91
 
@@ -401,8 +416,241 @@ For detailed migration instructions, see the [Migration Guide](docs/zero-downtim
401
416
  - Message size optimization for large queries
402
417
 
403
418
  ### Connection Management
404
- - Enable connection pooling for better resource utilization
419
+
420
+ #### Connection Pooling
421
+
422
+ The e6data Python connector now includes a built-in connection pool for efficient connection management and reuse across multiple threads. The `ConnectionPool` class provides:
423
+
424
+ - **Thread-safe connection reuse**: Each thread automatically reuses its assigned connection
425
+ - **Automatic lifecycle management**: Handles connection creation, health checks, and cleanup
426
+ - **Overflow connections**: Creates temporary connections when pool is exhausted
427
+ - **Connection health monitoring**: Automatic detection and replacement of broken connections
428
+ - **Statistics tracking**: Monitor pool usage and performance
429
+
430
+ ##### Basic Connection Pool Usage
431
+
432
+ ```python
433
+ from e6data_python_connector import ConnectionPool
434
+
435
+ # Create a connection pool
436
+ pool = ConnectionPool(
437
+ min_size=2, # Minimum connections to maintain
438
+ max_size=10, # Maximum connections in pool
439
+ max_overflow=5, # Additional temporary connections allowed
440
+ timeout=30.0, # Timeout for getting connection (seconds)
441
+ recycle=3600, # Maximum age before recycling (seconds)
442
+ debug=False, # Enable debug logging
443
+ pre_ping=True, # Check connection health before use
444
+ # Connection parameters
445
+ host=host,
446
+ port=port,
447
+ username=username,
448
+ password=password,
449
+ database=database,
450
+ catalog=catalog_name,
451
+ cluster_name=cluster_name,
452
+ secure=True
453
+ )
454
+
455
+ # Get connection and execute query
456
+ conn = pool.get_connection()
457
+ cursor = conn.cursor()
458
+ cursor.execute("SELECT * FROM table")
459
+ results = cursor.fetchall()
460
+
461
+ # Return connection to pool (important!)
462
+ pool.return_connection(conn)
463
+
464
+ # Clean up when done
465
+ pool.close_all()
466
+ ```
467
+
468
+ ##### Using Context Manager (Recommended)
469
+
470
+ The context manager pattern ensures connections are automatically returned to the pool:
471
+
472
+ ```python
473
+ from e6data_python_connector import ConnectionPool
474
+
475
+ pool = ConnectionPool(
476
+ min_size=2,
477
+ max_size=10,
478
+ host=host,
479
+ port=port,
480
+ username=username,
481
+ password=password,
482
+ database=database
483
+ )
484
+
485
+ # Connection automatically returned to pool after use
486
+ with pool.get_connection_context() as conn:
487
+ cursor = conn.cursor()
488
+ cursor.execute("SELECT * FROM table")
489
+ results = cursor.fetchall()
490
+ print(results)
491
+ ```
492
+
493
+ ##### Concurrent Query Execution
494
+
495
+ Connection pooling is especially beneficial for concurrent query execution:
496
+
497
+ ```python
498
+ import concurrent.futures
499
+ from e6data_python_connector import ConnectionPool
500
+
501
+ def execute_query(pool, query_id, query):
502
+ """Execute a query using a pooled connection."""
503
+ # Each thread will reuse its assigned connection
504
+ conn = pool.get_connection()
505
+ try:
506
+ cursor = conn.cursor()
507
+ cursor.execute(query)
508
+ results = cursor.fetchall()
509
+ return f"Query {query_id}: {len(results)} rows"
510
+ finally:
511
+ pool.return_connection(conn)
512
+
513
+ # Create pool
514
+ pool = ConnectionPool(
515
+ min_size=3,
516
+ max_size=10,
517
+ host=host,
518
+ port=port,
519
+ username=username,
520
+ password=password,
521
+ database=database
522
+ )
523
+
524
+ # Execute multiple queries concurrently
525
+ queries = [
526
+ "SELECT COUNT(*) FROM table1",
527
+ "SELECT AVG(value) FROM table2",
528
+ "SELECT MAX(date) FROM table3"
529
+ ]
530
+
531
+ with concurrent.futures.ThreadPoolExecutor(max_workers=5) as executor:
532
+ futures = [
533
+ executor.submit(execute_query, pool, i, query)
534
+ for i, query in enumerate(queries)
535
+ ]
536
+
537
+ for future in concurrent.futures.as_completed(futures):
538
+ print(future.result())
539
+
540
+ # Clean up
541
+ pool.close_all()
542
+ ```
543
+
544
+ ##### Connection Pool Configuration
545
+
546
+ | Parameter | Type | Default | Description |
547
+ |-----------|------|---------|-------------|
548
+ | `min_size` | int | 2 | Minimum number of connections to maintain |
549
+ | `max_size` | int | 10 | Maximum number of connections in pool |
550
+ | `max_overflow` | int | 5 | Additional temporary connections allowed |
551
+ | `timeout` | float | 30.0 | Timeout for getting connection (seconds) |
552
+ | `recycle` | int | 3600 | Maximum connection age before recycling (seconds) |
553
+ | `debug` | bool | False | Enable debug logging for pool operations |
554
+ | `pre_ping` | bool | True | Check connection health before returning from pool |
555
+
556
+ ##### Monitoring Pool Statistics
557
+
558
+ ```python
559
+ # Get pool statistics
560
+ stats = pool.get_statistics()
561
+ print(f"Active connections: {stats['active_connections']}")
562
+ print(f"Idle connections: {stats['idle_connections']}")
563
+ print(f"Total requests: {stats['total_requests']}")
564
+ print(f"Failed connections: {stats['failed_connections']}")
565
+ ```
566
+
567
+ ##### When to Use Connection Pooling
568
+
569
+ Connection pooling is recommended when:
570
+ - Executing multiple queries concurrently
571
+ - Building web applications or APIs
572
+ - Running batch processing jobs
573
+ - Reducing connection overhead
574
+ - Improving application performance
575
+
576
+ ##### Direct Connection Usage (Without Pool)
577
+
578
+ For simple, single-threaded applications, you can still use direct connections:
579
+
580
+ ```python
581
+ from e6data_python_connector import Connection
582
+
583
+ conn = Connection(
584
+ host=host,
585
+ port=port,
586
+ username=username,
587
+ password=password,
588
+ database=database
589
+ )
590
+
591
+ cursor = conn.cursor()
592
+ cursor.execute("SELECT * FROM table")
593
+ results = cursor.fetchall()
594
+ conn.close()
595
+ ```
596
+
597
+ #### Additional Connection Management Features
405
598
  - Automatic connection health monitoring
406
599
  - Graceful connection recovery and retry logic
600
+ - Blue-green deployment support with automatic failover
601
+
602
+ ## Debugging and Troubleshooting
603
+
604
+ ### Enable Debug Mode
605
+
606
+ Enable comprehensive debugging to troubleshoot connection and query issues:
607
+
608
+ ```python
609
+ from e6data_python_connector import Connection
610
+
611
+ conn = Connection(
612
+ host=host,
613
+ port=port,
614
+ username=username,
615
+ password=password,
616
+ database=database,
617
+ debug=True # Enable debug logging
618
+ )
619
+ ```
620
+
621
+ When `debug=True`, the following features are enabled:
622
+ - Python logging at DEBUG level for all operations
623
+ - Blue-green strategy transition logging
624
+ - Connection lifecycle logging
625
+ - Query execution detailed logging
626
+
627
+ ### gRPC Network Tracing
628
+
629
+ For low-level gRPC network debugging (HTTP/2 frames, TCP events), set environment variables **before** running your Python script:
630
+
631
+ ```bash
632
+ # Enable gRPC network tracing
633
+ export GRPC_VERBOSITY=DEBUG
634
+ export GRPC_TRACE=client_channel,http2
635
+
636
+ # For comprehensive tracing
637
+ export GRPC_TRACE=api,call_error,channel,client_channel,connectivity_state,http,http2_stream,tcp,transport_security
638
+
639
+ # Run your script
640
+ python your_script.py
641
+ ```
642
+
643
+ **Note**: These environment variables must be set before Python starts, as the gRPC C++ core reads them at module import time.
644
+
645
+ ### Common Issues and Solutions
646
+
647
+ | Issue | Solution |
648
+ |-------|----------|
649
+ | Connection timeout | Check network connectivity, firewall rules, and ensure port 80/443 is open |
650
+ | Authentication failure | Verify username (email) and access token are correct |
651
+ | 503 Service Unavailable | Cluster may be suspended; enable `auto_resume=True` |
652
+ | 456 Strategy Error | Automatic blue-green failover will handle this |
653
+ | Memory issues with large results | Use `fetchall_buffer()` instead of `fetchall()` |
654
+ | gRPC message size errors | Configure `grpc_options` with appropriate message size limits |
407
655
 
408
656
  See [TECH_DOC.md](TECH_DOC.md) for detailed technical documentation.
@@ -0,0 +1,4 @@
1
+ from e6data_python_connector.e6data_grpc import Connection, Cursor
2
+ from e6data_python_connector.connection_pool import ConnectionPool
3
+
4
+ __all__ = ['Connection', 'Cursor', 'ConnectionPool']