Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Handle analytical mode in community_detection during parallel changes #400

Merged
merged 24 commits into from Dec 7, 2023

Conversation

Darych
Copy link
Contributor

@Darych Darych commented Oct 23, 2023

Description

NB: PR taken over by @antepusic; connected with memgraph/memgraph#1395

Since there are no ACID guarantees in analytical storage mode, the graph may get modified by parallel transactions while the user’s query is running. Query procedures and functions that return graph elements (nodes and relationships) should be robust to the deletion of graph elements, and this PR aims to fix that.

This PR contains changes to query modules that ensure the intended behavior:

  • Query modules written with the C and C++ APIs check the existence of values that may contain graph elements before returning them (Python API query modules do this under the hood)

Merge commit message:

Add proper handling of deleted return values for query procedures and functions ran in analytical mode

Pull request type

  • Bugfix

Related issues

Resolves memgraph/memgraph#1036 (together with memgraph/memgraph#1395)

######################################

Reviewer checklist (the reviewer checks this part)

Module/Algorithm

######################################

Comment on lines 329 to 335
if (memgraph_to_inner_id_.find(memgraph_id) == memgraph_to_inner_id_.end()) {
if (IsTransactional()) {
throw mg_exception::InvalidIDException();
}
return std::nullopt;
}
return memgraph_to_inner_id_.at(memgraph_id);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can keep a reference to iterator here, and instead of doing find, and then at, you can do find, and return element if it is there

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

@antepusic antepusic added Needs new memgraph lang: cpp Issue on C++ codebase In progress Docs needed Docs needed and removed lang: cpp Issue on C++ codebase labels Nov 6, 2023
Copy link
Collaborator

@as51340 as51340 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think good job in general, take care about duplication please :)


// Otherwise:
auto record = record_factory.NewRecord();
record.Insert(kFieldNode.data(), graph.GetNodeById(mgp::Id::FromUint(node_id)));
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cache result of calling graph.GetNodeById, no need to call it twice


// Otherwise:
auto record = record_factory.NewRecord();
record.Insert(kFieldNode.data(), graph.GetNodeById(mgp::Id::FromUint(node_id)));
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same

// As IN_MEMORY_ANALYTICAL doesn’t offer ACID guarantees, check if the graph elements in the result exist
try {
// If so, throw an exception:
const auto maybe_node = graph.GetNodeById(mgp::Id::FromUint(node_id));
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What exceptions can be thrown in GetNodeById?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

cpp/mg_utility/data_structures/graph_view.hpp Show resolved Hide resolved
Comment on lines 122 to 123
bool isTransactionalStorage = mgp::graph_is_transactional(memgraph_graph);
graph->SetIsTransactional(isTransactionalStorage);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
bool isTransactionalStorage = mgp::graph_is_transactional(memgraph_graph);
graph->SetIsTransactional(isTransactionalStorage);
graph->SetIsTransactional(mgp::graph_is_transactional(memgraph_graph));

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

@@ -1,4 +1,6 @@
#include <chrono>
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why these 2 things got included?

Copy link
Collaborator

@antepusic antepusic Nov 20, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removed – I’d originally added them to help with replicating the issue.

Comment on lines 20 to 28
auto *vertex = mgp::graph_get_vertex_by_id(graph, mgp_vertex_id{.as_int = static_cast<int64_t>(node_id)}, memory);
if (!vertex) {
if (mgp::graph_is_transactional(graph)) {
throw mg_exception::InvalidIDException();
}
// In IN_MEMORY_ANALYTICAL mode, vertices/edges may be erased by parallel transactions.
return;
}

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These code is duplicates over all modules... It would be good to respect to respect DRY principle here and abstract this code into a method.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

Comment on lines 329 to 335
if (memgraph_to_inner_id_.find(memgraph_id) == memgraph_to_inner_id_.end()) {
if (IsTransactional()) {
throw mg_exception::InvalidIDException();
}
return std::nullopt;
}
return memgraph_to_inner_id_.at(memgraph_id);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
if (memgraph_to_inner_id_.find(memgraph_id) == memgraph_to_inner_id_.end()) {
if (IsTransactional()) {
throw mg_exception::InvalidIDException();
}
return std::nullopt;
}
return memgraph_to_inner_id_.at(memgraph_id);
auto it = memgraph_to_inner_id_.find(memgraph_id);
if (it != memgraph_to_inner_id_.end()) {
return *it;
}
if (IsTransactional()) {
throw mg_exception::InvalidIDException();
}
return std::nullopt;
}

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

@as51340
Copy link
Collaborator

as51340 commented Nov 20, 2023

And you have build errors, please take care about that before requesting review again. For next time tip: it is better to request review after build errors are fixed since it gives reviewer more confidence that hey this is good and meaningful fix without negative impact on the rest of the system


// Otherwise:
auto record = record_factory.NewRecord();
record.Insert(kFieldNode, graph.GetNodeById(mgp::Id::FromUint(node_id)));
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of calling graph.GetNodeById once again, can we use maybe_node here?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

// As IN_MEMORY_ANALYTICAL doesn’t offer ACID guarantees, check if the graph elements in the result exist
try {
// If so, throw an exception:
const auto maybe_node = graph.GetNodeById(mgp::Id::FromUint(node_id));
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since the function returns not optional Node, IMO it's better to name the variable as node and reuse it later.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

// As IN_MEMORY_ANALYTICAL doesn’t offer ACID guarantees, check if the graph elements in the result exist
try {
// If so, throw an exception:
const auto maybe_node = graph.GetNodeById(mgp::Id::FromUint(node_id));
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The same comment as in katz_centrality_online_module.cpp

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

// As IN_MEMORY_ANALYTICAL doesn’t offer ACID guarantees, check if the graph elements in the result exist
try {
// If so, throw an exception:
const auto maybe_node = graph.GetNodeById(mgp::Id::FromUint(node_id));
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

cpp/mg_utility/data_structures/graph_view.hpp Show resolved Hide resolved
@Darych
Copy link
Contributor Author

Darych commented Nov 28, 2023

My initial idea was to make body of GetInnerNodeIdOpt a default implementation for GetInnerNodeId, so we will have just one implementation. I mentioned about this HERE
Currently. we have 2 implementations and users should think which one they need to use in their modules.
@antoniofilipovic what do you think?

@antepusic antepusic modified the milestones: 1.12.0, 1.13.0 Nov 28, 2023
Copy link
Contributor Author

@Darych Darych left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM. I can't approve the PR since I'm author.

@antepusic antepusic mentioned this pull request Dec 4, 2023
16 tasks
Copy link

sonarcloud bot commented Dec 5, 2023

Kudos, SonarCloud Quality Gate passed!    Quality Gate passed

Bug A 0 Bugs
Vulnerability A 0 Vulnerabilities
Security Hotspot A 0 Security Hotspots
Code Smell A 0 Code Smells

No Coverage information No Coverage information
0.7% 0.7% Duplication

@antepusic antepusic removed the request for review from Josipmrden December 5, 2023 22:36
.github/workflows/test.yml Show resolved Hide resolved
@antepusic
Copy link
Collaborator

@vpavicic As this PR is the MAGE side of memgraph/memgraph#1395, they are covered by the same docs PR (memgraph/documentation#361) and should both be linked in the same release note.

@antepusic antepusic merged commit 085bd27 into main Dec 7, 2023
6 checks passed
@antepusic antepusic deleted the handle-analytical-mode branch December 7, 2023 11:49
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Docs needed Docs needed status: ready PR is ready for review
Projects
Status: Done
Development

Successfully merging this pull request may close these issues.

Fix running mage modules/algorithms under IN_MEMORY_ANALYTICAL mode and streaming
4 participants