Fix remote pull
Summary: - Extracted TypedValue reconstruction into utils (will need that soon) - Added more error handling in RemotePull Reviewers: teon.banek, msantl Reviewed By: teon.banek Subscribers: pullbot Differential Revision: https://phabricator.memgraph.io/D1183
This commit is contained in:
parent
e2f5bcf96d
commit
45da645a4b
@ -12,6 +12,7 @@
|
|||||||
#include "distributed/plan_consumer.hpp"
|
#include "distributed/plan_consumer.hpp"
|
||||||
#include "distributed/remote_pull_produce_rpc_messages.hpp"
|
#include "distributed/remote_pull_produce_rpc_messages.hpp"
|
||||||
#include "query/context.hpp"
|
#include "query/context.hpp"
|
||||||
|
#include "query/exceptions.hpp"
|
||||||
#include "query/frontend/semantic/symbol_table.hpp"
|
#include "query/frontend/semantic/symbol_table.hpp"
|
||||||
#include "query/interpret/frame.hpp"
|
#include "query/interpret/frame.hpp"
|
||||||
#include "query/parameters.hpp"
|
#include "query/parameters.hpp"
|
||||||
@ -47,16 +48,30 @@ class RemoteProduceRpcServer {
|
|||||||
/** Returns a vector of typed values (one for each `pull_symbol`), and a
|
/** Returns a vector of typed values (one for each `pull_symbol`), and a
|
||||||
* `bool` indicating if the pull was successful (or the cursor is
|
* `bool` indicating if the pull was successful (or the cursor is
|
||||||
* exhausted). */
|
* exhausted). */
|
||||||
std::pair<std::vector<query::TypedValue>, bool> Pull() {
|
std::pair<std::vector<query::TypedValue>, RemotePullState> Pull() {
|
||||||
|
RemotePullState state = RemotePullState::CURSOR_IN_PROGRESS;
|
||||||
std::vector<query::TypedValue> results;
|
std::vector<query::TypedValue> results;
|
||||||
auto success = cursor_->Pull(frame_, context_);
|
try {
|
||||||
if (success) {
|
if (cursor_->Pull(frame_, context_)) {
|
||||||
results.reserve(pull_symbols_.size());
|
results.reserve(pull_symbols_.size());
|
||||||
for (const auto &symbol : pull_symbols_) {
|
for (const auto &symbol : pull_symbols_) {
|
||||||
results.emplace_back(std::move(frame_[symbol]));
|
results.emplace_back(std::move(frame_[symbol]));
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
state = RemotePullState::CURSOR_EXHAUSTED;
|
||||||
}
|
}
|
||||||
return std::make_pair(std::move(results), success);
|
} catch (const mvcc::SerializationError &) {
|
||||||
|
state = RemotePullState::SERIALIZATION_ERROR;
|
||||||
|
} catch (const LockTimeoutException &) {
|
||||||
|
state = RemotePullState::LOCK_TIMEOUT_ERROR;
|
||||||
|
} catch (const RecordDeletedError &) {
|
||||||
|
state = RemotePullState::UPDATE_DELETED_ERROR;
|
||||||
|
} catch (const query::ReconstructionException &) {
|
||||||
|
state = RemotePullState::RECONSTRUCTION_ERROR;
|
||||||
|
} catch (const query::QueryRuntimeException &) {
|
||||||
|
state = RemotePullState::QUERY_ERROR;
|
||||||
|
}
|
||||||
|
return std::make_pair(std::move(results), state);
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
@ -123,17 +138,13 @@ class RemoteProduceRpcServer {
|
|||||||
auto &ongoing_produce = GetOngoingProduce(req);
|
auto &ongoing_produce = GetOngoingProduce(req);
|
||||||
|
|
||||||
RemotePullResData result{db_.WorkerId(), req.send_old, req.send_new};
|
RemotePullResData result{db_.WorkerId(), req.send_old, req.send_new};
|
||||||
|
|
||||||
result.state_and_frames.pull_state = RemotePullState::CURSOR_IN_PROGRESS;
|
result.state_and_frames.pull_state = RemotePullState::CURSOR_IN_PROGRESS;
|
||||||
|
|
||||||
for (int i = 0; i < req.batch_size; ++i) {
|
for (int i = 0; i < req.batch_size; ++i) {
|
||||||
// TODO exception handling (Serialization errors)
|
|
||||||
// when full CRUD. Maybe put it in OngoingProduce::Pull
|
|
||||||
auto pull_result = ongoing_produce.Pull();
|
auto pull_result = ongoing_produce.Pull();
|
||||||
if (!pull_result.second) {
|
result.state_and_frames.pull_state = pull_result.second;
|
||||||
result.state_and_frames.pull_state = RemotePullState::CURSOR_EXHAUSTED;
|
if (pull_result.second != RemotePullState::CURSOR_IN_PROGRESS)
|
||||||
break;
|
break;
|
||||||
}
|
|
||||||
result.state_and_frames.frames.emplace_back(std::move(pull_result.first));
|
result.state_and_frames.frames.emplace_back(std::move(pull_result.first));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -26,8 +26,11 @@ constexpr int kDefaultBatchSize = 20;
|
|||||||
enum class RemotePullState {
|
enum class RemotePullState {
|
||||||
CURSOR_EXHAUSTED,
|
CURSOR_EXHAUSTED,
|
||||||
CURSOR_IN_PROGRESS,
|
CURSOR_IN_PROGRESS,
|
||||||
SERIALIZATION_ERROR // future-proofing for full CRUD
|
SERIALIZATION_ERROR,
|
||||||
// TODO in full CRUD other errors
|
LOCK_TIMEOUT_ERROR,
|
||||||
|
UPDATE_DELETED_ERROR,
|
||||||
|
RECONSTRUCTION_ERROR,
|
||||||
|
QUERY_ERROR
|
||||||
};
|
};
|
||||||
|
|
||||||
const std::string kRemotePullProduceRpcName = "RemotePullProduceRpc";
|
const std::string kRemotePullProduceRpcName = "RemotePullProduceRpc";
|
||||||
|
@ -180,4 +180,37 @@ std::string ParseParameter(const std::string &s) {
|
|||||||
}
|
}
|
||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ReconstructTypedValue(TypedValue &value) {
|
||||||
|
using Type = TypedValue::Type;
|
||||||
|
switch (value.type()) {
|
||||||
|
case Type::Vertex:
|
||||||
|
if (!value.ValueVertex().Reconstruct()) throw ReconstructionException();
|
||||||
|
break;
|
||||||
|
case Type::Edge:
|
||||||
|
if (!value.ValueEdge().Reconstruct()) throw ReconstructionException();
|
||||||
|
break;
|
||||||
|
case Type::List:
|
||||||
|
for (TypedValue &inner_value : value.Value<std::vector<TypedValue>>())
|
||||||
|
ReconstructTypedValue(inner_value);
|
||||||
|
break;
|
||||||
|
case Type::Map:
|
||||||
|
for (auto &kv : value.Value<std::map<std::string, TypedValue>>())
|
||||||
|
ReconstructTypedValue(kv.second);
|
||||||
|
break;
|
||||||
|
case Type::Path:
|
||||||
|
for (auto &vertex : value.ValuePath().vertices()) {
|
||||||
|
if (!vertex.Reconstruct()) throw ReconstructionException();
|
||||||
|
}
|
||||||
|
for (auto &edge : value.ValuePath().edges()) {
|
||||||
|
if (!edge.Reconstruct()) throw ReconstructionException();
|
||||||
|
}
|
||||||
|
case Type::Null:
|
||||||
|
case Type::Bool:
|
||||||
|
case Type::Int:
|
||||||
|
case Type::Double:
|
||||||
|
case Type::String:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
} // namespace query
|
} // namespace query
|
||||||
|
@ -3,6 +3,8 @@
|
|||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
|
#include "query/typed_value.hpp"
|
||||||
|
|
||||||
namespace query {
|
namespace query {
|
||||||
|
|
||||||
// These are the functions for parsing literals and parameter names from
|
// These are the functions for parsing literals and parameter names from
|
||||||
@ -23,4 +25,12 @@ std::string ParseParameter(const std::string &s);
|
|||||||
* by some previous part of execution.
|
* by some previous part of execution.
|
||||||
*/
|
*/
|
||||||
enum class GraphView { AS_IS, OLD, NEW };
|
enum class GraphView { AS_IS, OLD, NEW };
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper function for recursively reconstructing all the accessors in the
|
||||||
|
* given TypedValue.
|
||||||
|
*
|
||||||
|
* @returns - If the reconstruction succeeded.
|
||||||
|
*/
|
||||||
|
void ReconstructTypedValue(TypedValue &value);
|
||||||
}
|
}
|
||||||
|
@ -99,4 +99,12 @@ class HintedAbortError : public utils::BasicException {
|
|||||||
"--query-execution-time-sec flag") {}
|
"--query-execution-time-sec flag") {}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class ReconstructionException : public QueryException {
|
||||||
|
public:
|
||||||
|
ReconstructionException()
|
||||||
|
: QueryException(
|
||||||
|
"Record invalid after WITH clause. Most likely deleted by a "
|
||||||
|
"preceeding DELETE.") {}
|
||||||
|
};
|
||||||
|
|
||||||
} // namespace query
|
} // namespace query
|
||||||
|
@ -19,6 +19,7 @@
|
|||||||
#include "query/frontend/semantic/symbol_table.hpp"
|
#include "query/frontend/semantic/symbol_table.hpp"
|
||||||
#include "query/interpret/eval.hpp"
|
#include "query/interpret/eval.hpp"
|
||||||
#include "query/path.hpp"
|
#include "query/path.hpp"
|
||||||
|
#include "utils/exceptions.hpp"
|
||||||
|
|
||||||
// macro for the default implementation of LogicalOperator::Accept
|
// macro for the default implementation of LogicalOperator::Accept
|
||||||
// that accepts the visitor and visits it's input_ operator
|
// that accepts the visitor and visits it's input_ operator
|
||||||
@ -1618,47 +1619,6 @@ void ExpandUniquenessFilter<TAccessor>::ExpandUniquenessFilterCursor::Reset() {
|
|||||||
template class ExpandUniquenessFilter<VertexAccessor>;
|
template class ExpandUniquenessFilter<VertexAccessor>;
|
||||||
template class ExpandUniquenessFilter<EdgeAccessor>;
|
template class ExpandUniquenessFilter<EdgeAccessor>;
|
||||||
|
|
||||||
namespace {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Helper function for recursively reconstructing all the accessors in the
|
|
||||||
* given TypedValue.
|
|
||||||
*/
|
|
||||||
void ReconstructTypedValue(TypedValue &value) {
|
|
||||||
const static std::string vertex_error_msg =
|
|
||||||
"Vertex invalid after WITH clause, (most likely deleted by a "
|
|
||||||
"preceeding DELETE clause)";
|
|
||||||
const static std::string edge_error_msg =
|
|
||||||
"Edge invalid after WITH clause, (most likely deleted by a "
|
|
||||||
"preceeding DELETE clause)";
|
|
||||||
switch (value.type()) {
|
|
||||||
case TypedValue::Type::Vertex:
|
|
||||||
if (!value.Value<VertexAccessor>().Reconstruct())
|
|
||||||
throw QueryRuntimeException(vertex_error_msg);
|
|
||||||
break;
|
|
||||||
case TypedValue::Type::Edge:
|
|
||||||
if (!value.Value<EdgeAccessor>().Reconstruct())
|
|
||||||
throw QueryRuntimeException(edge_error_msg);
|
|
||||||
break;
|
|
||||||
case TypedValue::Type::List:
|
|
||||||
for (TypedValue &inner_value : value.Value<std::vector<TypedValue>>())
|
|
||||||
ReconstructTypedValue(inner_value);
|
|
||||||
break;
|
|
||||||
case TypedValue::Type::Map:
|
|
||||||
for (auto &kv : value.Value<std::map<std::string, TypedValue>>())
|
|
||||||
ReconstructTypedValue(kv.second);
|
|
||||||
break;
|
|
||||||
case TypedValue::Type::Path:
|
|
||||||
for (auto &vertex : value.ValuePath().vertices())
|
|
||||||
if (vertex.Reconstruct()) throw QueryRuntimeException(vertex_error_msg);
|
|
||||||
for (auto &edge : value.ValuePath().edges())
|
|
||||||
if (edge.Reconstruct()) throw QueryRuntimeException(edge_error_msg);
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} // namespace
|
|
||||||
|
|
||||||
Accumulate::Accumulate(const std::shared_ptr<LogicalOperator> &input,
|
Accumulate::Accumulate(const std::shared_ptr<LogicalOperator> &input,
|
||||||
const std::vector<Symbol> &symbols, bool advance_command)
|
const std::vector<Symbol> &symbols, bool advance_command)
|
||||||
: input_(input), symbols_(symbols), advance_command_(advance_command) {}
|
: input_(input), symbols_(symbols), advance_command_(advance_command) {}
|
||||||
@ -1690,7 +1650,7 @@ bool Accumulate::AccumulateCursor::Pull(Frame &frame, Context &context) {
|
|||||||
if (self_.advance_command_) {
|
if (self_.advance_command_) {
|
||||||
db_.AdvanceCommand();
|
db_.AdvanceCommand();
|
||||||
for (auto &row : cache_)
|
for (auto &row : cache_)
|
||||||
for (auto &col : row) ReconstructTypedValue(col);
|
for (auto &col : row) query::ReconstructTypedValue(col);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2633,7 +2593,20 @@ bool PullRemote::PullRemoteCursor::Pull(Frame &frame, Context &context) {
|
|||||||
EndRemotePull();
|
EndRemotePull();
|
||||||
throw mvcc::SerializationError(
|
throw mvcc::SerializationError(
|
||||||
"Serialization error occured during PullRemote !");
|
"Serialization error occured during PullRemote !");
|
||||||
break;
|
case distributed::RemotePullState::LOCK_TIMEOUT_ERROR:
|
||||||
|
EndRemotePull();
|
||||||
|
throw LockTimeoutException(
|
||||||
|
"LockTimeout error occured during PullRemote !");
|
||||||
|
case distributed::RemotePullState::UPDATE_DELETED_ERROR:
|
||||||
|
EndRemotePull();
|
||||||
|
throw RecordDeletedError();
|
||||||
|
case distributed::RemotePullState::RECONSTRUCTION_ERROR:
|
||||||
|
EndRemotePull();
|
||||||
|
throw query::ReconstructionException();
|
||||||
|
case distributed::RemotePullState::QUERY_ERROR:
|
||||||
|
EndRemotePull();
|
||||||
|
throw QueryRuntimeException(
|
||||||
|
"Query runtime error occurred duing PullRemote !");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -452,7 +452,7 @@ TEST(QueryPlan, DeleteAdvance) {
|
|||||||
n.op_, std::vector<Expression *>{n_get}, false);
|
n.op_, std::vector<Expression *>{n_get}, false);
|
||||||
auto advance = std::make_shared<Accumulate>(
|
auto advance = std::make_shared<Accumulate>(
|
||||||
delete_op, std::vector<Symbol>{n.sym_}, true);
|
delete_op, std::vector<Symbol>{n.sym_}, true);
|
||||||
EXPECT_THROW(PullAll(advance, dba, symbol_table), QueryRuntimeException);
|
EXPECT_THROW(PullAll(advance, dba, symbol_table), ReconstructionException);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(QueryPlan, SetProperty) {
|
TEST(QueryPlan, SetProperty) {
|
||||||
|
Loading…
Reference in New Issue
Block a user