e6data-python-connector 2.1.21__tar.gz → 2.2.0__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.1.21/e6data_python_connector.egg-info → e6data_python_connector-2.2.0}/PKG-INFO +2 -2
- {e6data_python_connector-2.1.21 → e6data_python_connector-2.2.0}/README.md +1 -1
- e6data_python_connector-2.2.0/e6data_python_connector/cluster_manager.py +105 -0
- e6data_python_connector-2.2.0/e6data_python_connector/cluster_server/__init__.py +0 -0
- e6data_python_connector-2.2.0/e6data_python_connector/cluster_server/cluster_pb2.py +38 -0
- e6data_python_connector-2.2.0/e6data_python_connector/cluster_server/cluster_pb2_grpc.py +132 -0
- {e6data_python_connector-2.1.21 → e6data_python_connector-2.2.0}/e6data_python_connector/dialect.py +4 -1
- {e6data_python_connector-2.1.21 → e6data_python_connector-2.2.0}/e6data_python_connector/e6data_grpc.py +33 -0
- {e6data_python_connector-2.1.21 → e6data_python_connector-2.2.0/e6data_python_connector.egg-info}/PKG-INFO +2 -2
- {e6data_python_connector-2.1.21 → e6data_python_connector-2.2.0}/e6data_python_connector.egg-info/SOURCES.txt +4 -0
- {e6data_python_connector-2.1.21 → e6data_python_connector-2.2.0}/setup.py +1 -1
- {e6data_python_connector-2.1.21 → e6data_python_connector-2.2.0}/LICENSE +0 -0
- {e6data_python_connector-2.1.21 → e6data_python_connector-2.2.0}/MANIFEST.in +0 -0
- {e6data_python_connector-2.1.21 → e6data_python_connector-2.2.0}/e6data_python_connector/__init__.py +0 -0
- {e6data_python_connector-2.1.21 → e6data_python_connector-2.2.0}/e6data_python_connector/common.py +0 -0
- {e6data_python_connector-2.1.21 → e6data_python_connector-2.2.0}/e6data_python_connector/constants.py +0 -0
- {e6data_python_connector-2.1.21 → e6data_python_connector-2.2.0}/e6data_python_connector/datainputstream.py +0 -0
- {e6data_python_connector-2.1.21 → e6data_python_connector-2.2.0}/e6data_python_connector/date_time_utils.py +0 -0
- {e6data_python_connector-2.1.21 → e6data_python_connector-2.2.0}/e6data_python_connector/e6x_vector/__init__.py +0 -0
- {e6data_python_connector-2.1.21 → e6data_python_connector-2.2.0}/e6data_python_connector/e6x_vector/constants.py +0 -0
- {e6data_python_connector-2.1.21 → e6data_python_connector-2.2.0}/e6data_python_connector/e6x_vector/ttypes.py +0 -0
- {e6data_python_connector-2.1.21 → e6data_python_connector-2.2.0}/e6data_python_connector/exceptions.py +0 -0
- {e6data_python_connector-2.1.21 → e6data_python_connector-2.2.0}/e6data_python_connector/server/QueryEngineService.py +0 -0
- {e6data_python_connector-2.1.21 → e6data_python_connector-2.2.0}/e6data_python_connector/server/__init__.py +0 -0
- {e6data_python_connector-2.1.21 → e6data_python_connector-2.2.0}/e6data_python_connector/server/constants.py +0 -0
- {e6data_python_connector-2.1.21 → e6data_python_connector-2.2.0}/e6data_python_connector/server/e6x_engine_pb2.py +0 -0
- {e6data_python_connector-2.1.21 → e6data_python_connector-2.2.0}/e6data_python_connector/server/e6x_engine_pb2.pyi +0 -0
- {e6data_python_connector-2.1.21 → e6data_python_connector-2.2.0}/e6data_python_connector/server/e6x_engine_pb2_grpc.py +0 -0
- {e6data_python_connector-2.1.21 → e6data_python_connector-2.2.0}/e6data_python_connector/server/ttypes.py +0 -0
- {e6data_python_connector-2.1.21 → e6data_python_connector-2.2.0}/e6data_python_connector/typeId.py +0 -0
- {e6data_python_connector-2.1.21 → e6data_python_connector-2.2.0}/e6data_python_connector.egg-info/dependency_links.txt +0 -0
- {e6data_python_connector-2.1.21 → e6data_python_connector-2.2.0}/e6data_python_connector.egg-info/entry_points.txt +0 -0
- {e6data_python_connector-2.1.21 → e6data_python_connector-2.2.0}/e6data_python_connector.egg-info/requires.txt +0 -0
- {e6data_python_connector-2.1.21 → e6data_python_connector-2.2.0}/e6data_python_connector.egg-info/top_level.txt +0 -0
- {e6data_python_connector-2.1.21 → e6data_python_connector-2.2.0}/pyproject.toml +0 -0
- {e6data_python_connector-2.1.21 → e6data_python_connector-2.2.0}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: e6data-python-connector
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.2.0
|
|
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: Uniphi, Inc.
|
|
@@ -27,7 +27,7 @@ Requires-Dist: sqlalchemy
|
|
|
27
27
|
|
|
28
28
|
# e6data Python Connector
|
|
29
29
|
|
|
30
|
-

|
|
31
31
|
|
|
32
32
|
## Introduction
|
|
33
33
|
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import threading
|
|
2
|
+
import time
|
|
3
|
+
import e6data_python_connector.cluster_server.cluster_pb2 as cluster_pb2
|
|
4
|
+
import e6data_python_connector.cluster_server.cluster_pb2_grpc as cluster_pb2_grpc
|
|
5
|
+
import grpc
|
|
6
|
+
from grpc._channel import _InactiveRpcError
|
|
7
|
+
import multiprocessing
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class _StatusLock:
|
|
11
|
+
_LOCK_TIMEOUT = 500
|
|
12
|
+
|
|
13
|
+
def __init__(self):
|
|
14
|
+
self._status_thread_lock = threading.Lock()
|
|
15
|
+
self._status_multiprocessing_lock = multiprocessing.Semaphore()
|
|
16
|
+
self._is_active = False
|
|
17
|
+
|
|
18
|
+
@property
|
|
19
|
+
def is_active(self) -> bool:
|
|
20
|
+
return self._is_active
|
|
21
|
+
|
|
22
|
+
def set_active(self):
|
|
23
|
+
self._is_active = True
|
|
24
|
+
|
|
25
|
+
def __enter__(self):
|
|
26
|
+
self._status_thread_lock.acquire(timeout=self._LOCK_TIMEOUT)
|
|
27
|
+
self._status_multiprocessing_lock.acquire(timeout=self._LOCK_TIMEOUT)
|
|
28
|
+
return self
|
|
29
|
+
|
|
30
|
+
def __exit__(self, exc_type, exc_val, exc_tb):
|
|
31
|
+
self._status_thread_lock.release()
|
|
32
|
+
self._status_multiprocessing_lock.release()
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
status_lock = _StatusLock()
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
class ClusterManager:
|
|
39
|
+
def __init__(self, host: str, port: int, user: str, password: str, secure_channel: bool = False, timeout=60 * 3):
|
|
40
|
+
self._host = host
|
|
41
|
+
self._port = port
|
|
42
|
+
self._user = user
|
|
43
|
+
self._password = password
|
|
44
|
+
self._timeout = time.time() + timeout
|
|
45
|
+
self._secure_channel = secure_channel
|
|
46
|
+
|
|
47
|
+
@property
|
|
48
|
+
def _get_connection(self):
|
|
49
|
+
if self._secure_channel:
|
|
50
|
+
self._channel = grpc.secure_channel(
|
|
51
|
+
target='{}:{}'.format(self._host, self._port),
|
|
52
|
+
credentials=grpc.ssl_channel_credentials()
|
|
53
|
+
)
|
|
54
|
+
else:
|
|
55
|
+
self._channel = grpc.insecure_channel(
|
|
56
|
+
target='{}:{}'.format(self._host, self._port)
|
|
57
|
+
)
|
|
58
|
+
return cluster_pb2_grpc.ClusterServiceStub(self._channel)
|
|
59
|
+
|
|
60
|
+
def resume(self) -> bool:
|
|
61
|
+
"""
|
|
62
|
+
Return True if cluster came to active state else False.
|
|
63
|
+
"""
|
|
64
|
+
with status_lock as lock:
|
|
65
|
+
if lock.is_active:
|
|
66
|
+
return True
|
|
67
|
+
status_payload = cluster_pb2.ClusterStatusRequest(
|
|
68
|
+
user=self._user,
|
|
69
|
+
password=self._password
|
|
70
|
+
)
|
|
71
|
+
current_status = self._get_connection.status(status_payload)
|
|
72
|
+
if current_status.status == 'suspended':
|
|
73
|
+
payload = cluster_pb2.ResumeRequest(
|
|
74
|
+
user=self._user,
|
|
75
|
+
password=self._password
|
|
76
|
+
)
|
|
77
|
+
response = self._get_connection.resume(payload)
|
|
78
|
+
elif current_status.status == 'active':
|
|
79
|
+
return True
|
|
80
|
+
elif current_status.status != 'resuming':
|
|
81
|
+
"""
|
|
82
|
+
If cluster is in resuming state already, start watching for the status.
|
|
83
|
+
Cluster is in different state, cannot resume.
|
|
84
|
+
"""
|
|
85
|
+
return False
|
|
86
|
+
while True:
|
|
87
|
+
try:
|
|
88
|
+
status_payload = cluster_pb2.ClusterStatusRequest(
|
|
89
|
+
user=self._user,
|
|
90
|
+
password=self._password
|
|
91
|
+
)
|
|
92
|
+
response = self._get_connection.status(status_payload)
|
|
93
|
+
if response.status == 'active':
|
|
94
|
+
lock.set_active()
|
|
95
|
+
return True
|
|
96
|
+
if response.status in ['suspended', 'failed']:
|
|
97
|
+
return False
|
|
98
|
+
if time.time() > self._timeout:
|
|
99
|
+
return False
|
|
100
|
+
except _InactiveRpcError as e:
|
|
101
|
+
pass
|
|
102
|
+
time.sleep(5)
|
|
103
|
+
|
|
104
|
+
def suspend(self):
|
|
105
|
+
pass
|
|
File without changes
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
# Generated by the protocol buffer compiler. DO NOT EDIT!
|
|
3
|
+
# source: cluster.proto
|
|
4
|
+
"""Generated protocol buffer code."""
|
|
5
|
+
from google.protobuf import descriptor as _descriptor
|
|
6
|
+
from google.protobuf import descriptor_pool as _descriptor_pool
|
|
7
|
+
from google.protobuf import symbol_database as _symbol_database
|
|
8
|
+
from google.protobuf.internal import builder as _builder
|
|
9
|
+
# @@protoc_insertion_point(imports)
|
|
10
|
+
|
|
11
|
+
_sym_db = _symbol_database.Default()
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\rcluster.proto\"/\n\rResumeRequest\x12\x0c\n\x04user\x18\x01 \x01(\t\x12\x10\n\x08password\x18\x02 \x01(\t\" \n\x0eResumeResponse\x12\x0e\n\x06status\x18\x01 \x01(\t\"0\n\x0eSuspendRequest\x12\x0c\n\x04user\x18\x01 \x01(\t\x12\x10\n\x08password\x18\x02 \x01(\t\"!\n\x0fSuspendResponse\x12\x0e\n\x06status\x18\x01 \x01(\t\"6\n\x14\x43lusterStatusRequest\x12\x0c\n\x04user\x18\x01 \x01(\t\x12\x10\n\x08password\x18\x02 \x01(\t\"\'\n\x15\x43lusterStatusResponse\x12\x0e\n\x06status\x18\x01 \x01(\t2\xa2\x01\n\x0e\x43lusterService\x12)\n\x06resume\x12\x0e.ResumeRequest\x1a\x0f.ResumeResponse\x12,\n\x07suspend\x12\x0f.SuspendRequest\x1a\x10.SuspendResponse\x12\x37\n\x06status\x12\x15.ClusterStatusRequest\x1a\x16.ClusterStatusResponseB\x02P\x01\x62\x06proto3')
|
|
17
|
+
|
|
18
|
+
_globals = globals()
|
|
19
|
+
_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals)
|
|
20
|
+
_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'cluster_pb2', _globals)
|
|
21
|
+
if _descriptor._USE_C_DESCRIPTORS == False:
|
|
22
|
+
DESCRIPTOR._options = None
|
|
23
|
+
DESCRIPTOR._serialized_options = b'P\001'
|
|
24
|
+
_globals['_RESUMEREQUEST']._serialized_start=17
|
|
25
|
+
_globals['_RESUMEREQUEST']._serialized_end=64
|
|
26
|
+
_globals['_RESUMERESPONSE']._serialized_start=66
|
|
27
|
+
_globals['_RESUMERESPONSE']._serialized_end=98
|
|
28
|
+
_globals['_SUSPENDREQUEST']._serialized_start=100
|
|
29
|
+
_globals['_SUSPENDREQUEST']._serialized_end=148
|
|
30
|
+
_globals['_SUSPENDRESPONSE']._serialized_start=150
|
|
31
|
+
_globals['_SUSPENDRESPONSE']._serialized_end=183
|
|
32
|
+
_globals['_CLUSTERSTATUSREQUEST']._serialized_start=185
|
|
33
|
+
_globals['_CLUSTERSTATUSREQUEST']._serialized_end=239
|
|
34
|
+
_globals['_CLUSTERSTATUSRESPONSE']._serialized_start=241
|
|
35
|
+
_globals['_CLUSTERSTATUSRESPONSE']._serialized_end=280
|
|
36
|
+
_globals['_CLUSTERSERVICE']._serialized_start=283
|
|
37
|
+
_globals['_CLUSTERSERVICE']._serialized_end=445
|
|
38
|
+
# @@protoc_insertion_point(module_scope)
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
# Generated by the gRPC Python protocol compiler plugin. DO NOT EDIT!
|
|
2
|
+
"""Client and server classes corresponding to protobuf-defined services."""
|
|
3
|
+
import grpc
|
|
4
|
+
|
|
5
|
+
import e6data_python_connector.cluster_server.cluster_pb2 as cluster__pb2
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class ClusterServiceStub(object):
|
|
9
|
+
"""Missing associated documentation comment in .proto file."""
|
|
10
|
+
|
|
11
|
+
def __init__(self, channel):
|
|
12
|
+
"""Constructor.
|
|
13
|
+
|
|
14
|
+
Args:
|
|
15
|
+
channel: A grpc.Channel.
|
|
16
|
+
"""
|
|
17
|
+
self.resume = channel.unary_unary(
|
|
18
|
+
'/ClusterService/resume',
|
|
19
|
+
request_serializer=cluster__pb2.ResumeRequest.SerializeToString,
|
|
20
|
+
response_deserializer=cluster__pb2.ResumeResponse.FromString,
|
|
21
|
+
)
|
|
22
|
+
self.suspend = channel.unary_unary(
|
|
23
|
+
'/ClusterService/suspend',
|
|
24
|
+
request_serializer=cluster__pb2.SuspendRequest.SerializeToString,
|
|
25
|
+
response_deserializer=cluster__pb2.SuspendResponse.FromString,
|
|
26
|
+
)
|
|
27
|
+
self.status = channel.unary_unary(
|
|
28
|
+
'/ClusterService/status',
|
|
29
|
+
request_serializer=cluster__pb2.ClusterStatusRequest.SerializeToString,
|
|
30
|
+
response_deserializer=cluster__pb2.ClusterStatusResponse.FromString,
|
|
31
|
+
)
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
class ClusterServiceServicer(object):
|
|
35
|
+
"""Missing associated documentation comment in .proto file."""
|
|
36
|
+
|
|
37
|
+
def resume(self, request, context):
|
|
38
|
+
"""Missing associated documentation comment in .proto file."""
|
|
39
|
+
context.set_code(grpc.StatusCode.UNIMPLEMENTED)
|
|
40
|
+
context.set_details('Method not implemented!')
|
|
41
|
+
raise NotImplementedError('Method not implemented!')
|
|
42
|
+
|
|
43
|
+
def suspend(self, request, context):
|
|
44
|
+
"""Missing associated documentation comment in .proto file."""
|
|
45
|
+
context.set_code(grpc.StatusCode.UNIMPLEMENTED)
|
|
46
|
+
context.set_details('Method not implemented!')
|
|
47
|
+
raise NotImplementedError('Method not implemented!')
|
|
48
|
+
|
|
49
|
+
def status(self, request, context):
|
|
50
|
+
"""Missing associated documentation comment in .proto file."""
|
|
51
|
+
context.set_code(grpc.StatusCode.UNIMPLEMENTED)
|
|
52
|
+
context.set_details('Method not implemented!')
|
|
53
|
+
raise NotImplementedError('Method not implemented!')
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def add_ClusterServiceServicer_to_server(servicer, server):
|
|
57
|
+
rpc_method_handlers = {
|
|
58
|
+
'resume': grpc.unary_unary_rpc_method_handler(
|
|
59
|
+
servicer.resume,
|
|
60
|
+
request_deserializer=cluster__pb2.ResumeRequest.FromString,
|
|
61
|
+
response_serializer=cluster__pb2.ResumeResponse.SerializeToString,
|
|
62
|
+
),
|
|
63
|
+
'suspend': grpc.unary_unary_rpc_method_handler(
|
|
64
|
+
servicer.suspend,
|
|
65
|
+
request_deserializer=cluster__pb2.SuspendRequest.FromString,
|
|
66
|
+
response_serializer=cluster__pb2.SuspendResponse.SerializeToString,
|
|
67
|
+
),
|
|
68
|
+
'status': grpc.unary_unary_rpc_method_handler(
|
|
69
|
+
servicer.status,
|
|
70
|
+
request_deserializer=cluster__pb2.ClusterStatusRequest.FromString,
|
|
71
|
+
response_serializer=cluster__pb2.ClusterStatusResponse.SerializeToString,
|
|
72
|
+
),
|
|
73
|
+
}
|
|
74
|
+
generic_handler = grpc.method_handlers_generic_handler(
|
|
75
|
+
'ClusterService', rpc_method_handlers)
|
|
76
|
+
server.add_generic_rpc_handlers((generic_handler,))
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
# This class is part of an EXPERIMENTAL API.
|
|
80
|
+
class ClusterService(object):
|
|
81
|
+
"""Missing associated documentation comment in .proto file."""
|
|
82
|
+
|
|
83
|
+
@staticmethod
|
|
84
|
+
def resume(request,
|
|
85
|
+
target,
|
|
86
|
+
options=(),
|
|
87
|
+
channel_credentials=None,
|
|
88
|
+
call_credentials=None,
|
|
89
|
+
insecure=False,
|
|
90
|
+
compression=None,
|
|
91
|
+
wait_for_ready=None,
|
|
92
|
+
timeout=None,
|
|
93
|
+
metadata=None):
|
|
94
|
+
return grpc.experimental.unary_unary(request, target, '/ClusterService/resume',
|
|
95
|
+
cluster__pb2.ResumeRequest.SerializeToString,
|
|
96
|
+
cluster__pb2.ResumeResponse.FromString,
|
|
97
|
+
options, channel_credentials,
|
|
98
|
+
insecure, call_credentials, compression, wait_for_ready, timeout, metadata)
|
|
99
|
+
|
|
100
|
+
@staticmethod
|
|
101
|
+
def suspend(request,
|
|
102
|
+
target,
|
|
103
|
+
options=(),
|
|
104
|
+
channel_credentials=None,
|
|
105
|
+
call_credentials=None,
|
|
106
|
+
insecure=False,
|
|
107
|
+
compression=None,
|
|
108
|
+
wait_for_ready=None,
|
|
109
|
+
timeout=None,
|
|
110
|
+
metadata=None):
|
|
111
|
+
return grpc.experimental.unary_unary(request, target, '/ClusterService/suspend',
|
|
112
|
+
cluster__pb2.SuspendRequest.SerializeToString,
|
|
113
|
+
cluster__pb2.SuspendResponse.FromString,
|
|
114
|
+
options, channel_credentials,
|
|
115
|
+
insecure, call_credentials, compression, wait_for_ready, timeout, metadata)
|
|
116
|
+
|
|
117
|
+
@staticmethod
|
|
118
|
+
def status(request,
|
|
119
|
+
target,
|
|
120
|
+
options=(),
|
|
121
|
+
channel_credentials=None,
|
|
122
|
+
call_credentials=None,
|
|
123
|
+
insecure=False,
|
|
124
|
+
compression=None,
|
|
125
|
+
wait_for_ready=None,
|
|
126
|
+
timeout=None,
|
|
127
|
+
metadata=None):
|
|
128
|
+
return grpc.experimental.unary_unary(request, target, '/ClusterService/status',
|
|
129
|
+
cluster__pb2.ClusterStatusRequest.SerializeToString,
|
|
130
|
+
cluster__pb2.ClusterStatusResponse.FromString,
|
|
131
|
+
options, channel_credentials,
|
|
132
|
+
insecure, call_credentials, compression, wait_for_ready, timeout, metadata)
|
{e6data_python_connector-2.1.21 → e6data_python_connector-2.2.0}/e6data_python_connector/dialect.py
RENAMED
|
@@ -215,6 +215,7 @@ class E6dataDialect(default.DefaultDialect):
|
|
|
215
215
|
catalog_name = None
|
|
216
216
|
cluster_uuid = None
|
|
217
217
|
secure = False
|
|
218
|
+
auto_resume = False
|
|
218
219
|
|
|
219
220
|
def _dialect_specific_select_one(self):
|
|
220
221
|
return "NOOP"
|
|
@@ -230,6 +231,7 @@ class E6dataDialect(default.DefaultDialect):
|
|
|
230
231
|
self.catalog_name = url.query.get("catalog")
|
|
231
232
|
self.cluster_uuid = url.query.get("cluster-uuid")
|
|
232
233
|
self.secure = url.query.get("secure") == 'true'
|
|
234
|
+
self.auto_resume = url.query.get("auto-resume") == 'true'
|
|
233
235
|
if not self.catalog_name:
|
|
234
236
|
raise Exception('Please specify catalog in query parameter.')
|
|
235
237
|
|
|
@@ -242,7 +244,8 @@ class E6dataDialect(default.DefaultDialect):
|
|
|
242
244
|
"database": database,
|
|
243
245
|
"catalog": self.catalog_name,
|
|
244
246
|
"cluster_uuid": self.cluster_uuid,
|
|
245
|
-
'secure': self.secure
|
|
247
|
+
'secure': self.secure,
|
|
248
|
+
'auto_resume': self.auto_resume
|
|
246
249
|
}
|
|
247
250
|
return [], kwargs
|
|
248
251
|
|
|
@@ -16,7 +16,9 @@ from io import BytesIO
|
|
|
16
16
|
from ssl import CERT_NONE, CERT_OPTIONAL, CERT_REQUIRED
|
|
17
17
|
|
|
18
18
|
import grpc
|
|
19
|
+
from grpc._channel import _InactiveRpcError
|
|
19
20
|
|
|
21
|
+
from e6data_python_connector.cluster_manager import ClusterManager
|
|
20
22
|
from e6data_python_connector.common import DBAPITypeObject, ParamEscaper, DBAPICursor
|
|
21
23
|
from e6data_python_connector.constants import *
|
|
22
24
|
from e6data_python_connector.datainputstream import get_query_columns_info, read_rows_from_chunk
|
|
@@ -114,6 +116,7 @@ class Connection(object):
|
|
|
114
116
|
database: str = None,
|
|
115
117
|
cluster_uuid: str = None,
|
|
116
118
|
secure: bool = False,
|
|
119
|
+
auto_resume: bool = False,
|
|
117
120
|
grpc_options: dict = None,
|
|
118
121
|
):
|
|
119
122
|
"""
|
|
@@ -137,6 +140,8 @@ class Connection(object):
|
|
|
137
140
|
Cluster's uuid
|
|
138
141
|
secure: bool, Optional
|
|
139
142
|
Flag to use a secure channel for data transfer
|
|
143
|
+
auto_resume: bool, Optional
|
|
144
|
+
Flag to enable auto resume of the cluster before the query execution
|
|
140
145
|
grpc_options: dict, Optional
|
|
141
146
|
Specify gRPC configuration
|
|
142
147
|
- keepalive_timeout_ms: This parameter defines the time, in milliseconds, that a gRPC connection should remain idle before sending a keepalive ping to check if the connection is still alive.
|
|
@@ -160,6 +165,8 @@ class Connection(object):
|
|
|
160
165
|
|
|
161
166
|
self.catalog_name = catalog
|
|
162
167
|
|
|
168
|
+
self._auto_resume = auto_resume
|
|
169
|
+
|
|
163
170
|
self._keepalive_timeout_ms = 900000
|
|
164
171
|
self._max_receive_message_length = -1
|
|
165
172
|
self._max_send_message_length = 300 * 1024 * 1024 # mb
|
|
@@ -213,6 +220,32 @@ class Connection(object):
|
|
|
213
220
|
self._session_id = authenticate_response.sessionId
|
|
214
221
|
if not self._session_id:
|
|
215
222
|
raise ValueError("Invalid credentials.")
|
|
223
|
+
except _InactiveRpcError as e:
|
|
224
|
+
if self._auto_resume:
|
|
225
|
+
if e.code() == grpc.StatusCode.UNKNOWN and 'Stream removed' in e.details():
|
|
226
|
+
status = ClusterManager(
|
|
227
|
+
host=self._host,
|
|
228
|
+
port=self._port,
|
|
229
|
+
user=self.__username,
|
|
230
|
+
password=self.__password,
|
|
231
|
+
secure_channel=self._secure_channel
|
|
232
|
+
).resume()
|
|
233
|
+
if status:
|
|
234
|
+
authenticate_request = e6x_engine_pb2.AuthenticateRequest(
|
|
235
|
+
user=self.__username,
|
|
236
|
+
password=self.__password
|
|
237
|
+
)
|
|
238
|
+
authenticate_response = self._client.authenticate(
|
|
239
|
+
authenticate_request,
|
|
240
|
+
metadata=_get_grpc_header(cluster=self.cluster_uuid)
|
|
241
|
+
)
|
|
242
|
+
self._session_id = authenticate_response.sessionId
|
|
243
|
+
else:
|
|
244
|
+
raise e
|
|
245
|
+
else:
|
|
246
|
+
raise e
|
|
247
|
+
else:
|
|
248
|
+
raise e
|
|
216
249
|
except Exception as e:
|
|
217
250
|
self._channel.close()
|
|
218
251
|
raise e
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: e6data-python-connector
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.2.0
|
|
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: Uniphi, Inc.
|
|
@@ -27,7 +27,7 @@ Requires-Dist: sqlalchemy
|
|
|
27
27
|
|
|
28
28
|
# e6data Python Connector
|
|
29
29
|
|
|
30
|
-

|
|
31
31
|
|
|
32
32
|
## Introduction
|
|
33
33
|
|
|
@@ -5,6 +5,7 @@ pyproject.toml
|
|
|
5
5
|
setup.cfg
|
|
6
6
|
setup.py
|
|
7
7
|
e6data_python_connector/__init__.py
|
|
8
|
+
e6data_python_connector/cluster_manager.py
|
|
8
9
|
e6data_python_connector/common.py
|
|
9
10
|
e6data_python_connector/constants.py
|
|
10
11
|
e6data_python_connector/datainputstream.py
|
|
@@ -19,6 +20,9 @@ e6data_python_connector.egg-info/dependency_links.txt
|
|
|
19
20
|
e6data_python_connector.egg-info/entry_points.txt
|
|
20
21
|
e6data_python_connector.egg-info/requires.txt
|
|
21
22
|
e6data_python_connector.egg-info/top_level.txt
|
|
23
|
+
e6data_python_connector/cluster_server/__init__.py
|
|
24
|
+
e6data_python_connector/cluster_server/cluster_pb2.py
|
|
25
|
+
e6data_python_connector/cluster_server/cluster_pb2_grpc.py
|
|
22
26
|
e6data_python_connector/e6x_vector/__init__.py
|
|
23
27
|
e6data_python_connector/e6x_vector/constants.py
|
|
24
28
|
e6data_python_connector/e6x_vector/ttypes.py
|
|
File without changes
|
|
File without changes
|
{e6data_python_connector-2.1.21 → e6data_python_connector-2.2.0}/e6data_python_connector/__init__.py
RENAMED
|
File without changes
|
{e6data_python_connector-2.1.21 → e6data_python_connector-2.2.0}/e6data_python_connector/common.py
RENAMED
|
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.1.21 → e6data_python_connector-2.2.0}/e6data_python_connector/typeId.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|