Skip to content

Commit ab458e4

Browse files
committed
<Vishal> fix: Auto resume
2 parents eba7e8f + a6cd913 commit ab458e4

File tree

5 files changed

+161
-22
lines changed

5 files changed

+161
-22
lines changed

README.md

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,7 @@ The `Connection` class supports the following parameters:
8585
| `secure` | bool | No | False | Enable SSL/TLS for secure connections |
8686
| `auto_resume` | bool | No | True | Automatically resume cluster if suspended |
8787
| `grpc_options` | dict | No | None | Additional gRPC configuration options |
88+
| `debug` | bool | No | False | Enable debug logging for troubleshooting |
8889

8990
#### Secure Connection Example
9091

@@ -598,4 +599,58 @@ conn.close()
598599
- Graceful connection recovery and retry logic
599600
- Blue-green deployment support with automatic failover
600601

602+
## Debugging and Troubleshooting
603+
604+
### Enable Debug Mode
605+
606+
Enable comprehensive debugging to troubleshoot connection and query issues:
607+
608+
```python
609+
from e6data_python_connector import Connection
610+
611+
conn = Connection(
612+
host=host,
613+
port=port,
614+
username=username,
615+
password=password,
616+
database=database,
617+
debug=True # Enable debug logging
618+
)
619+
```
620+
621+
When `debug=True`, the following features are enabled:
622+
- Python logging at DEBUG level for all operations
623+
- Blue-green strategy transition logging
624+
- Connection lifecycle logging
625+
- Query execution detailed logging
626+
627+
### gRPC Network Tracing
628+
629+
For low-level gRPC network debugging (HTTP/2 frames, TCP events), set environment variables **before** running your Python script:
630+
631+
```bash
632+
# Enable gRPC network tracing
633+
export GRPC_VERBOSITY=DEBUG
634+
export GRPC_TRACE=client_channel,http2
635+
636+
# For comprehensive tracing
637+
export GRPC_TRACE=api,call_error,channel,client_channel,connectivity_state,http,http2_stream,tcp,transport_security
638+
639+
# Run your script
640+
python your_script.py
641+
```
642+
643+
**Note**: These environment variables must be set before Python starts, as the gRPC C++ core reads them at module import time.
644+
645+
### Common Issues and Solutions
646+
647+
| Issue | Solution |
648+
|-------|----------|
649+
| Connection timeout | Check network connectivity, firewall rules, and ensure port 80/443 is open |
650+
| Authentication failure | Verify username (email) and access token are correct |
651+
| 503 Service Unavailable | Cluster may be suspended; enable `auto_resume=True` |
652+
| 456 Strategy Error | Automatic blue-green failover will handle this |
653+
| Memory issues with large results | Use `fetchall_buffer()` instead of `fetchall()` |
654+
| gRPC message size errors | Configure `grpc_options` with appropriate message size limits |
655+
601656
See [TECH_DOC.md](TECH_DOC.md) for detailed technical documentation.

e6data_python_connector/datainputstream.py

Lines changed: 31 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -26,16 +26,18 @@
2626
_logger = logging.getLogger(__name__)
2727

2828

29-
def _binary_to_decimal128(binary_data):
29+
def _binary_to_decimal128(binary_data, scale=None):
3030
"""
3131
Convert binary data to Decimal128.
32-
32+
3333
The binary data represents a 128-bit decimal number in IEEE 754-2008 Decimal128 format.
3434
Based on the Java implementation from e6data's JDBC driver.
35-
35+
3636
Args:
3737
binary_data (bytes): Binary representation of Decimal128
38-
38+
scale (int, optional): Scale parameter for decimal precision. If None,
39+
attempts to decode from IEEE format or defaults to 0.
40+
3941
Returns:
4042
Decimal: Python Decimal object
4143
"""
@@ -59,7 +61,7 @@ def _binary_to_decimal128(binary_data):
5961

6062
# Handle IEEE 754-2008 Decimal128 binary format
6163
if len(binary_data) == 16: # Decimal128 should be exactly 16 bytes
62-
return _decode_decimal128_binary_java_style(binary_data)
64+
return _decode_decimal128_binary_java_style(binary_data, scale)
6365
else:
6466
_logger.warning(f"Invalid Decimal128 binary length: {len(binary_data)} bytes, expected 16")
6567
return Decimal('0')
@@ -73,16 +75,17 @@ def _binary_to_decimal128(binary_data):
7375
return Decimal('0')
7476

7577

76-
def _decode_decimal128_binary_java_style(binary_data):
78+
def _decode_decimal128_binary_java_style(binary_data, scale=None):
7779
"""
7880
Decode IEEE 754-2008 Decimal128 binary format following Java implementation.
79-
81+
8082
Based on the Java implementation from e6data's JDBC driver getFieldDataFromChunk method.
8183
This method follows the same logic as the Java BigDecimal creation from ByteBuffer.
82-
84+
8385
Args:
8486
binary_data (bytes): 16-byte binary representation
85-
87+
scale (int, optional): Scale parameter for decimal precision
88+
8689
Returns:
8790
Decimal: Python Decimal object
8891
"""
@@ -103,17 +106,21 @@ def _decode_decimal128_binary_java_style(binary_data):
103106
if big_int_value == 0:
104107
return Decimal('0')
105108

106-
# The Java code creates BigDecimal from BigInteger with scale 0
107-
# This means we treat the integer value as the unscaled value
108-
# However, for Decimal128, we need to handle the scaling properly
109-
110-
# Try to create decimal directly from the integer value
111-
decimal_value = Decimal(big_int_value)
109+
# The Java code creates BigDecimal from BigInteger with the provided scale
110+
# If scale is provided, use it to create the properly scaled decimal
111+
if scale is not None:
112+
# Create decimal with the specified scale (like Java's BigDecimal constructor)
113+
# This treats big_int_value as the unscaled value
114+
decimal_value = Decimal(big_int_value) / (Decimal(10) ** scale)
115+
else:
116+
# Fallback: try to create decimal directly from the integer value
117+
decimal_value = Decimal(big_int_value)
112118

113119
# Check if this produces a reasonable decimal value
114120
# Decimal128 should represent normal decimal numbers
115-
if abs(decimal_value) < Decimal('1E-6143') or abs(decimal_value) > Decimal(
116-
'9.999999999999999999999999999999999E+6144'):
121+
# Only check range if scale was not provided (backward compatibility)
122+
if scale is None and (abs(decimal_value) < Decimal('1E-6143') or abs(decimal_value) > Decimal(
123+
'9.999999999999999999999999999999999E+6144')):
117124
# Value is outside normal Decimal128 range, try alternative interpretation
118125
return _decode_decimal128_alternative(binary_data)
119126

@@ -624,10 +631,12 @@ def get_column_from_chunk(vector: Vector) -> list:
624631
if vector.isConstantVector:
625632
# For constant vectors, get the binary data and convert it once
626633
binary_data = vector.data.numericDecimal128ConstantData.data
634+
# Get scale with backward compatibility for older engines
635+
scale = getattr(vector.data.numericDecimal128ConstantData, 'scale', None)
627636

628637
# Convert binary data to BigDecimal equivalent
629638
if binary_data:
630-
decimal_value = _binary_to_decimal128(binary_data)
639+
decimal_value = _binary_to_decimal128(binary_data, scale)
631640
else:
632641
decimal_value = Decimal('0')
633642

@@ -639,13 +648,16 @@ def get_column_from_chunk(vector: Vector) -> list:
639648
value_array.append(decimal_value)
640649
else:
641650
# For non-constant vectors, process each row individually
651+
# Get scale from decimal128Data (with backward compatibility)
652+
scale = getattr(vector.data.decimal128Data, 'scale', None)
653+
642654
for row in range(vector.size):
643655
if get_null(vector, row):
644656
value_array.append(None)
645657
continue
646658
# Get binary data for this row
647659
binary_data = vector.data.decimal128Data.data[row]
648-
decimal_value = _binary_to_decimal128(binary_data)
660+
decimal_value = _binary_to_decimal128(binary_data, scale)
649661
value_array.append(decimal_value)
650662
else:
651663
value_array.append(None)

e6data_python_connector/e6data_grpc.py

Lines changed: 47 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@
88

99
import datetime
1010
import logging
11+
import os
12+
1113
import re
1214
import sys
1315
import time
@@ -400,8 +402,51 @@ def __init__(
400402
self._debug = debug
401403
if self._debug:
402404
_debug_connections.add(id(self))
405+
406+
# Enable comprehensive debugging if debug flag is set
407+
if self._debug:
408+
# Configure root logger for DEBUG level
409+
logging.basicConfig(
410+
level=logging.DEBUG,
411+
format='[%(name)s] %(asctime)s - %(levelname)s - %(message)s',
412+
datefmt='%Y-%m-%d %H:%M:%S',
413+
force=True # Force reconfiguration even if logging is already configured
414+
)
415+
416+
# Note: gRPC C++ core tracing (GRPC_VERBOSITY and GRPC_TRACE) must be set
417+
# BEFORE the gRPC module is imported to take effect. Setting them at runtime
418+
# will not enable HTTP/2 frame logs or low-level tracing.
419+
#
420+
# To enable full gRPC network tracing, set these environment variables
421+
# before starting your Python script:
422+
# export GRPC_VERBOSITY=DEBUG
423+
# export GRPC_TRACE=client_channel,http2
424+
#
425+
# The following runtime settings only affect Python-level logging:
426+
427+
# Enable gRPC Python logging (this works at runtime)
428+
os.environ['GRPC_PYTHON_LOG_LEVEL'] = 'DEBUG'
429+
os.environ['GRPC_PYTHON_LOG_STDERR'] = '1'
430+
431+
# Ensure gRPC logger is at DEBUG level
432+
grpc_logger = logging.getLogger('grpc')
433+
grpc_logger.setLevel(logging.DEBUG)
434+
435+
# Enable gRPC transport logger
436+
grpc_transport_logger = logging.getLogger('grpc._channel')
437+
grpc_transport_logger.setLevel(logging.DEBUG)
438+
439+
# Enable gRPC server logger
440+
grpc_server_logger = logging.getLogger('grpc._server')
441+
grpc_server_logger.setLevel(logging.DEBUG)
442+
443+
# Set e6data connector logger to DEBUG
444+
e6data_logger = logging.getLogger('e6data_python_connector')
445+
e6data_logger.setLevel(logging.DEBUG)
446+
403447
_strategy_debug_log(f"Debug mode enabled for connection {id(self)}")
404-
448+
_strategy_debug_log(f"GRPC_TRACE={os.environ.get('GRPC_TRACE')}")
449+
405450
self._create_client()
406451

407452
@property
@@ -453,6 +498,7 @@ def _create_client(self):
453498
Raises:
454499
grpc.RpcError: If there is an error in creating the gRPC channel or client stub.
455500
"""
501+
456502
if self._secure_channel:
457503
self._channel = grpc.secure_channel(
458504
target='{}:{}'.format(self._host, self._port),

e6data_python_connector/e6x_vector/ttypes.py

Lines changed: 26 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

e6x_vector.thrift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,7 @@ struct Float64Data
8888
struct Decimal128Data
8989
{
9090
1: list<binary> data
91+
2: i32 scale
9192
}
9293

9394
struct VarcharData
@@ -123,6 +124,7 @@ struct NumericDecimalConstantData
123124
struct NumericDecimal128ConstantData
124125
{
125126
1: binary data
127+
2: i32 scale
126128
}
127129

128130
struct TemporalIntervalConstantData

0 commit comments

Comments
 (0)