Skip to content

Commit 025e900

Browse files
authored
Feature: Health Check (#350)
* add basic healthcheck * add conditional check for health * additional health check handling * handle exit * use daemon thread
1 parent 4f498c1 commit 025e900

File tree

4 files changed

+58
-3
lines changed

4 files changed

+58
-3
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ tests/cyclic/htmlcov
4747
.coverage
4848
*,cover
4949
.benchmarks/
50+
test-output.xml
5051

5152
\#*
5253
.\#*

ansys/mapdl/core/mapdl.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1920,7 +1920,8 @@ def directory(self):
19201920
pass
19211921

19221922
# os independent path format
1923-
self._path = self._path.replace('\\', '/')
1923+
if self._path is not None:
1924+
self._path = self._path.replace('\\', '/')
19241925
return self._path
19251926

19261927
@property

ansys/mapdl/core/mapdl_grpc.py

Lines changed: 53 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,12 +17,13 @@
1717
import numpy as np
1818
from tqdm import tqdm
1919
from grpc._channel import _InactiveRpcError, _MultiThreadedRendezvous
20+
from grpc_health.v1 import health_pb2, health_pb2_grpc
2021
from ansys.grpc.mapdl import mapdl_pb2 as pb_types
2122
from ansys.grpc.mapdl import mapdl_pb2_grpc as mapdl_grpc
2223
from ansys.grpc.mapdl import ansys_kernel_pb2 as anskernel
2324

2425
from ansys.mapdl.core.mapdl import _MapdlCore
25-
from ansys.mapdl.core.errors import MapdlExitedError, protect_grpc
26+
from ansys.mapdl.core.errors import MapdlExitedError, protect_grpc, MapdlRuntimeError
2627
from ansys.mapdl.core.misc import supress_logging, run_as_prep7, last_created
2728
from ansys.mapdl.core.post import PostProcessing
2829
from ansys.mapdl.core.common_grpc import (parse_chunks,
@@ -194,6 +195,8 @@ def __init__(self, ip='127.0.0.1', port=None, timeout=15, loglevel='WARNING',
194195
self._channel_str = None
195196
self._local = ip in ['127.0.0.1', '127.0.1.1', 'localhost']
196197
self._ip = ip
198+
self._health_response_queue = None
199+
self._exiting = False
197200

198201
if port is None:
199202
from ansys.mapdl.core.launcher import MAPDL_DEFAULT_PORT
@@ -298,6 +301,12 @@ def _connect(self, port, timeout=5, set_no_abort=True):
298301
self._post = PostProcessing(self)
299302
self._xpl = ansXpl(self)
300303

304+
# TODO: version check
305+
306+
307+
# enable health check
308+
self._enable_health_check()
309+
301310
# housekeeping otherwise, many failures in a row will cause
302311
# MAPDL to exit without returning anything useful. Also
303312
# avoids abort in batch mode if set.
@@ -306,6 +315,48 @@ def _connect(self, port, timeout=5, set_no_abort=True):
306315

307316
return True
308317

318+
def _enable_health_check(self):
319+
"""Places the status of the health check in _health_response_queue"""
320+
def _consume_responses(response_iterator, response_queue):
321+
try:
322+
for response in response_iterator:
323+
response_queue.put(response)
324+
# NOTE: we're doing absolutely nothing with this as
325+
# this point since the server side health check
326+
# doesn't change state.
327+
except Exception as err:
328+
if self._exiting:
329+
return
330+
self._exited = True
331+
raise MapdlExitedError('Lost connection with MAPDL server') from None
332+
333+
# enable health check
334+
from queue import Queue
335+
request = health_pb2.HealthCheckRequest()
336+
self._health_stub = health_pb2_grpc.HealthStub(self._channel)
337+
rendezvous = self._health_stub.Watch(request)
338+
339+
# health check feature implemented after 2020R2
340+
try:
341+
status = rendezvous.next()
342+
except Exception as err:
343+
if err.code().name != 'UNIMPLEMENTED':
344+
raise err
345+
return
346+
347+
if status.status != health_pb2.HealthCheckResponse.SERVING:
348+
raise MapdlRuntimeError('Unable to enable health check and/or connect to'
349+
' the MAPDL server')
350+
351+
self._health_response_queue = Queue()
352+
353+
# allow main process to exit by setting daemon to true
354+
thread = threading.Thread(target=_consume_responses,
355+
args=(rendezvous, self._health_response_queue),
356+
daemon=True)
357+
thread.start()
358+
359+
309360
def _launch(self, start_parm):
310361
"""Launch a local session of MAPDL in gRPC mode.
311362
@@ -441,6 +492,7 @@ def exit(self, save=False):
441492
--------
442493
>>> mapdl.exit()
443494
"""
495+
self._exiting = True
444496
self._log.debug('Exiting MAPDL')
445497

446498
if save:

setup.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,10 @@
2121
'pyiges>=0.1.2',
2222
'scipy>=1.3.0', # for sparse (consider optional?)
2323
'google-api-python-client',
24-
'grpcio>=1.30.0',
24+
'grpcio>=1.30.0', # tested up to grpcio==1.35
2525
'ansys-grpc-mapdl==0.2.0',
2626
'ansys-mapdl-reader>=0.50.0',
27+
'grpcio-health-checking>=1.30.0',
2728
'ansys-corba', # pending depreciation to ansys-mapdl-corba
2829
]
2930

0 commit comments

Comments
 (0)