e6data-python-connector 2.2.5rc2__py3-none-any.whl → 2.2.5rc6__py3-none-any.whl
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/cluster_manager.py +173 -37
- e6data_python_connector/e6data_grpc.py +531 -39
- e6data_python_connector/server/e6x_engine_pb2.py +108 -107
- e6data_python_connector/server/e6x_engine_pb2.pyi +127 -73
- e6data_python_connector/server/e6x_engine_pb2_grpc.py +432 -116
- e6data_python_connector/strategy.py +201 -0
- {e6data_python_connector-2.2.5rc2.dist-info → e6data_python_connector-2.2.5rc6.dist-info}/METADATA +157 -3
- e6data_python_connector-2.2.5rc6.dist-info/RECORD +44 -0
- {e6data_python_connector-2.2.5rc2.dist-info → e6data_python_connector-2.2.5rc6.dist-info}/top_level.txt +1 -0
- test/__init__.py +1 -0
- test/analyze_38_nines.py +97 -0
- test/analyze_binary.py +70 -0
- test/check_decimal_errors.py +25 -0
- test/test_decimal128_binary_parsing.py +200 -0
- test/test_decimal128_parsing.py +255 -0
- test/test_fix.py +29 -0
- test/test_improved_parsing.py +113 -0
- test/test_specific_binary.py +99 -0
- test/test_user_binary_value.py +71 -0
- test/tests.py +307 -0
- test/tests_grpc.py +155 -0
- test/validate_decimal128.py +76 -0
- test/verify_decimal_fix.py +36 -0
- e6data_python_connector-2.2.5rc2.dist-info/RECORD +0 -29
- {e6data_python_connector-2.2.5rc2.dist-info → e6data_python_connector-2.2.5rc6.dist-info}/LICENSE +0 -0
- {e6data_python_connector-2.2.5rc2.dist-info → e6data_python_connector-2.2.5rc6.dist-info}/WHEEL +0 -0
- {e6data_python_connector-2.2.5rc2.dist-info → e6data_python_connector-2.2.5rc6.dist-info}/entry_points.txt +0 -0
|
@@ -5,28 +5,32 @@ import e6data_python_connector.cluster_server.cluster_pb2_grpc as cluster_pb2_gr
|
|
|
5
5
|
import grpc
|
|
6
6
|
from grpc._channel import _InactiveRpcError
|
|
7
7
|
import multiprocessing
|
|
8
|
+
import logging
|
|
8
9
|
|
|
10
|
+
# Import strategy management functions
|
|
11
|
+
from e6data_python_connector.strategy import _get_active_strategy, _set_active_strategy, _set_pending_strategy, _get_grpc_header as _get_strategy_header
|
|
9
12
|
|
|
10
|
-
|
|
13
|
+
# Set up logging
|
|
14
|
+
_logger = logging.getLogger(__name__)
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def _get_grpc_header(engine_ip=None, cluster=None, strategy=None):
|
|
11
18
|
"""
|
|
12
19
|
Generate gRPC metadata headers for the request.
|
|
13
20
|
|
|
14
21
|
This function creates a list of metadata headers to be used in gRPC requests.
|
|
15
|
-
It includes optional headers for the engine IP and
|
|
22
|
+
It includes optional headers for the engine IP, cluster UUID, and deployment strategy.
|
|
16
23
|
|
|
17
24
|
Args:
|
|
18
25
|
engine_ip (str, optional): The IP address of the engine. Defaults to None.
|
|
19
26
|
cluster (str, optional): The UUID of the cluster. Defaults to None.
|
|
27
|
+
strategy (str, optional): The deployment strategy (blue/green). Defaults to None.
|
|
20
28
|
|
|
21
29
|
Returns:
|
|
22
30
|
list: A list of tuples representing the gRPC metadata headers.
|
|
23
31
|
"""
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
metadata.append(('plannerip', engine_ip))
|
|
27
|
-
if cluster:
|
|
28
|
-
metadata.append(('cluster-uuid', cluster))
|
|
29
|
-
return metadata
|
|
32
|
+
# Use the strategy module's implementation
|
|
33
|
+
return _get_strategy_header(engine_ip=engine_ip, cluster=cluster, strategy=strategy)
|
|
30
34
|
|
|
31
35
|
|
|
32
36
|
class _StatusLock:
|
|
@@ -187,22 +191,162 @@ class ClusterManager:
|
|
|
187
191
|
)
|
|
188
192
|
return cluster_pb2_grpc.ClusterServiceStub(self._channel)
|
|
189
193
|
|
|
190
|
-
def
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
194
|
+
def _try_cluster_request(self, request_type, payload=None):
|
|
195
|
+
"""
|
|
196
|
+
Execute a cluster request with strategy fallback for 456 errors.
|
|
197
|
+
|
|
198
|
+
For efficiency:
|
|
199
|
+
- If we have an active strategy, use it first
|
|
200
|
+
- Only try authentication sequence (blue -> green) if no active strategy
|
|
201
|
+
- On 456 error, switch to alternative strategy and update active strategy
|
|
202
|
+
|
|
203
|
+
Args:
|
|
204
|
+
request_type: Type of request ('status' or 'resume')
|
|
205
|
+
payload: Request payload (optional, will be created if not provided)
|
|
206
|
+
|
|
207
|
+
Returns:
|
|
208
|
+
The response from the successful request
|
|
209
|
+
"""
|
|
210
|
+
current_strategy = _get_active_strategy()
|
|
211
|
+
|
|
212
|
+
# Create payload if not provided
|
|
213
|
+
if payload is None:
|
|
214
|
+
if request_type == "status":
|
|
215
|
+
payload = cluster_pb2.ClusterStatusRequest(
|
|
195
216
|
user=self._user,
|
|
196
217
|
password=self._password
|
|
197
218
|
)
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
219
|
+
elif request_type == "resume":
|
|
220
|
+
payload = cluster_pb2.ResumeRequest(
|
|
221
|
+
user=self._user,
|
|
222
|
+
password=self._password
|
|
202
223
|
)
|
|
203
|
-
|
|
204
|
-
|
|
224
|
+
|
|
225
|
+
# If we have an active strategy, use it first
|
|
226
|
+
if current_strategy is not None:
|
|
227
|
+
try:
|
|
228
|
+
_logger.info(f"ClusterManager: Trying {request_type} with established strategy: {current_strategy}")
|
|
229
|
+
|
|
230
|
+
if request_type == "status":
|
|
231
|
+
response = self._get_connection.status(
|
|
232
|
+
payload,
|
|
233
|
+
metadata=_get_grpc_header(cluster=self.cluster_uuid, strategy=current_strategy)
|
|
234
|
+
)
|
|
235
|
+
elif request_type == "resume":
|
|
236
|
+
response = self._get_connection.resume(
|
|
237
|
+
payload,
|
|
238
|
+
metadata=_get_grpc_header(cluster=self.cluster_uuid, strategy=current_strategy)
|
|
239
|
+
)
|
|
240
|
+
else:
|
|
241
|
+
raise ValueError(f"Unknown request type: {request_type}")
|
|
242
|
+
|
|
243
|
+
# Check for new strategy in response
|
|
244
|
+
if hasattr(response, 'new_strategy') and response.new_strategy:
|
|
245
|
+
new_strategy = response.new_strategy.lower()
|
|
246
|
+
if new_strategy != current_strategy:
|
|
247
|
+
_logger.info(f"ClusterManager: Server indicated new strategy during {request_type}: {new_strategy}")
|
|
248
|
+
_set_pending_strategy(new_strategy)
|
|
249
|
+
|
|
250
|
+
return response
|
|
251
|
+
|
|
252
|
+
except _InactiveRpcError as e:
|
|
253
|
+
if e.code() == grpc.StatusCode.UNKNOWN and 'status: 456' in e.details():
|
|
254
|
+
# 456 error - switch to alternative strategy
|
|
255
|
+
alternative_strategy = 'green' if current_strategy == 'blue' else 'blue'
|
|
256
|
+
_logger.info(f"ClusterManager: {request_type} failed with 456 error on {current_strategy}, switching to: {alternative_strategy}")
|
|
257
|
+
|
|
258
|
+
try:
|
|
259
|
+
if request_type == "status":
|
|
260
|
+
response = self._get_connection.status(
|
|
261
|
+
payload,
|
|
262
|
+
metadata=_get_grpc_header(cluster=self.cluster_uuid, strategy=alternative_strategy)
|
|
263
|
+
)
|
|
264
|
+
elif request_type == "resume":
|
|
265
|
+
response = self._get_connection.resume(
|
|
266
|
+
payload,
|
|
267
|
+
metadata=_get_grpc_header(cluster=self.cluster_uuid, strategy=alternative_strategy)
|
|
268
|
+
)
|
|
269
|
+
|
|
270
|
+
# Update active strategy since the alternative worked
|
|
271
|
+
_set_active_strategy(alternative_strategy)
|
|
272
|
+
_logger.info(f"ClusterManager: {request_type} succeeded with alternative strategy: {alternative_strategy}")
|
|
273
|
+
|
|
274
|
+
# Check for new strategy in response
|
|
275
|
+
if hasattr(response, 'new_strategy') and response.new_strategy:
|
|
276
|
+
new_strategy = response.new_strategy.lower()
|
|
277
|
+
if new_strategy != alternative_strategy:
|
|
278
|
+
_logger.info(f"ClusterManager: Server indicated new strategy during {request_type}: {new_strategy}")
|
|
279
|
+
_set_pending_strategy(new_strategy)
|
|
280
|
+
|
|
281
|
+
return response
|
|
282
|
+
|
|
283
|
+
except _InactiveRpcError as e2:
|
|
284
|
+
_logger.error(f"ClusterManager: Both strategies failed for {request_type}. Original error: {e}, Alternative error: {e2}")
|
|
285
|
+
raise e # Raise the original error
|
|
286
|
+
else:
|
|
287
|
+
# Non-456 error - don't retry
|
|
288
|
+
_logger.error(f"ClusterManager: {request_type} failed with non-456 error: {e}")
|
|
289
|
+
raise e
|
|
290
|
+
|
|
291
|
+
# No active strategy - start with authentication logic (blue first, then green)
|
|
292
|
+
_logger.info(f"ClusterManager: No active strategy, starting authentication sequence for {request_type}")
|
|
293
|
+
strategies_to_try = ['blue', 'green']
|
|
294
|
+
|
|
295
|
+
for i, strategy in enumerate(strategies_to_try):
|
|
296
|
+
try:
|
|
297
|
+
_logger.info(f"ClusterManager: Trying {request_type} with strategy: {strategy}")
|
|
298
|
+
|
|
299
|
+
if request_type == "status":
|
|
300
|
+
response = self._get_connection.status(
|
|
301
|
+
payload,
|
|
302
|
+
metadata=_get_grpc_header(cluster=self.cluster_uuid, strategy=strategy)
|
|
303
|
+
)
|
|
304
|
+
elif request_type == "resume":
|
|
305
|
+
response = self._get_connection.resume(
|
|
306
|
+
payload,
|
|
307
|
+
metadata=_get_grpc_header(cluster=self.cluster_uuid, strategy=strategy)
|
|
308
|
+
)
|
|
309
|
+
else:
|
|
310
|
+
raise ValueError(f"Unknown request type: {request_type}")
|
|
311
|
+
|
|
312
|
+
# Set the working strategy as active
|
|
313
|
+
_set_active_strategy(strategy)
|
|
314
|
+
_logger.info(f"ClusterManager: {request_type} succeeded with strategy: {strategy}")
|
|
315
|
+
|
|
316
|
+
# Check for new strategy in response
|
|
317
|
+
if hasattr(response, 'new_strategy') and response.new_strategy:
|
|
318
|
+
new_strategy = response.new_strategy.lower()
|
|
319
|
+
if new_strategy != strategy:
|
|
320
|
+
_logger.info(f"ClusterManager: Server indicated new strategy during {request_type}: {new_strategy}")
|
|
321
|
+
_set_pending_strategy(new_strategy)
|
|
322
|
+
|
|
323
|
+
return response
|
|
324
|
+
|
|
205
325
|
except _InactiveRpcError as e:
|
|
326
|
+
if e.code() == grpc.StatusCode.UNKNOWN and 'status: 456' in e.details():
|
|
327
|
+
# 456 error - try next strategy
|
|
328
|
+
if i < len(strategies_to_try) - 1:
|
|
329
|
+
_logger.info(f"ClusterManager: {request_type} failed with 456 error on {strategy}, trying next strategy: {strategies_to_try[i + 1]}")
|
|
330
|
+
continue
|
|
331
|
+
else:
|
|
332
|
+
_logger.error(f"ClusterManager: {request_type} failed with 456 error on all strategies")
|
|
333
|
+
raise e
|
|
334
|
+
else:
|
|
335
|
+
# Non-456 error - don't retry
|
|
336
|
+
_logger.error(f"ClusterManager: {request_type} failed with non-456 error: {e}")
|
|
337
|
+
raise e
|
|
338
|
+
|
|
339
|
+
# If we get here, all strategies failed
|
|
340
|
+
_logger.error(f"ClusterManager: All strategies failed for {request_type}")
|
|
341
|
+
raise e
|
|
342
|
+
|
|
343
|
+
def _check_cluster_status(self):
|
|
344
|
+
while True:
|
|
345
|
+
try:
|
|
346
|
+
# Use the unified strategy-aware request method
|
|
347
|
+
response = self._try_cluster_request("status")
|
|
348
|
+
yield response.status
|
|
349
|
+
except _InactiveRpcError:
|
|
206
350
|
yield None
|
|
207
351
|
|
|
208
352
|
def resume(self) -> bool:
|
|
@@ -237,25 +381,17 @@ class ClusterManager:
|
|
|
237
381
|
if lock.is_active:
|
|
238
382
|
return True
|
|
239
383
|
|
|
240
|
-
# Retrieve the current cluster status
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
current_status = self._get_connection.status(
|
|
246
|
-
status_payload,
|
|
247
|
-
metadata=_get_grpc_header(cluster=self.cluster_uuid)
|
|
248
|
-
)
|
|
384
|
+
# Retrieve the current cluster status with strategy header
|
|
385
|
+
try:
|
|
386
|
+
current_status = self._try_cluster_request("status")
|
|
387
|
+
except _InactiveRpcError:
|
|
388
|
+
return False
|
|
249
389
|
if current_status.status == 'suspended':
|
|
250
|
-
# Send the resume request
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
response = self._get_connection.resume(
|
|
256
|
-
payload,
|
|
257
|
-
metadata=_get_grpc_header(cluster=self.cluster_uuid)
|
|
258
|
-
)
|
|
390
|
+
# Send the resume request with strategy header
|
|
391
|
+
try:
|
|
392
|
+
response = self._try_cluster_request("resume")
|
|
393
|
+
except _InactiveRpcError:
|
|
394
|
+
return False
|
|
259
395
|
elif current_status.status == 'active':
|
|
260
396
|
return True
|
|
261
397
|
elif current_status.status != 'resuming':
|