Skip to content

Commit ba952b0

Browse files
authored
feat(interactive): Make use of all available cpus at startup (#4343)
At startup, try to make use of all available cpus on hosts. Also fix a deep copy bug in `test_robustness.py`. The CI failures are due to rust version, and will be fixed in PR: #4373
1 parent bbe9295 commit ba952b0

File tree

13 files changed

+142
-45
lines changed

13 files changed

+142
-45
lines changed

.github/workflows/pr-check.yml

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -213,11 +213,15 @@ jobs:
213213
python3 -m black --check --diff .
214214
python3 -m flake8 .
215215
popd
216-
pushd flex/interactive/sdk/python
216+
# we need to generate the code first
217+
pushd flex/interactive/sdk
218+
bash generate_sdk.sh -g python
219+
pushd python
217220
python3 -m isort --check --diff .
218221
python3 -m black --check --diff .
219222
python3 -m flake8 .
220223
popd
224+
popd
221225
222226
- name: Generate Docs
223227
shell: bash

flex/engines/graph_db/runtime/adhoc/expr_impl.h

Lines changed: 17 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -85,17 +85,23 @@ class WithInExpr : public ExprBase {
8585
WithInExpr(const ReadTransaction& txn, const Context& ctx,
8686
std::unique_ptr<ExprBase>&& key, const common::Value& array)
8787
: key_(std::move(key)) {
88-
if constexpr (std::is_same_v<T, int64_t>) {
89-
CHECK(array.item_case() == common::Value::kI64Array);
90-
size_t len = array.i64_array().item_size();
91-
for (size_t idx = 0; idx < len; ++idx) {
92-
container_.push_back(array.i64_array().item(idx));
93-
}
94-
} else if constexpr (std::is_same_v<T, int32_t>) {
95-
CHECK(array.item_case() == common::Value::kI32Array);
96-
size_t len = array.i32_array().item_size();
97-
for (size_t idx = 0; idx < len; ++idx) {
98-
container_.push_back(array.i32_array().item(idx));
88+
if constexpr ((std::is_same_v<T, int64_t>) ||
89+
(std::is_same_v<T, int32_t>) ) {
90+
// Implicitly convert to T
91+
if (array.item_case() == common::Value::kI64Array) {
92+
size_t len = array.i64_array().item_size();
93+
for (size_t idx = 0; idx < len; ++idx) {
94+
container_.push_back(array.i64_array().item(idx));
95+
}
96+
} else if (array.item_case() == common::Value::kI32Array) {
97+
size_t len = array.i32_array().item_size();
98+
for (size_t idx = 0; idx < len; ++idx) {
99+
container_.push_back(array.i32_array().item(idx));
100+
}
101+
} else {
102+
LOG(FATAL) << "Fail to construct WithInExpr of type "
103+
<< typeid(T).name() << " with array of type "
104+
<< array.item_case();
99105
}
100106
} else if constexpr (std::is_same_v<T, std::string>) {
101107
CHECK(array.item_case() == common::Value::kStrArray);

flex/engines/http_server/handler/graph_db_http_handler.cc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -176,7 +176,7 @@ class stored_proc_handler : public StoppableHandler {
176176

177177
bool start() override {
178178
if (get_executors()[StoppableHandler::shard_id()].size() > 0) {
179-
LOG(ERROR) << "The actors have been already created!";
179+
VLOG(10) << "The actors have been already created!";
180180
return false;
181181
}
182182
return StoppableHandler::start_scope(

flex/engines/http_server/handler/graph_db_http_handler.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ class StoppableHandler : public seastar::httpd::handler_base {
6969
} catch (const std::exception& e) {
7070
// In case the scope is already cancelled, we should ignore the
7171
// exception.
72-
LOG(INFO) << "Failed to cancel IC scope: " << e.what();
72+
VLOG(1) << "Failed to cancel IC scope: " << e.what();
7373
}
7474
func();
7575
return seastar::make_ready_future<>();

flex/interactive/sdk/python/gs_interactive/client/status.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
from gs_interactive.exceptions import NotFoundException
2424
from gs_interactive.exceptions import ServiceException
2525
from urllib3.exceptions import MaxRetryError
26+
from urllib3.exceptions import ProtocolError
2627

2728
from gs_interactive.client.generated.interactive_pb2 import Code as StatusCode
2829
from gs_interactive.models.api_response_with_code import APIResponseWithCode
@@ -108,6 +109,8 @@ def from_exception(exception: ApiException):
108109
return Status(StatusCode.INTERNAL_ERROR, exception.body)
109110
elif isinstance(exception, MaxRetryError):
110111
return Status(StatusCode.INTERNAL_ERROR, exception)
112+
elif isinstance(exception, ProtocolError):
113+
return Status(StatusCode.INTERNAL_ERROR, exception)
111114
return Status(
112115
StatusCode.UNKNOWN, "Unknown Error from exception " + exception.body
113116
)

flex/interactive/sdk/python/gs_interactive/tests/conftest.py

Lines changed: 27 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
#
1818

1919
# get the directory of the current file
20+
import copy
2021
import os
2122
import time
2223

@@ -27,6 +28,7 @@
2728
from gs_interactive.client.session import Session
2829
from gs_interactive.models import CreateGraphRequest
2930
from gs_interactive.models import CreateProcedureRequest
31+
from gs_interactive.models import GetGraphSchemaResponse
3032
from gs_interactive.models import SchemaMapping
3133
from gs_interactive.models import StartServiceRequest
3234
from gs_interactive.models import UpdateProcedureRequest
@@ -39,7 +41,7 @@
3941

4042

4143
modern_graph_full = {
42-
"name": "modern_graph",
44+
"name": "full_graph",
4345
"description": "This is a test graph",
4446
"schema": {
4547
"vertex_types": [
@@ -120,7 +122,7 @@
120122
}
121123

122124
modern_graph_vertex_only = {
123-
"name": "modern_graph",
125+
"name": "vertex_only",
124126
"description": "This is a test graph, only contains vertex",
125127
"schema": {
126128
"vertex_types": [
@@ -148,7 +150,7 @@
148150
}
149151

150152
modern_graph_partial = {
151-
"name": "modern_graph",
153+
"name": "partial_graph",
152154
"description": "This is a test graph",
153155
"schema": {
154156
"vertex_types": [
@@ -336,7 +338,7 @@ def create_partial_modern_graph(interactive_session):
336338

337339
@pytest.fixture(scope="function")
338340
def create_graph_with_custom_pk_name(interactive_session):
339-
modern_graph_custom_pk_name = modern_graph_full.copy()
341+
modern_graph_custom_pk_name = copy.deepcopy(modern_graph_full)
340342
for vertex_type in modern_graph_custom_pk_name["schema"]["vertex_types"]:
341343
vertex_type["properties"][0]["property_name"] = "custom_id"
342344
vertex_type["primary_keys"] = ["custom_id"]
@@ -479,3 +481,24 @@ def start_service_on_graph(interactive_session, graph_id: str):
479481
assert resp.is_ok()
480482
# wait three second to let compiler get the new graph
481483
time.sleep(3)
484+
485+
486+
def ensure_compiler_schema_ready(
487+
interactive_session, neo4j_session: Neo4jSession, graph_id: str
488+
):
489+
rel_graph_meta = interactive_session.get_graph_schema(graph_id).get_value()
490+
max_times = 10
491+
while True:
492+
if max_times == 0:
493+
raise Exception("compiler schema is not ready")
494+
res = neo4j_session.run("CALL gs.procedure.meta.schema();")
495+
val = res.single().value()
496+
compiler_graph_schema = GetGraphSchemaResponse.from_json(val)
497+
# print("compiler_graph_schema: ", compiler_graph_schema)
498+
# print("rel_graph_meta: ", rel_graph_meta)
499+
if compiler_graph_schema == rel_graph_meta:
500+
break
501+
print("compiler schema is not ready, wait for 1 second")
502+
time.sleep(1)
503+
max_times -= 1
504+
print("compiler schema is ready")

flex/interactive/sdk/python/gs_interactive/tests/test_robustness.py

Lines changed: 41 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
from gs_interactive.tests.conftest import call_procedure # noqa: E402
2828
from gs_interactive.tests.conftest import create_procedure
2929
from gs_interactive.tests.conftest import delete_procedure
30+
from gs_interactive.tests.conftest import ensure_compiler_schema_ready
3031
from gs_interactive.tests.conftest import import_data_to_full_modern_graph
3132
from gs_interactive.tests.conftest import import_data_to_partial_modern_graph
3233
from gs_interactive.tests.conftest import import_data_to_vertex_only_modern_graph
@@ -61,6 +62,9 @@ def test_query_on_vertex_only_graph(
6162
"""
6263
print("[Query on vertex only graph]")
6364
start_service_on_graph(interactive_session, create_vertex_only_modern_graph)
65+
ensure_compiler_schema_ready(
66+
interactive_session, neo4j_session, create_vertex_only_modern_graph
67+
)
6468
run_cypher_test_suite(
6569
neo4j_session, create_vertex_only_modern_graph, vertex_only_cypher_queries
6670
)
@@ -69,6 +73,10 @@ def test_query_on_vertex_only_graph(
6973
import_data_to_vertex_only_modern_graph(
7074
interactive_session, create_vertex_only_modern_graph
7175
)
76+
start_service_on_graph(interactive_session, create_vertex_only_modern_graph)
77+
ensure_compiler_schema_ready(
78+
interactive_session, neo4j_session, create_vertex_only_modern_graph
79+
)
7280
run_cypher_test_suite(
7381
neo4j_session, create_vertex_only_modern_graph, vertex_only_cypher_queries
7482
)
@@ -83,12 +91,19 @@ def test_query_on_partial_graph(
8391
print("[Query on partial graph]")
8492
# start service on new graph
8593
start_service_on_graph(interactive_session, create_partial_modern_graph)
94+
ensure_compiler_schema_ready(
95+
interactive_session, neo4j_session, create_partial_modern_graph
96+
)
8697
# try to query on the graph
8798
run_cypher_test_suite(neo4j_session, create_partial_modern_graph, cypher_queries)
8899
start_service_on_graph(interactive_session, "1")
89100
import_data_to_partial_modern_graph(
90101
interactive_session, create_partial_modern_graph
91102
)
103+
start_service_on_graph(interactive_session, create_partial_modern_graph)
104+
ensure_compiler_schema_ready(
105+
interactive_session, neo4j_session, create_partial_modern_graph
106+
)
92107
run_cypher_test_suite(neo4j_session, create_partial_modern_graph, cypher_queries)
93108

94109

@@ -100,10 +115,17 @@ def test_query_on_full_modern_graph(
100115
"""
101116
print("[Query on full modern graph]")
102117
start_service_on_graph(interactive_session, create_modern_graph)
118+
ensure_compiler_schema_ready(
119+
interactive_session, neo4j_session, create_modern_graph
120+
)
103121
# try to query on the graph
104122
run_cypher_test_suite(neo4j_session, create_modern_graph, cypher_queries)
105123
start_service_on_graph(interactive_session, "1")
106124
import_data_to_full_modern_graph(interactive_session, create_modern_graph)
125+
start_service_on_graph(interactive_session, create_modern_graph)
126+
ensure_compiler_schema_ready(
127+
interactive_session, neo4j_session, create_modern_graph
128+
)
107129
run_cypher_test_suite(neo4j_session, create_modern_graph, cypher_queries)
108130

109131

@@ -129,6 +151,9 @@ def test_service_switching(
129151
)
130152
print("Procedure id: ", a_proc_id)
131153
start_service_on_graph(interactive_session, create_modern_graph)
154+
ensure_compiler_schema_ready(
155+
interactive_session, neo4j_session, create_modern_graph
156+
)
132157
call_procedure(neo4j_session, create_modern_graph, a_proc_id)
133158

134159
# create procedure on graph_b_id
@@ -139,6 +164,9 @@ def test_service_switching(
139164
"MATCH(n: person) return count(n);",
140165
)
141166
start_service_on_graph(interactive_session, create_vertex_only_modern_graph)
167+
ensure_compiler_schema_ready(
168+
interactive_session, neo4j_session, create_vertex_only_modern_graph
169+
)
142170
call_procedure(neo4j_session, create_vertex_only_modern_graph, b_proc_id)
143171

144172

@@ -156,6 +184,9 @@ def test_procedure_creation(interactive_session, neo4j_session, create_modern_gr
156184
)
157185
print("Procedure id: ", a_proc_id)
158186
start_service_on_graph(interactive_session, create_modern_graph)
187+
ensure_compiler_schema_ready(
188+
interactive_session, neo4j_session, create_modern_graph
189+
)
159190
call_procedure(neo4j_session, create_modern_graph, a_proc_id)
160191

161192
# create procedure with name containing space,
@@ -202,6 +233,9 @@ def test_builtin_procedure(interactive_session, neo4j_session, create_modern_gra
202233
)
203234
# Call the builtin procedure
204235
start_service_on_graph(interactive_session, create_modern_graph)
236+
ensure_compiler_schema_ready(
237+
interactive_session, neo4j_session, create_modern_graph
238+
)
205239
call_procedure(
206240
neo4j_session,
207241
create_modern_graph,
@@ -259,6 +293,10 @@ def test_list_jobs(interactive_session, create_vertex_only_modern_graph):
259293
def test_call_proc_in_cypher(interactive_session, neo4j_session, create_modern_graph):
260294
print("[Test call procedure in cypher]")
261295
import_data_to_full_modern_graph(interactive_session, create_modern_graph)
296+
start_service_on_graph(interactive_session, create_modern_graph)
297+
ensure_compiler_schema_ready(
298+
interactive_session, neo4j_session, create_modern_graph
299+
)
262300
result = neo4j_session.run(
263301
'MATCH(p: person) with p.id as oid CALL k_neighbors("person", oid, 1) return label_name, vertex_oid;'
264302
)
@@ -276,6 +314,9 @@ def test_custom_pk_name(
276314
interactive_session, create_graph_with_custom_pk_name
277315
)
278316
start_service_on_graph(interactive_session, create_graph_with_custom_pk_name)
317+
ensure_compiler_schema_ready(
318+
interactive_session, neo4j_session, create_graph_with_custom_pk_name
319+
)
279320
result = neo4j_session.run(
280321
"MATCH (n: person) where n.custom_id = 4 return n.custom_id;"
281322
)
@@ -289,4 +330,3 @@ def test_custom_pk_name(
289330
)
290331
records = result.fetch(1)
291332
assert len(records) == 1 and records[0]["$f0"] == 2
292-
start_service_on_graph(interactive_session, "1")

flex/interactive/sdk/python/setup.cfg

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ profile = black
33
ensure_newline_before_comments = True
44
line_length = 88
55
force_single_line = True
6-
skip = build/,dist/,gs_interactive/api/,gs_interactive/api_response.py,gs_interactive/configuration.py,gs_interactive/exceptions.py,gs_interactive/models/,gs_interactiverest.py,
6+
skip = build/,dist/,gs_interactive/api/,gs_interactive/api_response.py,gs_interactive/configuration.py,gs_interactive/exceptions.py,gs_interactive/models/,gs_interactiverest.py,gs_interactive/api_client.py,gs_interactive/__init__.py,gs_interactive/rest.py
77
skip_glob = *_pb2.py,*_pb2_grpc.py,build/*
88

99
[flake8]

flex/storages/metadata/local_file_metadata_store.cc

Lines changed: 31 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -181,8 +181,8 @@ Result<bool> LocalFileMetadataStore::UpdateMeta(const meta_kind_t& meta_kind,
181181

182182
Result<LocalFileMetadataStore::meta_key_t>
183183
LocalFileMetadataStore::get_next_meta_key(
184-
const LocalFileMetadataStore::meta_kind_t& meta_kind) const {
185-
return std::to_string(get_max_id(meta_kind) + 1);
184+
const LocalFileMetadataStore::meta_kind_t& meta_kind) {
185+
return std::to_string(increase_and_get_id(meta_kind));
186186
}
187187

188188
std::string LocalFileMetadataStore::get_root_meta_dir() const {
@@ -208,29 +208,38 @@ std::string LocalFileMetadataStore::get_meta_file(const meta_kind_t& meta_kind,
208208
return ret;
209209
}
210210

211-
int32_t LocalFileMetadataStore::get_max_id(const meta_kind_t& meta_kind) const {
212-
// iterate all files in the directory, get the max id.
213-
int max_id_ = 0;
211+
// Guarded by meta_mutex_ outside.
212+
int32_t LocalFileMetadataStore::increase_and_get_id(
213+
const meta_kind_t& meta_kind) {
214214
auto dir = get_meta_kind_dir(meta_kind);
215-
for (auto& p : std::filesystem::directory_iterator(dir)) {
216-
if (std::filesystem::is_directory(p)) {
217-
continue;
218-
}
219-
auto file_name = p.path().filename().string();
220-
if (file_name.find(META_FILE_PREFIX) != std::string::npos) {
221-
auto id_str = file_name.substr(strlen(META_FILE_PREFIX));
222-
int32_t id;
223-
try {
224-
id = std::stoi(id_str);
225-
} catch (std::invalid_argument& e) {
226-
LOG(ERROR) << "Invalid id: " << id_str;
227-
continue;
228-
}
229-
if (id > max_id_) {
230-
max_id_ = id;
231-
}
215+
int max_id_ = 0;
216+
// In the directory, we expect a file with name CUR_ID_FILE_NAME.
217+
// If the file does not exist, we will create one with content "0".
218+
auto cur_id_file = dir + "/" + CUR_ID_FILE_NAME;
219+
if (!std::filesystem::exists(cur_id_file)) {
220+
std::ofstream out_file(cur_id_file);
221+
if (!out_file.is_open()) {
222+
LOG(ERROR) << "Failed to create file: " << cur_id_file;
223+
return -1;
232224
}
225+
out_file << "0";
226+
out_file.close();
227+
}
228+
std::ifstream in_file(cur_id_file);
229+
if (!in_file.is_open()) {
230+
LOG(ERROR) << "Failed to open file: " << cur_id_file;
231+
return -1;
233232
}
233+
in_file >> max_id_;
234+
in_file.close();
235+
max_id_++;
236+
std::ofstream out_file(cur_id_file);
237+
if (!out_file.is_open()) {
238+
LOG(ERROR) << "Failed to open file: " << cur_id_file;
239+
return -1;
240+
}
241+
out_file << max_id_;
242+
out_file.close();
234243
return max_id_;
235244
}
236245

0 commit comments

Comments
 (0)