Skip to content

Commit c020046

Browse files
authored
feat(coordinator): Add cypher endpoint info when list_service_status for groot (#4382)
- Add cypher_endpoint for GrootClient. - Rename `gremlin_interface` to `groot_endpoints`.
1 parent 0c3d19e commit c020046

File tree

6 files changed

+72
-19
lines changed

6 files changed

+72
-19
lines changed

charts/graphscope-store/templates/portal/statefulset.yaml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,8 @@ spec:
105105
value: {{ .Values.frontend.service.servicePort | quote }}
106106
- name: GROOT_GREMLIN_PORT
107107
value: {{ .Values.frontend.service.gremlinPort | quote }}
108+
- name: GROOT_CYPHER_PORT
109+
value: {{ .Values.frontend.service.cypherPort | quote }}
108110
- name: INSTANCE_NAME
109111
value: {{ .Release.Name | quote }}
110112
- name: NAMESPACE

charts/graphscope-store/values.yaml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,8 @@ frontend:
137137

138138
gremlinPort: 12312
139139

140+
cypherPort: 7687
141+
140142
## Internal port for communication between components.
141143
##
142144
port: 55555

coordinator/gscoordinator/flex/core/config.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,7 @@ def str_to_bool(s):
101101
# groot
102102
GROOT_GRPC_PORT = os.environ.get("GROOT_GRPC_PORT", 55556)
103103
GROOT_GREMLIN_PORT = os.environ.get("GROOT_GREMLIN_PORT", 12312)
104+
GROOT_CYPHER_PORT = os.environ.get("GROOT_CYPHER_PORT", 7687)
104105
GROOT_USERNAME = os.environ.get("GROOT_USERNAME", "")
105106
GROOT_PASSWORD = os.environ.get("GROOT_PASSWORD", "")
106107
try:

coordinator/gscoordinator/flex/core/insight/graph.py

Lines changed: 20 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -28,12 +28,14 @@
2828

2929
from gscoordinator.flex.core.config import CLUSTER_TYPE
3030
from gscoordinator.flex.core.config import CREATION_TIME
31+
from gscoordinator.flex.core.config import GROOT_CYPHER_PORT
3132
from gscoordinator.flex.core.config import GROOT_GREMLIN_PORT
3233
from gscoordinator.flex.core.config import GROOT_GRPC_PORT
3334
from gscoordinator.flex.core.config import GROOT_PASSWORD
3435
from gscoordinator.flex.core.config import GROOT_USERNAME
3536
from gscoordinator.flex.core.config import INSTANCE_NAME
3637
from gscoordinator.flex.core.config import NAMESPACE
38+
from gscoordinator.flex.core.insight.utils import test_cypher_endpoint
3739
from gscoordinator.flex.core.scheduler import schedule
3840
from gscoordinator.flex.core.utils import data_type_to_groot
3941
from gscoordinator.flex.core.utils import get_internal_ip
@@ -46,7 +48,7 @@
4648
class GrootGraph(object):
4749
"""Graph class for GraphScope store"""
4850

49-
def __init__(self, name, creation_time, gremlin_endpoint, grpc_endpoint):
51+
def __init__(self, name, creation_time, gremlin_endpoint, grpc_endpoint, cypher_endpoint = None):
5052
self._id = "1"
5153
self._name = name
5254

@@ -60,12 +62,14 @@ def __init__(self, name, creation_time, gremlin_endpoint, grpc_endpoint):
6062
)
6163
self._g = self._conn.g()
6264
self._schema = self._g.schema().to_dict()
63-
self._gremlin_interface = {
65+
self._endpoints = {
6466
"gremlin_endpoint": gremlin_endpoint,
6567
"grpc_endpoint": grpc_endpoint,
6668
"username": GROOT_USERNAME,
6769
"password": GROOT_PASSWORD,
6870
}
71+
self._endpoints["cypher_endpoint"] = cypher_endpoint
72+
6973
# kubernetes
7074
if CLUSTER_TYPE == "KUBERNETES":
7175
self._api_client = resolve_api_client()
@@ -97,22 +101,27 @@ def _fetch_endpoints_impl(self):
97101
grpc_endpoint, gremlin_endpoint, GROOT_USERNAME, GROOT_PASSWORD
98102
)
99103
g = conn.g()
104+
cypher_endpoint = test_cypher_endpoint(pod.status.pod_ip, GROOT_CYPHER_PORT)
105+
100106
except Exception as e:
101107
logger.warn(f"Failed to fetch frontend endpoints: {str(e)}")
102108
else:
103109
if (
104-
gremlin_endpoint != self._gremlin_interface["gremlin_endpoint"]
105-
or grpc_endpoint != self._gremlin_interface["grpc_endpoint"]
110+
gremlin_endpoint != self._endpoints["gremlin_endpoint"]
111+
or grpc_endpoint != self._endpoints["grpc_endpoint"]
112+
or cypher_endpoint != self._endpoints.get("cypher_endpoint")
106113
):
107114
self._conn = conn
108115
self._g = g
109116
self._schema = self._g.schema().to_dict()
110-
self._gremlin_interface = {
117+
self._endpoints = {
111118
"gremlin_endpoint": gremlin_endpoint,
112119
"grpc_endpoint": grpc_endpoint,
113120
"username": GROOT_USERNAME,
114121
"password": GROOT_PASSWORD,
115122
}
123+
if cypher_endpoint:
124+
self._endpoints["cypher_endpoint"] = cypher_endpoint
116125
logger.info(f"Update frontend endpoints: {str(endpoints)}")
117126

118127
def __del__(self):
@@ -131,8 +140,8 @@ def name(self):
131140
return self._name
132141

133142
@property
134-
def gremlin_interface(self):
135-
return self._gremlin_interface
143+
def groot_endpoints(self):
144+
return self._endpoints
136145

137146
@property
138147
def schema(self):
@@ -284,12 +293,16 @@ def get_groot_graph_from_local():
284293
client.close()
285294
break
286295
time.sleep(5)
296+
# test whether cypher endpoint is ready
297+
cypher_endpoint = test_cypher_endpoint(host, GROOT_CYPHER_PORT)
298+
287299
# groot graph
288300
return GrootGraph(
289301
name=INSTANCE_NAME,
290302
creation_time=CREATION_TIME,
291303
gremlin_endpoint=gremlin_endpoint,
292304
grpc_endpoint=grpc_endpoint,
305+
cypher_endpoint=cypher_endpoint,
293306
)
294307

295308

coordinator/gscoordinator/flex/core/insight/groot.py

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -99,18 +99,21 @@ def check_graph_exists(self, graph_id: str):
9999
raise RuntimeError(f"Graph {graph_id} not exist.")
100100

101101
def list_service_status(self) -> List[dict]:
102-
gremlin_interface = self._graph.gremlin_interface
103-
return [
102+
groot_endpoints = self._graph.groot_endpoints
103+
res = [
104104
{
105105
"graph_id": self._graph.id,
106106
"status": "Running",
107107
"start_time": CREATION_TIME,
108108
"sdk_endpoints": {
109-
"gremlin": gremlin_interface["gremlin_endpoint"],
110-
"grpc": gremlin_interface["grpc_endpoint"],
109+
"gremlin": groot_endpoints["gremlin_endpoint"],
110+
"grpc": groot_endpoints["grpc_endpoint"],
111111
},
112112
}
113113
]
114+
if "cypher_endpoint" in groot_endpoints and groot_endpoints["cypher_endpoint"]:
115+
res[0]["sdk_endpoints"]["cypher"] = groot_endpoints["cypher_endpoint"]
116+
return res
114117

115118
def create_graph(self, graph: dict) -> dict:
116119
raise RuntimeError("Create graph is not supported yet.")
@@ -284,12 +287,12 @@ def get_storage_usage(self) -> dict:
284287

285288
def gremlin_service_available(self) -> bool:
286289
try:
287-
gremlin_interface = self._graph.gremlin_interface
290+
groot_endpoints = self._graph.groot_endpoints
288291
client = Client(
289-
gremlin_interface["gremlin_endpoint"],
292+
groot_endpoints["gremlin_endpoint"],
290293
"g",
291-
username=gremlin_interface["username"],
292-
password=gremlin_interface["password"],
294+
username=groot_endpoints["username"],
295+
password=groot_endpoints["password"],
293296
)
294297
client.submit(
295298
"g.with('evaluationTimeout', 5000).V().limit(1)"

coordinator/gscoordinator/flex/core/insight/utils.py

Lines changed: 36 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,14 +18,19 @@
1818

1919
import hashlib
2020
import json
21+
import logging
22+
23+
from urllib3.exceptions import ProtocolError
2124

2225
from gscoordinator.flex.core.config import BASEID
2326
from gscoordinator.version import __version__
2427

28+
logger = logging.getLogger("graphscope")
29+
2530

2631
def convert_to_configini(graph, ds_manager, config):
2732
# for bulk loader to connect to groot
28-
gremlin_interface = graph.gremlin_interface
33+
groot_endpoints = graph.groot_endpoints
2934
# column mapping config
3035
column_mapping_config = {}
3136
# project
@@ -112,12 +117,12 @@ def convert_to_configini(graph, ds_manager, config):
112117
# custom_config
113118
custom_config = {
114119
"separatr": "\\\\|", # fixed
115-
"graphEndpoint": gremlin_interface["grpc_endpoint"],
120+
"graphEndpoint": groot_endpoints["grpc_endpoint"],
116121
"project": project,
117122
"outputTable": output_table,
118123
"columnMappingConfig": json.dumps(column_mapping_config),
119-
"authUsername": gremlin_interface["username"],
120-
"authPassword": gremlin_interface["password"],
124+
"authUsername": groot_endpoints["username"],
125+
"authPassword": groot_endpoints["password"],
121126
"dataSinkType": "volume",
122127
"odpsVolumeProject": project,
123128
# "-" is not allowed
@@ -136,3 +141,30 @@ def convert_to_configini(graph, ds_manager, config):
136141
"customConfig": custom_config,
137142
}
138143
return configini
144+
145+
def test_cypher_endpoint(host : str, port : int):
146+
"""
147+
Test if the cypher endpoint is available, if not return None, otherwise return the cypher endpoint
148+
Note that we send http request to check if the cypher endpoint is available, not submitting a cypher query,
149+
the reason is that the cypher query may raise exceptions in case of other errors.
150+
"""
151+
cypher_endpoint = f"neo4j://{host}:{port}"
152+
try:
153+
import requests
154+
response = requests.get(f"http://{host}:{port}")
155+
response.raise_for_status()
156+
except (requests.exceptions.ConnectionError) as e:
157+
if (e.args != None and len(e.args) > 0):
158+
# Sending http request to cypher endpoint should fail with ProtocolError
159+
if isinstance(e.args[0], ProtocolError):
160+
logger.debug("Cypher endpoint is available: {cypher_endpoint}")
161+
else:
162+
cypher_endpoint = None
163+
logger.debug(f"Cypher endpoint is not available: {str(e)}")
164+
except Exception as e:
165+
logger.debug(f"Cypher endpoint is not available: {str(e)}")
166+
cypher_endpoint = None
167+
return cypher_endpoint
168+
else:
169+
logger.error("Should not reach here")
170+
return cypher_endpoint

0 commit comments

Comments
 (0)