Remove distributed logic from single node
Summary: Start removing `is_remote` from `Address` Remove `GlobalAddress` Remove `GlobalizedAddress` Remove bitmasks from `Address` Remove `is_local` from `Address` Remove `is_local` from `RecordAccessor` Remove `worker_id` from `Address` Remove `worker_id` from `GidGenerator` Unfriend `IndexRpcServer` from `Storage` Remove `LocalizedAddressIfPossible` Make member private Remove `worker_id` from `Storage` Copy function to ease removal of distributed logic Remove `worker_id` from `WriteAheadLog` Remove `worker_id` from `GraphDb` Remove `worker_id` from durability Remove nonexistant function Remove `gid` from `Address` Remove usage of `Address` Remove `Address` Remove `VertexAddress` and `EdgeAddress` Fix Id test Remove `cypher_id` from `VersionList` Remove `cypher_id` from durability Remove `cypher_id` member from `VersionList` Remove `cypher_id` from database Fix recovery (revert D1142) Remove unnecessary functions from `GraphDbAccessor` Revert `InsertEdge` implementation to the way it was in/before D1142 Remove leftover `VertexAddress` from `Edge` Remove `PostCreateIndex` and `PopulateIndexFromBuildIndex` Split durability paths into single node and distributed Fix `TransactionIdFromWalFilename` implementation Fix tests Remove `cypher_id` from `snapshooter` and `durability` test Reviewers: msantl, teon.banek Reviewed By: msantl Subscribers: msantl, pullbot Differential Revision: https://phabricator.memgraph.io/D1647
This commit is contained in:
parent
4c03cb615e
commit
6525451489
@ -19,7 +19,7 @@ set(mg_single_node_sources
|
|||||||
database/single_node/graph_db.cpp
|
database/single_node/graph_db.cpp
|
||||||
database/single_node/graph_db_accessor.cpp
|
database/single_node/graph_db_accessor.cpp
|
||||||
durability/single_node/state_delta.cpp
|
durability/single_node/state_delta.cpp
|
||||||
durability/paths.cpp
|
durability/single_node/paths.cpp
|
||||||
durability/single_node/recovery.cpp
|
durability/single_node/recovery.cpp
|
||||||
durability/single_node/snapshooter.cpp
|
durability/single_node/snapshooter.cpp
|
||||||
durability/single_node/wal.cpp
|
durability/single_node/wal.cpp
|
||||||
@ -123,7 +123,7 @@ set(mg_distributed_sources
|
|||||||
database/distributed/config.cpp
|
database/distributed/config.cpp
|
||||||
database/distributed/graph_db_accessor.cpp
|
database/distributed/graph_db_accessor.cpp
|
||||||
durability/distributed/state_delta.cpp
|
durability/distributed/state_delta.cpp
|
||||||
durability/paths.cpp
|
durability/distributed/paths.cpp
|
||||||
durability/distributed/recovery.cpp
|
durability/distributed/recovery.cpp
|
||||||
durability/distributed/snapshooter.cpp
|
durability/distributed/snapshooter.cpp
|
||||||
durability/distributed/wal.cpp
|
durability/distributed/wal.cpp
|
||||||
|
@ -6,7 +6,7 @@
|
|||||||
|
|
||||||
#include "database/single_node/counters.hpp"
|
#include "database/single_node/counters.hpp"
|
||||||
#include "database/single_node/graph_db_accessor.hpp"
|
#include "database/single_node/graph_db_accessor.hpp"
|
||||||
#include "durability/paths.hpp"
|
#include "durability/single_node/paths.hpp"
|
||||||
#include "durability/single_node/recovery.hpp"
|
#include "durability/single_node/recovery.hpp"
|
||||||
#include "durability/single_node/snapshooter.hpp"
|
#include "durability/single_node/snapshooter.hpp"
|
||||||
#include "storage/single_node/concurrent_id_mapper.hpp"
|
#include "storage/single_node/concurrent_id_mapper.hpp"
|
||||||
@ -17,8 +17,6 @@
|
|||||||
namespace database {
|
namespace database {
|
||||||
|
|
||||||
GraphDb::GraphDb(Config config) : config_(config) {
|
GraphDb::GraphDb(Config config) : config_(config) {
|
||||||
CHECK(config.worker_id == 0)
|
|
||||||
<< "Worker ID should only be set in distributed GraphDb";
|
|
||||||
if (config_.durability_enabled) utils::CheckDir(config_.durability_directory);
|
if (config_.durability_enabled) utils::CheckDir(config_.durability_directory);
|
||||||
|
|
||||||
// Durability recovery.
|
// Durability recovery.
|
||||||
@ -33,7 +31,7 @@ GraphDb::GraphDb(Config config) : config_(config) {
|
|||||||
|
|
||||||
recovery_info = durability::RecoverOnlySnapshot(
|
recovery_info = durability::RecoverOnlySnapshot(
|
||||||
config_.durability_directory, this, &recovery_data,
|
config_.durability_directory, this, &recovery_data,
|
||||||
std::experimental::nullopt, 0);
|
std::experimental::nullopt);
|
||||||
|
|
||||||
// Post-recovery setup and checking.
|
// Post-recovery setup and checking.
|
||||||
if (recovery_info) {
|
if (recovery_info) {
|
||||||
@ -132,7 +130,7 @@ void GraphDb::CollectGarbage() { storage_gc_->CollectGarbage(); }
|
|||||||
|
|
||||||
bool GraphDb::MakeSnapshot(GraphDbAccessor &accessor) {
|
bool GraphDb::MakeSnapshot(GraphDbAccessor &accessor) {
|
||||||
const bool status = durability::MakeSnapshot(
|
const bool status = durability::MakeSnapshot(
|
||||||
*this, accessor, 0, fs::path(config_.durability_directory),
|
*this, accessor, fs::path(config_.durability_directory),
|
||||||
config_.snapshot_max_retained);
|
config_.snapshot_max_retained);
|
||||||
if (status) {
|
if (status) {
|
||||||
LOG(INFO) << "Snapshot created successfully.";
|
LOG(INFO) << "Snapshot created successfully.";
|
||||||
@ -145,8 +143,7 @@ bool GraphDb::MakeSnapshot(GraphDbAccessor &accessor) {
|
|||||||
void GraphDb::ReinitializeStorage() {
|
void GraphDb::ReinitializeStorage() {
|
||||||
// Release gc scheduler to stop it from touching storage
|
// Release gc scheduler to stop it from touching storage
|
||||||
storage_gc_ = nullptr;
|
storage_gc_ = nullptr;
|
||||||
storage_ =
|
storage_ = std::make_unique<Storage>(config_.properties_on_disk);
|
||||||
std::make_unique<Storage>(config_.worker_id, config_.properties_on_disk);
|
|
||||||
storage_gc_ =
|
storage_gc_ =
|
||||||
std::make_unique<StorageGc>(*storage_, tx_engine_, config_.gc_cycle_sec);
|
std::make_unique<StorageGc>(*storage_, tx_engine_, config_.gc_cycle_sec);
|
||||||
}
|
}
|
||||||
|
@ -37,15 +37,6 @@ struct Config {
|
|||||||
|
|
||||||
// set of properties which will be stored on disk
|
// set of properties which will be stored on disk
|
||||||
std::vector<std::string> properties_on_disk;
|
std::vector<std::string> properties_on_disk;
|
||||||
|
|
||||||
// Distributed master/worker flags.
|
|
||||||
bool dynamic_graph_partitioner_enabled{false};
|
|
||||||
int rpc_num_client_workers{0};
|
|
||||||
int rpc_num_server_workers{0};
|
|
||||||
int worker_id{0};
|
|
||||||
io::network::Endpoint master_endpoint{"0.0.0.0", 0};
|
|
||||||
io::network::Endpoint worker_endpoint{"0.0.0.0", 0};
|
|
||||||
int recovering_cluster_size{0};
|
|
||||||
};
|
};
|
||||||
|
|
||||||
class GraphDbAccessor;
|
class GraphDbAccessor;
|
||||||
@ -115,10 +106,10 @@ class GraphDb {
|
|||||||
|
|
||||||
Config config_;
|
Config config_;
|
||||||
std::unique_ptr<Storage> storage_ =
|
std::unique_ptr<Storage> storage_ =
|
||||||
std::make_unique<Storage>(config_.worker_id, config_.properties_on_disk);
|
std::make_unique<Storage>(config_.properties_on_disk);
|
||||||
durability::WriteAheadLog wal_{
|
durability::WriteAheadLog wal_{config_.durability_directory,
|
||||||
config_.worker_id, config_.durability_directory,
|
config_.durability_enabled,
|
||||||
config_.durability_enabled, config_.synchronous_commit};
|
config_.synchronous_commit};
|
||||||
|
|
||||||
tx::Engine tx_engine_{&wal_};
|
tx::Engine tx_engine_{&wal_};
|
||||||
std::unique_ptr<StorageGc> storage_gc_ =
|
std::unique_ptr<StorageGc> storage_gc_ =
|
||||||
|
@ -6,7 +6,6 @@
|
|||||||
#include <glog/logging.h>
|
#include <glog/logging.h>
|
||||||
|
|
||||||
#include "durability/single_node/state_delta.hpp"
|
#include "durability/single_node/state_delta.hpp"
|
||||||
#include "storage/single_node/address_types.hpp"
|
|
||||||
#include "storage/single_node/edge.hpp"
|
#include "storage/single_node/edge.hpp"
|
||||||
#include "storage/single_node/edge_accessor.hpp"
|
#include "storage/single_node/edge_accessor.hpp"
|
||||||
#include "storage/single_node/vertex.hpp"
|
#include "storage/single_node/vertex.hpp"
|
||||||
@ -61,29 +60,26 @@ bool GraphDbAccessor::should_abort() const {
|
|||||||
durability::WriteAheadLog &GraphDbAccessor::wal() { return db_.wal(); }
|
durability::WriteAheadLog &GraphDbAccessor::wal() { return db_.wal(); }
|
||||||
|
|
||||||
VertexAccessor GraphDbAccessor::InsertVertex(
|
VertexAccessor GraphDbAccessor::InsertVertex(
|
||||||
std::experimental::optional<gid::Gid> requested_gid,
|
std::experimental::optional<gid::Gid> requested_gid) {
|
||||||
std::experimental::optional<int64_t> cypher_id) {
|
|
||||||
DCHECK(!commited_ && !aborted_) << "Accessor committed or aborted";
|
DCHECK(!commited_ && !aborted_) << "Accessor committed or aborted";
|
||||||
|
|
||||||
auto gid = db_.storage().vertex_generator_.Next(requested_gid);
|
auto gid = db_.storage().vertex_generator_.Next(requested_gid);
|
||||||
if (!cypher_id) cypher_id = utils::MemcpyCast<int64_t>(gid);
|
auto vertex_vlist = new mvcc::VersionList<Vertex>(transaction_, gid);
|
||||||
auto vertex_vlist =
|
|
||||||
new mvcc::VersionList<Vertex>(transaction_, gid, *cypher_id);
|
|
||||||
|
|
||||||
bool success =
|
bool success =
|
||||||
db_.storage().vertices_.access().insert(gid, vertex_vlist).second;
|
db_.storage().vertices_.access().insert(gid, vertex_vlist).second;
|
||||||
CHECK(success) << "Attempting to insert a vertex with an existing GID: "
|
CHECK(success) << "Attempting to insert a vertex with an existing GID: "
|
||||||
<< gid;
|
<< gid;
|
||||||
wal().Emplace(database::StateDelta::CreateVertex(
|
wal().Emplace(
|
||||||
transaction_.id_, vertex_vlist->gid_, vertex_vlist->cypher_id()));
|
database::StateDelta::CreateVertex(transaction_.id_, vertex_vlist->gid_));
|
||||||
auto va = VertexAccessor(storage::VertexAddress(vertex_vlist), *this);
|
auto va = VertexAccessor(vertex_vlist, *this);
|
||||||
return va;
|
return va;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::experimental::optional<VertexAccessor> GraphDbAccessor::FindVertexOptional(
|
std::experimental::optional<VertexAccessor> GraphDbAccessor::FindVertexOptional(
|
||||||
gid::Gid gid, bool current_state) {
|
gid::Gid gid, bool current_state) {
|
||||||
VertexAccessor record_accessor(
|
VertexAccessor record_accessor(db_.storage().LocalAddress<Vertex>(gid),
|
||||||
storage::VertexAddress(db_.storage().LocalAddress<Vertex>(gid)), *this);
|
*this);
|
||||||
if (!record_accessor.Visible(transaction(), current_state))
|
if (!record_accessor.Visible(transaction(), current_state))
|
||||||
return std::experimental::nullopt;
|
return std::experimental::nullopt;
|
||||||
return record_accessor;
|
return record_accessor;
|
||||||
@ -97,8 +93,7 @@ VertexAccessor GraphDbAccessor::FindVertex(gid::Gid gid, bool current_state) {
|
|||||||
|
|
||||||
std::experimental::optional<EdgeAccessor> GraphDbAccessor::FindEdgeOptional(
|
std::experimental::optional<EdgeAccessor> GraphDbAccessor::FindEdgeOptional(
|
||||||
gid::Gid gid, bool current_state) {
|
gid::Gid gid, bool current_state) {
|
||||||
EdgeAccessor record_accessor(
|
EdgeAccessor record_accessor(db_.storage().LocalAddress<Edge>(gid), *this);
|
||||||
storage::EdgeAddress(db_.storage().LocalAddress<Edge>(gid)), *this);
|
|
||||||
if (!record_accessor.Visible(transaction(), current_state))
|
if (!record_accessor.Visible(transaction(), current_state))
|
||||||
return std::experimental::nullopt;
|
return std::experimental::nullopt;
|
||||||
return record_accessor;
|
return record_accessor;
|
||||||
@ -131,8 +126,6 @@ void GraphDbAccessor::BuildIndex(storage::Label label,
|
|||||||
"Index is either being created by another transaction or already "
|
"Index is either being created by another transaction or already "
|
||||||
"exists.");
|
"exists.");
|
||||||
}
|
}
|
||||||
// Call the hook for inherited classes.
|
|
||||||
PostCreateIndex(key);
|
|
||||||
|
|
||||||
// Everything that happens after the line above ended will be added to the
|
// Everything that happens after the line above ended will be added to the
|
||||||
// index automatically, but we still have to add to index everything that
|
// index automatically, but we still have to add to index everything that
|
||||||
@ -171,7 +164,7 @@ void GraphDbAccessor::BuildIndex(storage::Label label,
|
|||||||
DCHECK(removed) << "Index building (read) transaction should be inside set";
|
DCHECK(removed) << "Index building (read) transaction should be inside set";
|
||||||
});
|
});
|
||||||
|
|
||||||
dba->PopulateIndexFromBuildIndex(key);
|
dba->PopulateIndex(key);
|
||||||
|
|
||||||
dba->EnableIndex(key);
|
dba->EnableIndex(key);
|
||||||
dba->Commit();
|
dba->Commit();
|
||||||
@ -197,8 +190,8 @@ void GraphDbAccessor::PopulateIndex(const LabelPropertyIndex::Key &key) {
|
|||||||
for (auto vertex : Vertices(key.label_, false)) {
|
for (auto vertex : Vertices(key.label_, false)) {
|
||||||
if (vertex.PropsAt(key.property_).type() == PropertyValue::Type::Null)
|
if (vertex.PropsAt(key.property_).type() == PropertyValue::Type::Null)
|
||||||
continue;
|
continue;
|
||||||
db_.storage().label_property_index_.UpdateOnLabelProperty(
|
db_.storage().label_property_index_.UpdateOnLabelProperty(vertex.address(),
|
||||||
vertex.address().local(), vertex.current_);
|
vertex.current_);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -206,8 +199,7 @@ void GraphDbAccessor::UpdateLabelIndices(storage::Label label,
|
|||||||
const VertexAccessor &vertex_accessor,
|
const VertexAccessor &vertex_accessor,
|
||||||
const Vertex *const vertex) {
|
const Vertex *const vertex) {
|
||||||
DCHECK(!commited_ && !aborted_) << "Accessor committed or aborted";
|
DCHECK(!commited_ && !aborted_) << "Accessor committed or aborted";
|
||||||
DCHECK(vertex_accessor.is_local()) << "Only local vertices belong in indexes";
|
auto *vlist_ptr = vertex_accessor.address();
|
||||||
auto *vlist_ptr = vertex_accessor.address().local();
|
|
||||||
db_.storage().labels_index_.Update(label, vlist_ptr, vertex);
|
db_.storage().labels_index_.Update(label, vlist_ptr, vertex);
|
||||||
db_.storage().label_property_index_.UpdateOnLabel(label, vlist_ptr, vertex);
|
db_.storage().label_property_index_.UpdateOnLabel(label, vlist_ptr, vertex);
|
||||||
}
|
}
|
||||||
@ -216,9 +208,8 @@ void GraphDbAccessor::UpdatePropertyIndex(
|
|||||||
storage::Property property, const RecordAccessor<Vertex> &vertex_accessor,
|
storage::Property property, const RecordAccessor<Vertex> &vertex_accessor,
|
||||||
const Vertex *const vertex) {
|
const Vertex *const vertex) {
|
||||||
DCHECK(!commited_ && !aborted_) << "Accessor committed or aborted";
|
DCHECK(!commited_ && !aborted_) << "Accessor committed or aborted";
|
||||||
DCHECK(vertex_accessor.is_local()) << "Only local vertices belong in indexes";
|
|
||||||
db_.storage().label_property_index_.UpdateOnProperty(
|
db_.storage().label_property_index_.UpdateOnProperty(
|
||||||
property, vertex_accessor.address().local(), vertex);
|
property, vertex_accessor.address(), vertex);
|
||||||
}
|
}
|
||||||
|
|
||||||
int64_t GraphDbAccessor::VerticesCount() const {
|
int64_t GraphDbAccessor::VerticesCount() const {
|
||||||
@ -305,7 +296,7 @@ bool GraphDbAccessor::RemoveVertex(VertexAccessor &vertex_accessor,
|
|||||||
vertex_accessor.out_degree() + vertex_accessor.in_degree() > 0)
|
vertex_accessor.out_degree() + vertex_accessor.in_degree() > 0)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
auto *vlist_ptr = vertex_accessor.address().local();
|
auto *vlist_ptr = vertex_accessor.address();
|
||||||
wal().Emplace(database::StateDelta::RemoveVertex(
|
wal().Emplace(database::StateDelta::RemoveVertex(
|
||||||
transaction_.id_, vlist_ptr->gid_, check_empty));
|
transaction_.id_, vlist_ptr->gid_, check_empty));
|
||||||
vlist_ptr->remove(vertex_accessor.current_, transaction_);
|
vlist_ptr->remove(vertex_accessor.current_, transaction_);
|
||||||
@ -331,76 +322,34 @@ void GraphDbAccessor::DetachRemoveVertex(VertexAccessor &vertex_accessor) {
|
|||||||
|
|
||||||
EdgeAccessor GraphDbAccessor::InsertEdge(
|
EdgeAccessor GraphDbAccessor::InsertEdge(
|
||||||
VertexAccessor &from, VertexAccessor &to, storage::EdgeType edge_type,
|
VertexAccessor &from, VertexAccessor &to, storage::EdgeType edge_type,
|
||||||
std::experimental::optional<gid::Gid> requested_gid,
|
std::experimental::optional<gid::Gid> requested_gid) {
|
||||||
std::experimental::optional<int64_t> cypher_id) {
|
|
||||||
DCHECK(!commited_ && !aborted_) << "Accessor committed or aborted";
|
DCHECK(!commited_ && !aborted_) << "Accessor committed or aborted";
|
||||||
|
|
||||||
auto edge_address =
|
|
||||||
InsertEdgeOnFrom(&from, &to, edge_type, requested_gid, cypher_id);
|
|
||||||
|
|
||||||
InsertEdgeOnTo(&from, &to, edge_type, edge_address);
|
|
||||||
return EdgeAccessor(edge_address, *this, from.address(), to.address(),
|
|
||||||
edge_type);
|
|
||||||
}
|
|
||||||
|
|
||||||
storage::EdgeAddress GraphDbAccessor::InsertEdgeOnFrom(
|
|
||||||
VertexAccessor *from, VertexAccessor *to,
|
|
||||||
const storage::EdgeType &edge_type,
|
|
||||||
const std::experimental::optional<gid::Gid> &requested_gid,
|
|
||||||
const std::experimental::optional<int64_t> &cypher_id) {
|
|
||||||
auto edge_accessor = InsertOnlyEdge(from->address(), to->address(), edge_type,
|
|
||||||
requested_gid, cypher_id);
|
|
||||||
auto edge_address = edge_accessor.address();
|
|
||||||
|
|
||||||
from->SwitchNew();
|
|
||||||
auto from_updated = &from->update();
|
|
||||||
|
|
||||||
// TODO when preparing WAL for distributed, most likely never use
|
|
||||||
// `CREATE_EDGE`, but always have it split into 3 parts (edge insertion,
|
|
||||||
// in/out modification).
|
|
||||||
wal().Emplace(database::StateDelta::CreateEdge(
|
|
||||||
transaction_.id_, edge_accessor.gid(), edge_accessor.CypherId(),
|
|
||||||
from->gid(), to->gid(), edge_type, EdgeTypeName(edge_type)));
|
|
||||||
|
|
||||||
from_updated->out_.emplace(
|
|
||||||
db_.storage().LocalizedAddressIfPossible(to->address()), edge_address,
|
|
||||||
edge_type);
|
|
||||||
return edge_address;
|
|
||||||
}
|
|
||||||
|
|
||||||
void GraphDbAccessor::InsertEdgeOnTo(VertexAccessor *from, VertexAccessor *to,
|
|
||||||
const storage::EdgeType &edge_type,
|
|
||||||
const storage::EdgeAddress &edge_address) {
|
|
||||||
// Ensure that the "to" accessor has the latest version (switch new).
|
|
||||||
// WARNING: Must do that after the above "from->update()" for cases when
|
|
||||||
// we are creating a cycle and "from" and "to" are the same vlist.
|
|
||||||
to->SwitchNew();
|
|
||||||
auto *to_updated = &to->update();
|
|
||||||
to_updated->in_.emplace(
|
|
||||||
db_.storage().LocalizedAddressIfPossible(from->address()), edge_address,
|
|
||||||
edge_type);
|
|
||||||
}
|
|
||||||
|
|
||||||
EdgeAccessor GraphDbAccessor::InsertOnlyEdge(
|
|
||||||
storage::VertexAddress from, storage::VertexAddress to,
|
|
||||||
storage::EdgeType edge_type,
|
|
||||||
std::experimental::optional<gid::Gid> requested_gid,
|
|
||||||
std::experimental::optional<int64_t> cypher_id) {
|
|
||||||
CHECK(from.is_local())
|
|
||||||
<< "`from` address should be local when calling InsertOnlyEdge";
|
|
||||||
auto gid = db_.storage().edge_generator_.Next(requested_gid);
|
auto gid = db_.storage().edge_generator_.Next(requested_gid);
|
||||||
if (!cypher_id) cypher_id = utils::MemcpyCast<int64_t>(gid);
|
auto edge_vlist = new mvcc::VersionList<Edge>(
|
||||||
auto edge_vlist = new mvcc::VersionList<Edge>(transaction_, gid, *cypher_id,
|
transaction_, gid, from.address(), to.address(), edge_type);
|
||||||
from, to, edge_type);
|
|
||||||
// We need to insert edge_vlist to edges_ before calling update since update
|
// We need to insert edge_vlist to edges_ before calling update since update
|
||||||
// can throw and edge_vlist will not be garbage collected if it is not in
|
// can throw and edge_vlist will not be garbage collected if it is not in
|
||||||
// edges_ skiplist.
|
// edges_ skiplist.
|
||||||
bool success = db_.storage().edges_.access().insert(gid, edge_vlist).second;
|
bool success = db_.storage().edges_.access().insert(gid, edge_vlist).second;
|
||||||
CHECK(success) << "Attempting to insert an edge with an existing GID: "
|
CHECK(success) << "Attempting to insert an edge with an existing GID: "
|
||||||
<< gid;
|
<< gid;
|
||||||
auto ea = EdgeAccessor(storage::EdgeAddress(edge_vlist), *this, from, to,
|
|
||||||
edge_type);
|
// ensure that the "from" accessor has the latest version
|
||||||
return ea;
|
from.SwitchNew();
|
||||||
|
from.update().out_.emplace(to.address(), edge_vlist, edge_type);
|
||||||
|
|
||||||
|
// ensure that the "to" accessor has the latest version (Switch new)
|
||||||
|
// WARNING: must do that after the above "from.update()" for cases when
|
||||||
|
// we are creating a cycle and "from" and "to" are the same vlist
|
||||||
|
to.SwitchNew();
|
||||||
|
to.update().in_.emplace(from.address(), edge_vlist, edge_type);
|
||||||
|
|
||||||
|
wal().Emplace(database::StateDelta::CreateEdge(
|
||||||
|
transaction_.id_, edge_vlist->gid_, from.gid(), to.gid(), edge_type,
|
||||||
|
EdgeTypeName(edge_type)));
|
||||||
|
|
||||||
|
return EdgeAccessor(edge_vlist, *this, from.address(), to.address(),
|
||||||
|
edge_type);
|
||||||
}
|
}
|
||||||
|
|
||||||
int64_t GraphDbAccessor::EdgesCount() const {
|
int64_t GraphDbAccessor::EdgesCount() const {
|
||||||
@ -419,7 +368,7 @@ void GraphDbAccessor::RemoveEdge(EdgeAccessor &edge, bool remove_out_edge,
|
|||||||
if (remove_out_edge) edge.from().RemoveOutEdge(edge.address());
|
if (remove_out_edge) edge.from().RemoveOutEdge(edge.address());
|
||||||
if (remove_in_edge) edge.to().RemoveInEdge(edge.address());
|
if (remove_in_edge) edge.to().RemoveInEdge(edge.address());
|
||||||
|
|
||||||
edge.address().local()->remove(edge.current_, transaction_);
|
edge.address()->remove(edge.current_, transaction_);
|
||||||
wal().Emplace(database::StateDelta::RemoveEdge(transaction_.id_, edge.gid()));
|
wal().Emplace(database::StateDelta::RemoveEdge(transaction_.id_, edge.gid()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -12,7 +12,6 @@
|
|||||||
|
|
||||||
#include "database/single_node/graph_db.hpp"
|
#include "database/single_node/graph_db.hpp"
|
||||||
#include "storage/common/types.hpp"
|
#include "storage/common/types.hpp"
|
||||||
#include "storage/single_node/address_types.hpp"
|
|
||||||
#include "storage/single_node/edge_accessor.hpp"
|
#include "storage/single_node/edge_accessor.hpp"
|
||||||
#include "storage/single_node/vertex_accessor.hpp"
|
#include "storage/single_node/vertex_accessor.hpp"
|
||||||
#include "transactions/transaction.hpp"
|
#include "transactions/transaction.hpp"
|
||||||
@ -77,14 +76,11 @@ class GraphDbAccessor {
|
|||||||
*
|
*
|
||||||
* @param requested_gid The requested GID. Should only be provided when
|
* @param requested_gid The requested GID. Should only be provided when
|
||||||
* recovering from durability.
|
* recovering from durability.
|
||||||
* @param cypher_id Take a look under mvcc::VersionList::cypher_id
|
|
||||||
*
|
*
|
||||||
* @return See above.
|
* @return See above.
|
||||||
*/
|
*/
|
||||||
VertexAccessor InsertVertex(std::experimental::optional<gid::Gid>
|
VertexAccessor InsertVertex(std::experimental::optional<gid::Gid>
|
||||||
requested_gid = std::experimental::nullopt,
|
requested_gid = std::experimental::nullopt);
|
||||||
std::experimental::optional<int64_t> cypher_id =
|
|
||||||
std::experimental::nullopt);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Removes the vertex of the given accessor. If the vertex has any outgoing or
|
* Removes the vertex of the given accessor. If the vertex has any outgoing or
|
||||||
@ -150,7 +146,7 @@ class GraphDbAccessor {
|
|||||||
// wrap version lists into accessors, which will look for visible versions
|
// wrap version lists into accessors, which will look for visible versions
|
||||||
auto accessors = iter::imap(
|
auto accessors = iter::imap(
|
||||||
[this](auto id_vlist) {
|
[this](auto id_vlist) {
|
||||||
return VertexAccessor(storage::VertexAddress(id_vlist.second), *this);
|
return VertexAccessor(id_vlist.second, *this);
|
||||||
},
|
},
|
||||||
db_.storage().vertices_.access());
|
db_.storage().vertices_.access());
|
||||||
|
|
||||||
@ -176,7 +172,7 @@ class GraphDbAccessor {
|
|||||||
DCHECK(!commited_ && !aborted_) << "Accessor committed or aborted";
|
DCHECK(!commited_ && !aborted_) << "Accessor committed or aborted";
|
||||||
return iter::imap(
|
return iter::imap(
|
||||||
[this](auto vlist) {
|
[this](auto vlist) {
|
||||||
return VertexAccessor(storage::VertexAddress(vlist), *this);
|
return VertexAccessor(vlist, *this);
|
||||||
},
|
},
|
||||||
db_.storage().labels_index_.GetVlists(label, transaction_,
|
db_.storage().labels_index_.GetVlists(label, transaction_,
|
||||||
current_state));
|
current_state));
|
||||||
@ -202,7 +198,7 @@ class GraphDbAccessor {
|
|||||||
<< "Label+property index doesn't exist.";
|
<< "Label+property index doesn't exist.";
|
||||||
return iter::imap(
|
return iter::imap(
|
||||||
[this](auto vlist) {
|
[this](auto vlist) {
|
||||||
return VertexAccessor(storage::VertexAddress(vlist), *this);
|
return VertexAccessor(vlist, *this);
|
||||||
},
|
},
|
||||||
db_.storage().label_property_index_.GetVlists(
|
db_.storage().label_property_index_.GetVlists(
|
||||||
LabelPropertyIndex::Key(label, property), transaction_,
|
LabelPropertyIndex::Key(label, property), transaction_,
|
||||||
@ -232,7 +228,7 @@ class GraphDbAccessor {
|
|||||||
<< "Can't query index for propery value type null.";
|
<< "Can't query index for propery value type null.";
|
||||||
return iter::imap(
|
return iter::imap(
|
||||||
[this](auto vlist) {
|
[this](auto vlist) {
|
||||||
return VertexAccessor(storage::VertexAddress(vlist), *this);
|
return VertexAccessor(vlist, *this);
|
||||||
},
|
},
|
||||||
db_.storage().label_property_index_.GetVlists(
|
db_.storage().label_property_index_.GetVlists(
|
||||||
LabelPropertyIndex::Key(label, property), value, transaction_,
|
LabelPropertyIndex::Key(label, property), value, transaction_,
|
||||||
@ -277,7 +273,7 @@ class GraphDbAccessor {
|
|||||||
<< "Label+property index doesn't exist.";
|
<< "Label+property index doesn't exist.";
|
||||||
return iter::imap(
|
return iter::imap(
|
||||||
[this](auto vlist) {
|
[this](auto vlist) {
|
||||||
return VertexAccessor(storage::VertexAddress(vlist), *this);
|
return VertexAccessor(vlist, *this);
|
||||||
},
|
},
|
||||||
db_.storage().label_property_index_.GetVlists(
|
db_.storage().label_property_index_.GetVlists(
|
||||||
LabelPropertyIndex::Key(label, property), lower, upper,
|
LabelPropertyIndex::Key(label, property), lower, upper,
|
||||||
@ -300,31 +296,14 @@ class GraphDbAccessor {
|
|||||||
* @param type Edge type.
|
* @param type Edge type.
|
||||||
* @param requested_gid The requested GID. Should only be provided when
|
* @param requested_gid The requested GID. Should only be provided when
|
||||||
* recovering from durability.
|
* recovering from durability.
|
||||||
* @param cypher_id Take a look under mvcc::VersionList::cypher_id
|
|
||||||
*
|
*
|
||||||
* @return An accessor to the edge.
|
* @return An accessor to the edge.
|
||||||
*/
|
*/
|
||||||
EdgeAccessor InsertEdge(VertexAccessor &from, VertexAccessor &to,
|
EdgeAccessor InsertEdge(VertexAccessor &from, VertexAccessor &to,
|
||||||
storage::EdgeType type,
|
storage::EdgeType type,
|
||||||
std::experimental::optional<gid::Gid> requested_gid =
|
std::experimental::optional<gid::Gid> requested_gid =
|
||||||
std::experimental::nullopt,
|
|
||||||
std::experimental::optional<int64_t> cypher_id =
|
|
||||||
std::experimental::nullopt);
|
std::experimental::nullopt);
|
||||||
|
|
||||||
/**
|
|
||||||
* Insert edge into main storage, but don't insert it into from and to
|
|
||||||
* vertices edge lists.
|
|
||||||
*
|
|
||||||
* @param cypher_id Take a look under mvcc::VersionList::cypher_id
|
|
||||||
*/
|
|
||||||
EdgeAccessor InsertOnlyEdge(storage::VertexAddress from,
|
|
||||||
storage::VertexAddress to,
|
|
||||||
storage::EdgeType edge_type,
|
|
||||||
std::experimental::optional<gid::Gid>
|
|
||||||
requested_gid = std::experimental::nullopt,
|
|
||||||
std::experimental::optional<int64_t> cypher_id =
|
|
||||||
std::experimental::nullopt);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Removes an edge from the graph. Parameters can indicate if the edge should
|
* Removes an edge from the graph. Parameters can indicate if the edge should
|
||||||
* be removed from data structures in vertices it connects. When removing an
|
* be removed from data structures in vertices it connects. When removing an
|
||||||
@ -382,7 +361,7 @@ class GraphDbAccessor {
|
|||||||
// wrap version lists into accessors, which will look for visible versions
|
// wrap version lists into accessors, which will look for visible versions
|
||||||
auto accessors = iter::imap(
|
auto accessors = iter::imap(
|
||||||
[this](auto id_vlist) {
|
[this](auto id_vlist) {
|
||||||
return EdgeAccessor(storage::EdgeAddress(id_vlist.second), *this);
|
return EdgeAccessor(id_vlist.second, *this);
|
||||||
},
|
},
|
||||||
db_.storage().edges_.access());
|
db_.storage().edges_.access());
|
||||||
|
|
||||||
@ -616,34 +595,6 @@ class GraphDbAccessor {
|
|||||||
const VertexAccessor &vertex_accessor,
|
const VertexAccessor &vertex_accessor,
|
||||||
const Vertex *const vertex);
|
const Vertex *const vertex);
|
||||||
|
|
||||||
protected:
|
|
||||||
/** Called in `BuildIndex` after creating an index, but before populating. */
|
|
||||||
void PostCreateIndex(const LabelPropertyIndex::Key &key) {}
|
|
||||||
|
|
||||||
/** Populates the index from a *new* transaction after creating the index. */
|
|
||||||
void PopulateIndexFromBuildIndex(const LabelPropertyIndex::Key &key) {
|
|
||||||
PopulateIndex(key);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Insert a new edge to `from` vertex and return the address.
|
|
||||||
* Called from `InsertEdge` as the first step in edge insertion.
|
|
||||||
* */
|
|
||||||
storage::EdgeAddress InsertEdgeOnFrom(
|
|
||||||
VertexAccessor *from, VertexAccessor *to,
|
|
||||||
const storage::EdgeType &edge_type,
|
|
||||||
const std::experimental::optional<gid::Gid> &requested_gid,
|
|
||||||
const std::experimental::optional<int64_t> &cypher_id);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set the newly created edge on `to` vertex.
|
|
||||||
* Called after `InsertEdgeOnFrom` in `InsertEdge`. The given `edge_address`
|
|
||||||
* is from the created edge, returned by `InsertEdgeOnFrom`.
|
|
||||||
*/
|
|
||||||
void InsertEdgeOnTo(VertexAccessor *from, VertexAccessor *to,
|
|
||||||
const storage::EdgeType &edge_type,
|
|
||||||
const storage::EdgeAddress &edge_address);
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
GraphDb &db_;
|
GraphDb &db_;
|
||||||
tx::Transaction &transaction_;
|
tx::Transaction &transaction_;
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
#include "durability/paths.hpp"
|
#include "durability/distributed/paths.hpp"
|
||||||
|
|
||||||
#include <experimental/filesystem>
|
#include <experimental/filesystem>
|
||||||
#include <experimental/optional>
|
#include <experimental/optional>
|
@ -5,12 +5,12 @@
|
|||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
|
|
||||||
#include "database/distributed/graph_db_accessor.hpp"
|
#include "database/distributed/graph_db_accessor.hpp"
|
||||||
|
#include "durability/distributed/paths.hpp"
|
||||||
#include "durability/distributed/snapshot_decoder.hpp"
|
#include "durability/distributed/snapshot_decoder.hpp"
|
||||||
#include "durability/distributed/snapshot_value.hpp"
|
#include "durability/distributed/snapshot_value.hpp"
|
||||||
#include "durability/distributed/version.hpp"
|
#include "durability/distributed/version.hpp"
|
||||||
#include "durability/distributed/wal.hpp"
|
#include "durability/distributed/wal.hpp"
|
||||||
#include "durability/hashed_file_reader.hpp"
|
#include "durability/hashed_file_reader.hpp"
|
||||||
#include "durability/paths.hpp"
|
|
||||||
#include "glue/communication.hpp"
|
#include "glue/communication.hpp"
|
||||||
// TODO: WTF is typed value doing here?!
|
// TODO: WTF is typed value doing here?!
|
||||||
#include "query/typed_value.hpp"
|
#include "query/typed_value.hpp"
|
||||||
|
@ -5,10 +5,10 @@
|
|||||||
#include <glog/logging.h>
|
#include <glog/logging.h>
|
||||||
|
|
||||||
#include "database/distributed/graph_db_accessor.hpp"
|
#include "database/distributed/graph_db_accessor.hpp"
|
||||||
|
#include "durability/distributed/paths.hpp"
|
||||||
#include "durability/distributed/snapshot_encoder.hpp"
|
#include "durability/distributed/snapshot_encoder.hpp"
|
||||||
#include "durability/distributed/version.hpp"
|
#include "durability/distributed/version.hpp"
|
||||||
#include "durability/hashed_file_writer.hpp"
|
#include "durability/hashed_file_writer.hpp"
|
||||||
#include "durability/paths.hpp"
|
|
||||||
#include "utils/file.hpp"
|
#include "utils/file.hpp"
|
||||||
|
|
||||||
namespace fs = std::experimental::filesystem;
|
namespace fs = std::experimental::filesystem;
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
#include "durability/distributed/wal.hpp"
|
#include "durability/distributed/wal.hpp"
|
||||||
|
|
||||||
#include "durability/distributed/version.hpp"
|
#include "durability/distributed/version.hpp"
|
||||||
#include "durability/paths.hpp"
|
#include "durability/distributed/paths.hpp"
|
||||||
#include "utils/file.hpp"
|
#include "utils/file.hpp"
|
||||||
#include "utils/flag_validation.hpp"
|
#include "utils/flag_validation.hpp"
|
||||||
|
|
||||||
|
90
src/durability/single_node/paths.cpp
Normal file
90
src/durability/single_node/paths.cpp
Normal file
@ -0,0 +1,90 @@
|
|||||||
|
#include "durability/single_node/paths.hpp"
|
||||||
|
|
||||||
|
#include <experimental/filesystem>
|
||||||
|
#include <experimental/optional>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
#include "glog/logging.h"
|
||||||
|
|
||||||
|
#include "transactions/type.hpp"
|
||||||
|
#include "utils/string.hpp"
|
||||||
|
#include "utils/timestamp.hpp"
|
||||||
|
|
||||||
|
namespace durability {
|
||||||
|
|
||||||
|
namespace fs = std::experimental::filesystem;
|
||||||
|
|
||||||
|
std::experimental::optional<tx::TransactionId> TransactionIdFromWalFilename(
|
||||||
|
const std::string &name) {
|
||||||
|
auto nullopt = std::experimental::nullopt;
|
||||||
|
// Get the max_transaction_id from the file name that has format
|
||||||
|
// "XXXXX__max_transaction_<MAX_TRANS_ID>"
|
||||||
|
auto file_name_split = utils::RSplit(name, "__", 1);
|
||||||
|
if (file_name_split.size() != 2) {
|
||||||
|
LOG(WARNING) << "Unable to parse WAL file name: " << name;
|
||||||
|
return nullopt;
|
||||||
|
}
|
||||||
|
if (utils::StartsWith(file_name_split[1], "current"))
|
||||||
|
return std::numeric_limits<tx::TransactionId>::max();
|
||||||
|
file_name_split = utils::Split(file_name_split[1], "_");
|
||||||
|
if (file_name_split.size() != 3) {
|
||||||
|
LOG(WARNING) << "Unable to parse WAL file name: " << name;
|
||||||
|
return nullopt;
|
||||||
|
}
|
||||||
|
auto &tx_id_str = file_name_split[2];
|
||||||
|
try {
|
||||||
|
return std::stoll(tx_id_str);
|
||||||
|
} catch (std::invalid_argument &) {
|
||||||
|
LOG(WARNING) << "Unable to parse WAL file name tx ID: " << tx_id_str;
|
||||||
|
return nullopt;
|
||||||
|
} catch (std::out_of_range &) {
|
||||||
|
LOG(WARNING) << "WAL file name tx ID too large: " << tx_id_str;
|
||||||
|
return nullopt;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Generates a file path for a write-ahead log file. If given a transaction ID
|
||||||
|
/// the file name will contain it. Otherwise the file path is for the "current"
|
||||||
|
/// WAL file for which the max tx id is still unknown.
|
||||||
|
fs::path WalFilenameForTransactionId(
|
||||||
|
const std::experimental::filesystem::path &wal_dir,
|
||||||
|
std::experimental::optional<tx::TransactionId> tx_id) {
|
||||||
|
auto file_name = utils::Timestamp::Now().ToIso8601();
|
||||||
|
if (tx_id) {
|
||||||
|
file_name += "__max_transaction_" + std::to_string(*tx_id);
|
||||||
|
} else {
|
||||||
|
file_name += "__current";
|
||||||
|
}
|
||||||
|
return wal_dir / file_name;
|
||||||
|
}
|
||||||
|
|
||||||
|
fs::path MakeSnapshotPath(const fs::path &durability_dir,
|
||||||
|
tx::TransactionId tx_id) {
|
||||||
|
std::string date_str =
|
||||||
|
utils::Timestamp(utils::Timestamp::Now())
|
||||||
|
.ToString("{:04d}_{:02d}_{:02d}__{:02d}_{:02d}_{:02d}_{:05d}");
|
||||||
|
auto file_name = date_str + "_tx_" + std::to_string(tx_id);
|
||||||
|
return durability_dir / kSnapshotDir / file_name;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::experimental::optional<tx::TransactionId>
|
||||||
|
TransactionIdFromSnapshotFilename(const std::string &name) {
|
||||||
|
auto nullopt = std::experimental::nullopt;
|
||||||
|
auto file_name_split = utils::RSplit(name, "_tx_", 1);
|
||||||
|
if (file_name_split.size() != 2) {
|
||||||
|
LOG(WARNING) << "Unable to parse snapshot file name: " << name;
|
||||||
|
return nullopt;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
return std::stoll(file_name_split[1]);
|
||||||
|
} catch (std::invalid_argument &) {
|
||||||
|
LOG(WARNING) << "Unable to parse snapshot file name tx ID: "
|
||||||
|
<< file_name_split[1];
|
||||||
|
return nullopt;
|
||||||
|
} catch (std::out_of_range &) {
|
||||||
|
LOG(WARNING) << "Unable to parse snapshot file name tx ID: "
|
||||||
|
<< file_name_split[1];
|
||||||
|
return nullopt;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} // namespace durability
|
41
src/durability/single_node/paths.hpp
Normal file
41
src/durability/single_node/paths.hpp
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <experimental/filesystem>
|
||||||
|
#include <experimental/optional>
|
||||||
|
|
||||||
|
#include "transactions/type.hpp"
|
||||||
|
|
||||||
|
namespace durability {
|
||||||
|
const std::string kSnapshotDir = "snapshots";
|
||||||
|
const std::string kWalDir = "wal";
|
||||||
|
const std::string kBackupDir = ".backup";
|
||||||
|
|
||||||
|
/// Returns the transaction id contained in the file name. If the filename is
|
||||||
|
/// not a parseable WAL file name, nullopt is returned. If the filename
|
||||||
|
/// represents the "current" WAL file, then the maximum possible transaction ID
|
||||||
|
/// is returned because that's appropriate for the recovery logic (the current
|
||||||
|
/// WAL does not yet have a maximum transaction ID and can't be discarded by
|
||||||
|
/// the recovery regardless of the snapshot from which the transaction starts).
|
||||||
|
std::experimental::optional<tx::TransactionId> TransactionIdFromWalFilename(
|
||||||
|
const std::string &name);
|
||||||
|
|
||||||
|
/// Generates a file path for a write-ahead log file. If given a transaction ID
|
||||||
|
/// the file name will contain it. Otherwise the file path is for the "current"
|
||||||
|
/// WAL file for which the max tx id is still unknown.
|
||||||
|
std::experimental::filesystem::path WalFilenameForTransactionId(
|
||||||
|
const std::experimental::filesystem::path &wal_dir,
|
||||||
|
std::experimental::optional<tx::TransactionId> tx_id =
|
||||||
|
std::experimental::nullopt);
|
||||||
|
|
||||||
|
/// Generates a path for a DB snapshot in the given folder in a well-defined
|
||||||
|
/// sortable format with transaction from which the snapshot is created appended
|
||||||
|
/// to the file name.
|
||||||
|
std::experimental::filesystem::path MakeSnapshotPath(
|
||||||
|
const std::experimental::filesystem::path &durability_dir,
|
||||||
|
tx::TransactionId tx_id);
|
||||||
|
|
||||||
|
/// Returns the transaction id contained in the file name. If the filename is
|
||||||
|
/// not a parseable WAL file name, nullopt is returned.
|
||||||
|
std::experimental::optional<tx::TransactionId>
|
||||||
|
TransactionIdFromSnapshotFilename(const std::string &name);
|
||||||
|
} // namespace durability
|
@ -4,17 +4,13 @@
|
|||||||
#include <limits>
|
#include <limits>
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
|
|
||||||
|
#include "communication/bolt/v1/decoder/decoder.hpp"
|
||||||
#include "database/single_node/graph_db_accessor.hpp"
|
#include "database/single_node/graph_db_accessor.hpp"
|
||||||
#include "durability/hashed_file_reader.hpp"
|
#include "durability/hashed_file_reader.hpp"
|
||||||
#include "durability/paths.hpp"
|
#include "durability/single_node/paths.hpp"
|
||||||
#include "durability/single_node/snapshot_decoder.hpp"
|
|
||||||
#include "durability/single_node/snapshot_value.hpp"
|
|
||||||
#include "durability/single_node/version.hpp"
|
#include "durability/single_node/version.hpp"
|
||||||
#include "durability/single_node/wal.hpp"
|
#include "durability/single_node/wal.hpp"
|
||||||
#include "glue/communication.hpp"
|
#include "glue/communication.hpp"
|
||||||
// TODO: WTF is typed value doing here?!
|
|
||||||
#include "query/typed_value.hpp"
|
|
||||||
#include "storage/single_node/address_types.hpp"
|
|
||||||
#include "storage/single_node/indexes/label_property_index.hpp"
|
#include "storage/single_node/indexes/label_property_index.hpp"
|
||||||
#include "transactions/type.hpp"
|
#include "transactions/type.hpp"
|
||||||
#include "utils/algorithm.hpp"
|
#include "utils/algorithm.hpp"
|
||||||
@ -44,7 +40,7 @@ bool VersionConsistency(const fs::path &durability_dir) {
|
|||||||
|
|
||||||
for (const auto &file : fs::directory_iterator(recovery_dir)) {
|
for (const auto &file : fs::directory_iterator(recovery_dir)) {
|
||||||
HashedFileReader reader;
|
HashedFileReader reader;
|
||||||
SnapshotDecoder<HashedFileReader> decoder(reader);
|
communication::bolt::Decoder<HashedFileReader> decoder(reader);
|
||||||
|
|
||||||
// The following checks are ok because we are only trying to detect
|
// The following checks are ok because we are only trying to detect
|
||||||
// version inconsistencies.
|
// version inconsistencies.
|
||||||
@ -108,9 +104,9 @@ using communication::bolt::Value;
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool RecoverSnapshot(const fs::path &snapshot_file, database::GraphDb *db,
|
bool RecoverSnapshot(const fs::path &snapshot_file, database::GraphDb *db,
|
||||||
RecoveryData *recovery_data, int worker_id) {
|
RecoveryData *recovery_data) {
|
||||||
HashedFileReader reader;
|
HashedFileReader reader;
|
||||||
SnapshotDecoder<HashedFileReader> decoder(reader);
|
communication::bolt::Decoder<HashedFileReader> decoder(reader);
|
||||||
|
|
||||||
RETURN_IF_NOT(reader.Open(snapshot_file));
|
RETURN_IF_NOT(reader.Open(snapshot_file));
|
||||||
|
|
||||||
@ -129,20 +125,6 @@ bool RecoverSnapshot(const fs::path &snapshot_file, database::GraphDb *db,
|
|||||||
RETURN_IF_NOT(decoder.ReadValue(&dv, Value::Type::Int) &&
|
RETURN_IF_NOT(decoder.ReadValue(&dv, Value::Type::Int) &&
|
||||||
dv.ValueInt() == durability::kVersion);
|
dv.ValueInt() == durability::kVersion);
|
||||||
|
|
||||||
// Checks worker id was set correctly
|
|
||||||
RETURN_IF_NOT(decoder.ReadValue(&dv, Value::Type::Int) &&
|
|
||||||
dv.ValueInt() == worker_id);
|
|
||||||
|
|
||||||
// Vertex and edge generator ids
|
|
||||||
RETURN_IF_NOT(decoder.ReadValue(&dv, Value::Type::Int));
|
|
||||||
uint64_t vertex_generator_cnt = dv.ValueInt();
|
|
||||||
db->storage().VertexGenerator().SetId(std::max(
|
|
||||||
db->storage().VertexGenerator().LocalCount(), vertex_generator_cnt));
|
|
||||||
RETURN_IF_NOT(decoder.ReadValue(&dv, Value::Type::Int));
|
|
||||||
uint64_t edge_generator_cnt = dv.ValueInt();
|
|
||||||
db->storage().EdgeGenerator().SetId(
|
|
||||||
std::max(db->storage().EdgeGenerator().LocalCount(), edge_generator_cnt));
|
|
||||||
|
|
||||||
RETURN_IF_NOT(decoder.ReadValue(&dv, Value::Type::Int));
|
RETURN_IF_NOT(decoder.ReadValue(&dv, Value::Type::Int));
|
||||||
recovery_data->snapshooter_tx_id = dv.ValueInt();
|
recovery_data->snapshooter_tx_id = dv.ValueInt();
|
||||||
// Transaction snapshot of the transaction that created the snapshot.
|
// Transaction snapshot of the transaction that created the snapshot.
|
||||||
@ -165,87 +147,33 @@ bool RecoverSnapshot(const fs::path &snapshot_file, database::GraphDb *db,
|
|||||||
}
|
}
|
||||||
|
|
||||||
auto dba = db->Access();
|
auto dba = db->Access();
|
||||||
std::unordered_map<gid::Gid,
|
std::unordered_map<uint64_t, VertexAccessor> vertices;
|
||||||
std::pair<storage::VertexAddress, storage::VertexAddress>>
|
|
||||||
edge_gid_endpoints_mapping;
|
|
||||||
|
|
||||||
for (int64_t i = 0; i < vertex_count; ++i) {
|
for (int64_t i = 0; i < vertex_count; ++i) {
|
||||||
auto vertex = decoder.ReadSnapshotVertex();
|
Value vertex_dv;
|
||||||
RETURN_IF_NOT(vertex);
|
RETURN_IF_NOT(decoder.ReadValue(&vertex_dv, Value::Type::Vertex));
|
||||||
|
auto &vertex = vertex_dv.ValueVertex();
|
||||||
|
auto vertex_accessor = dba->InsertVertex(vertex.id.AsUint());
|
||||||
|
|
||||||
auto vertex_accessor = dba->InsertVertex(vertex->gid, vertex->cypher_id);
|
for (const auto &label : vertex.labels) {
|
||||||
for (const auto &label : vertex->labels) {
|
|
||||||
vertex_accessor.add_label(dba->Label(label));
|
vertex_accessor.add_label(dba->Label(label));
|
||||||
}
|
}
|
||||||
for (const auto &property_pair : vertex->properties) {
|
for (const auto &property_pair : vertex.properties) {
|
||||||
vertex_accessor.PropsSet(dba->Property(property_pair.first),
|
vertex_accessor.PropsSet(dba->Property(property_pair.first),
|
||||||
glue::ToPropertyValue(property_pair.second));
|
glue::ToPropertyValue(property_pair.second));
|
||||||
}
|
}
|
||||||
auto vertex_record = vertex_accessor.GetNew();
|
vertices.insert({vertex.id.AsUint(), vertex_accessor});
|
||||||
for (const auto &edge : vertex->in) {
|
|
||||||
vertex_record->in_.emplace(edge.vertex, edge.address,
|
|
||||||
dba->EdgeType(edge.type));
|
|
||||||
edge_gid_endpoints_mapping[edge.address.gid()] = {
|
|
||||||
edge.vertex, vertex_accessor.GlobalAddress()};
|
|
||||||
}
|
|
||||||
for (const auto &edge : vertex->out) {
|
|
||||||
vertex_record->out_.emplace(edge.vertex, edge.address,
|
|
||||||
dba->EdgeType(edge.type));
|
|
||||||
edge_gid_endpoints_mapping[edge.address.gid()] = {
|
|
||||||
vertex_accessor.GlobalAddress(), edge.vertex};
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
auto vertex_transform_to_local_if_possible =
|
|
||||||
[&dba, worker_id](storage::VertexAddress &address) {
|
|
||||||
if (address.is_local()) return;
|
|
||||||
// If the worker id matches it should be a local apperance
|
|
||||||
if (address.worker_id() == worker_id) {
|
|
||||||
address = storage::VertexAddress(
|
|
||||||
dba->db().storage().LocalAddress<Vertex>(address.gid()));
|
|
||||||
CHECK(address.is_local()) << "Address should be local but isn't";
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
auto edge_transform_to_local_if_possible =
|
|
||||||
[&dba, worker_id](storage::EdgeAddress &address) {
|
|
||||||
if (address.is_local()) return;
|
|
||||||
// If the worker id matches it should be a local apperance
|
|
||||||
if (address.worker_id() == worker_id) {
|
|
||||||
address = storage::EdgeAddress(
|
|
||||||
dba->db().storage().LocalAddress<Edge>(address.gid()));
|
|
||||||
CHECK(address.is_local()) << "Address should be local but isn't";
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
Value dv_cypher_id;
|
|
||||||
|
|
||||||
for (int64_t i = 0; i < edge_count; ++i) {
|
for (int64_t i = 0; i < edge_count; ++i) {
|
||||||
RETURN_IF_NOT(
|
Value edge_dv;
|
||||||
decoder.ReadValue(&dv, communication::bolt::Value::Type::Edge));
|
RETURN_IF_NOT(decoder.ReadValue(&edge_dv, Value::Type::Edge));
|
||||||
auto &edge = dv.ValueEdge();
|
auto &edge = edge_dv.ValueEdge();
|
||||||
|
auto it_from = vertices.find(edge.from.AsUint());
|
||||||
// Read cypher_id
|
auto it_to = vertices.find(edge.to.AsUint());
|
||||||
RETURN_IF_NOT(decoder.ReadValue(&dv_cypher_id,
|
RETURN_IF_NOT(it_from != vertices.end() && it_to != vertices.end());
|
||||||
communication::bolt::Value::Type::Int));
|
auto edge_accessor =
|
||||||
auto cypher_id = dv_cypher_id.ValueInt();
|
dba->InsertEdge(it_from->second, it_to->second,
|
||||||
|
dba->EdgeType(edge.type), edge.id.AsUint());
|
||||||
// We have to take full edge endpoints from vertices since the endpoints
|
|
||||||
// found here don't containt worker_id, and this can't be changed since this
|
|
||||||
// edges must be bolt-compliant
|
|
||||||
auto &edge_endpoints = edge_gid_endpoints_mapping[edge.id.AsUint()];
|
|
||||||
|
|
||||||
storage::VertexAddress from;
|
|
||||||
storage::VertexAddress to;
|
|
||||||
std::tie(from, to) = edge_endpoints;
|
|
||||||
|
|
||||||
// From and to are written in the global_address format and we should
|
|
||||||
// convert them back to local format for speedup - if possible
|
|
||||||
vertex_transform_to_local_if_possible(from);
|
|
||||||
vertex_transform_to_local_if_possible(to);
|
|
||||||
|
|
||||||
auto edge_accessor = dba->InsertOnlyEdge(from, to, dba->EdgeType(edge.type),
|
|
||||||
edge.id.AsUint(), cypher_id);
|
|
||||||
|
|
||||||
for (const auto &property_pair : edge.properties)
|
for (const auto &property_pair : edge.properties)
|
||||||
edge_accessor.PropsSet(dba->Property(property_pair.first),
|
edge_accessor.PropsSet(dba->Property(property_pair.first),
|
||||||
@ -261,32 +189,6 @@ bool RecoverSnapshot(const fs::path &snapshot_file, database::GraphDb *db,
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// We have to replace global_ids with local ids where possible for all edges
|
|
||||||
// in every vertex and this can only be done after we inserted the edges; this
|
|
||||||
// is to speedup execution
|
|
||||||
for (auto &vertex_accessor : dba->Vertices(true)) {
|
|
||||||
auto vertex = vertex_accessor.GetNew();
|
|
||||||
auto iterate_and_transform =
|
|
||||||
[vertex_transform_to_local_if_possible,
|
|
||||||
edge_transform_to_local_if_possible](Edges &edges) {
|
|
||||||
Edges transformed;
|
|
||||||
for (auto &element : edges) {
|
|
||||||
auto vertex = element.vertex;
|
|
||||||
vertex_transform_to_local_if_possible(vertex);
|
|
||||||
|
|
||||||
auto edge = element.edge;
|
|
||||||
edge_transform_to_local_if_possible(edge);
|
|
||||||
|
|
||||||
transformed.emplace(vertex, edge, element.edge_type);
|
|
||||||
}
|
|
||||||
|
|
||||||
return transformed;
|
|
||||||
};
|
|
||||||
|
|
||||||
vertex->in_ = iterate_and_transform(vertex->in_);
|
|
||||||
vertex->out_ = iterate_and_transform(vertex->out_);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Ensure that the next transaction ID in the recovered DB will be greater
|
// Ensure that the next transaction ID in the recovered DB will be greater
|
||||||
// than the latest one we have recovered. Do this to make sure that
|
// than the latest one we have recovered. Do this to make sure that
|
||||||
// subsequently created snapshots and WAL files will have transactional info
|
// subsequently created snapshots and WAL files will have transactional info
|
||||||
@ -374,8 +276,7 @@ std::vector<tx::TransactionId> ReadWalRecoverableTransactions(
|
|||||||
RecoveryInfo RecoverOnlySnapshot(
|
RecoveryInfo RecoverOnlySnapshot(
|
||||||
const fs::path &durability_dir, database::GraphDb *db,
|
const fs::path &durability_dir, database::GraphDb *db,
|
||||||
RecoveryData *recovery_data,
|
RecoveryData *recovery_data,
|
||||||
std::experimental::optional<tx::TransactionId> required_snapshot_tx_id,
|
std::experimental::optional<tx::TransactionId> required_snapshot_tx_id) {
|
||||||
int worker_id) {
|
|
||||||
// Attempt to recover from snapshot files in reverse order (from newest
|
// Attempt to recover from snapshot files in reverse order (from newest
|
||||||
// backwards).
|
// backwards).
|
||||||
const auto snapshot_dir = durability_dir / kSnapshotDir;
|
const auto snapshot_dir = durability_dir / kSnapshotDir;
|
||||||
@ -397,7 +298,7 @@ RecoveryInfo RecoverOnlySnapshot(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
LOG(INFO) << "Starting snapshot recovery from: " << snapshot_file;
|
LOG(INFO) << "Starting snapshot recovery from: " << snapshot_file;
|
||||||
if (!RecoverSnapshot(snapshot_file, db, recovery_data, worker_id)) {
|
if (!RecoverSnapshot(snapshot_file, db, recovery_data)) {
|
||||||
db->ReinitializeStorage();
|
db->ReinitializeStorage();
|
||||||
recovery_data->Clear();
|
recovery_data->Clear();
|
||||||
LOG(WARNING) << "Snapshot recovery failed, trying older snapshot...";
|
LOG(WARNING) << "Snapshot recovery failed, trying older snapshot...";
|
||||||
|
@ -110,13 +110,12 @@ void MoveToBackup(const std::experimental::filesystem::path &durability_dir);
|
|||||||
RecoveryInfo RecoverOnlySnapshot(
|
RecoveryInfo RecoverOnlySnapshot(
|
||||||
const std::experimental::filesystem::path &durability_dir,
|
const std::experimental::filesystem::path &durability_dir,
|
||||||
database::GraphDb *db, durability::RecoveryData *recovery_data,
|
database::GraphDb *db, durability::RecoveryData *recovery_data,
|
||||||
std::experimental::optional<tx::TransactionId> required_snapshot_tx_id,
|
std::experimental::optional<tx::TransactionId> required_snapshot_tx_id);
|
||||||
int worker_id);
|
|
||||||
|
|
||||||
/** Interface for accessing transactions during WAL recovery. */
|
/** Interface for accessing transactions during WAL recovery. */
|
||||||
class RecoveryTransactions {
|
class RecoveryTransactions {
|
||||||
public:
|
public:
|
||||||
RecoveryTransactions(database::GraphDb *db);
|
explicit RecoveryTransactions(database::GraphDb *db);
|
||||||
|
|
||||||
void Begin(const tx::TransactionId &tx_id);
|
void Begin(const tx::TransactionId &tx_id);
|
||||||
|
|
||||||
|
@ -4,11 +4,12 @@
|
|||||||
|
|
||||||
#include <glog/logging.h>
|
#include <glog/logging.h>
|
||||||
|
|
||||||
|
#include "communication/bolt/v1/encoder/base_encoder.hpp"
|
||||||
#include "database/single_node/graph_db_accessor.hpp"
|
#include "database/single_node/graph_db_accessor.hpp"
|
||||||
#include "durability/hashed_file_writer.hpp"
|
#include "durability/hashed_file_writer.hpp"
|
||||||
#include "durability/paths.hpp"
|
#include "durability/single_node/paths.hpp"
|
||||||
#include "durability/single_node/snapshot_encoder.hpp"
|
|
||||||
#include "durability/single_node/version.hpp"
|
#include "durability/single_node/version.hpp"
|
||||||
|
#include "glue/communication.hpp"
|
||||||
#include "utils/file.hpp"
|
#include "utils/file.hpp"
|
||||||
|
|
||||||
namespace fs = std::experimental::filesystem;
|
namespace fs = std::experimental::filesystem;
|
||||||
@ -16,30 +17,21 @@ namespace fs = std::experimental::filesystem;
|
|||||||
namespace durability {
|
namespace durability {
|
||||||
|
|
||||||
// Snapshot layout is described in durability/version.hpp
|
// Snapshot layout is described in durability/version.hpp
|
||||||
static_assert(durability::kVersion == 6,
|
static_assert(durability::kVersion == 7,
|
||||||
"Wrong snapshot version, please update!");
|
"Wrong snapshot version, please update!");
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
bool Encode(const fs::path &snapshot_file, database::GraphDb &db,
|
bool Encode(const fs::path &snapshot_file, database::GraphDb &db,
|
||||||
database::GraphDbAccessor &dba, int worker_id) {
|
database::GraphDbAccessor &dba) {
|
||||||
try {
|
try {
|
||||||
HashedFileWriter buffer(snapshot_file);
|
HashedFileWriter buffer(snapshot_file);
|
||||||
SnapshotEncoder<HashedFileWriter> encoder(buffer);
|
communication::bolt::BaseEncoder<HashedFileWriter> encoder(buffer);
|
||||||
int64_t vertex_num = 0, edge_num = 0;
|
int64_t vertex_num = 0, edge_num = 0;
|
||||||
|
|
||||||
encoder.WriteRAW(durability::kSnapshotMagic.data(),
|
encoder.WriteRAW(durability::kSnapshotMagic.data(),
|
||||||
durability::kSnapshotMagic.size());
|
durability::kSnapshotMagic.size());
|
||||||
encoder.WriteInt(durability::kVersion);
|
encoder.WriteInt(durability::kVersion);
|
||||||
|
|
||||||
// Writes the worker id to snapshot, used to guarantee consistent cluster
|
|
||||||
// state after recovery
|
|
||||||
encoder.WriteInt(worker_id);
|
|
||||||
|
|
||||||
// Write the number of generated vertex and edges, used to recover
|
|
||||||
// generators internal states
|
|
||||||
encoder.WriteInt(db.storage().VertexGenerator().LocalCount());
|
|
||||||
encoder.WriteInt(db.storage().EdgeGenerator().LocalCount());
|
|
||||||
|
|
||||||
// Write the ID of the transaction doing the snapshot.
|
// Write the ID of the transaction doing the snapshot.
|
||||||
encoder.WriteInt(dba.transaction_id());
|
encoder.WriteInt(dba.transaction_id());
|
||||||
|
|
||||||
@ -63,12 +55,11 @@ bool Encode(const fs::path &snapshot_file, database::GraphDb &db,
|
|||||||
}
|
}
|
||||||
|
|
||||||
for (const auto &vertex : dba.Vertices(false)) {
|
for (const auto &vertex : dba.Vertices(false)) {
|
||||||
encoder.WriteSnapshotVertex(vertex);
|
encoder.WriteVertex(glue::ToBoltVertex(vertex));
|
||||||
vertex_num++;
|
vertex_num++;
|
||||||
}
|
}
|
||||||
for (const auto &edge : dba.Edges(false)) {
|
for (const auto &edge : dba.Edges(false)) {
|
||||||
encoder.WriteEdge(glue::ToBoltEdge(edge));
|
encoder.WriteEdge(glue::ToBoltEdge(edge));
|
||||||
encoder.WriteInt(edge.CypherId());
|
|
||||||
edge_num++;
|
edge_num++;
|
||||||
}
|
}
|
||||||
buffer.WriteValue(vertex_num);
|
buffer.WriteValue(vertex_num);
|
||||||
@ -122,13 +113,12 @@ void RemoveOldWals(const fs::path &wal_dir,
|
|||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
bool MakeSnapshot(database::GraphDb &db, database::GraphDbAccessor &dba,
|
bool MakeSnapshot(database::GraphDb &db, database::GraphDbAccessor &dba,
|
||||||
int worker_id, const fs::path &durability_dir,
|
const fs::path &durability_dir, int snapshot_max_retained) {
|
||||||
int snapshot_max_retained) {
|
|
||||||
if (!utils::EnsureDir(durability_dir / kSnapshotDir)) return false;
|
if (!utils::EnsureDir(durability_dir / kSnapshotDir)) return false;
|
||||||
const auto snapshot_file =
|
const auto snapshot_file =
|
||||||
MakeSnapshotPath(durability_dir, worker_id, dba.transaction_id());
|
MakeSnapshotPath(durability_dir, dba.transaction_id());
|
||||||
if (fs::exists(snapshot_file)) return false;
|
if (fs::exists(snapshot_file)) return false;
|
||||||
if (Encode(snapshot_file, db, dba, worker_id)) {
|
if (Encode(snapshot_file, db, dba)) {
|
||||||
RemoveOldSnapshots(durability_dir / kSnapshotDir, snapshot_max_retained);
|
RemoveOldSnapshots(durability_dir / kSnapshotDir, snapshot_max_retained);
|
||||||
RemoveOldWals(durability_dir / kWalDir, dba.transaction());
|
RemoveOldWals(durability_dir / kWalDir, dba.transaction());
|
||||||
return true;
|
return true;
|
||||||
|
@ -14,7 +14,6 @@ namespace durability {
|
|||||||
* @param snapshot_max_retained - maximum number of snapshots to retain.
|
* @param snapshot_max_retained - maximum number of snapshots to retain.
|
||||||
*/
|
*/
|
||||||
bool MakeSnapshot(database::GraphDb &db, database::GraphDbAccessor &dba,
|
bool MakeSnapshot(database::GraphDb &db, database::GraphDbAccessor &dba,
|
||||||
int worker_id,
|
|
||||||
const std::experimental::filesystem::path &durability_dir,
|
const std::experimental::filesystem::path &durability_dir,
|
||||||
int snapshot_max_retained);
|
int snapshot_max_retained);
|
||||||
|
|
||||||
|
@ -1,105 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include <experimental/optional>
|
|
||||||
|
|
||||||
#include "communication/bolt/v1/decoder/decoder.hpp"
|
|
||||||
#include "durability/single_node/snapshot_value.hpp"
|
|
||||||
|
|
||||||
namespace durability {
|
|
||||||
|
|
||||||
template <typename Buffer>
|
|
||||||
class SnapshotDecoder : public communication::bolt::Decoder<Buffer> {
|
|
||||||
public:
|
|
||||||
explicit SnapshotDecoder(Buffer &buffer)
|
|
||||||
: communication::bolt::Decoder<Buffer>(buffer) {}
|
|
||||||
|
|
||||||
std::experimental::optional<SnapshotVertex> ReadSnapshotVertex() {
|
|
||||||
communication::bolt::Value dv;
|
|
||||||
SnapshotVertex vertex;
|
|
||||||
|
|
||||||
// Read global id, labels and properties of the vertex
|
|
||||||
if (!communication::bolt::Decoder<Buffer>::ReadValue(
|
|
||||||
&dv, communication::bolt::Value::Type::Vertex)) {
|
|
||||||
DLOG(WARNING) << "Unable to read snapshot vertex";
|
|
||||||
return std::experimental::nullopt;
|
|
||||||
}
|
|
||||||
auto &read_vertex = dv.ValueVertex();
|
|
||||||
vertex.gid = read_vertex.id.AsUint();
|
|
||||||
vertex.labels = read_vertex.labels;
|
|
||||||
vertex.properties = read_vertex.properties;
|
|
||||||
|
|
||||||
// Read cypher_id
|
|
||||||
if (!communication::bolt::Decoder<Buffer>::ReadValue(
|
|
||||||
&dv, communication::bolt::Value::Type::Int)) {
|
|
||||||
DLOG(WARNING) << "Unable to read vertex cypher_id";
|
|
||||||
return std::experimental::nullopt;
|
|
||||||
}
|
|
||||||
vertex.cypher_id = dv.ValueInt();
|
|
||||||
|
|
||||||
// Read in edges
|
|
||||||
if (!communication::bolt::Decoder<Buffer>::ReadValue(
|
|
||||||
&dv, communication::bolt::Value::Type::Int)) {
|
|
||||||
DLOG(WARNING) << "[ReadSnapshotVertex] Couldn't read number of in "
|
|
||||||
"edges in vertex!";
|
|
||||||
return std::experimental::nullopt;
|
|
||||||
}
|
|
||||||
for (int i = 0; i < dv.ValueInt(); ++i) {
|
|
||||||
auto edge = ReadSnapshotEdge();
|
|
||||||
if (!edge) return std::experimental::nullopt;
|
|
||||||
vertex.in.emplace_back(*edge);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Read out edges
|
|
||||||
if (!communication::bolt::Decoder<Buffer>::ReadValue(
|
|
||||||
&dv, communication::bolt::Value::Type::Int)) {
|
|
||||||
DLOG(WARNING) << "[ReadSnapshotVertex] Couldn't read number of out "
|
|
||||||
"edges in vertex!";
|
|
||||||
return std::experimental::nullopt;
|
|
||||||
}
|
|
||||||
for (int i = 0; i < dv.ValueInt(); ++i) {
|
|
||||||
auto edge = ReadSnapshotEdge();
|
|
||||||
if (!edge) return std::experimental::nullopt;
|
|
||||||
vertex.out.emplace_back(*edge);
|
|
||||||
}
|
|
||||||
|
|
||||||
VLOG(20) << "[ReadSnapshotVertex] Success";
|
|
||||||
return vertex;
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
std::experimental::optional<InlinedVertexEdge> ReadSnapshotEdge() {
|
|
||||||
communication::bolt::Value dv;
|
|
||||||
InlinedVertexEdge edge;
|
|
||||||
|
|
||||||
VLOG(20) << "[ReadSnapshotEdge] Start";
|
|
||||||
|
|
||||||
// Read global id of this edge
|
|
||||||
if (!communication::bolt::Decoder<Buffer>::ReadValue(
|
|
||||||
&dv, communication::bolt::Value::Type::Int)) {
|
|
||||||
DLOG(WARNING) << "[ReadSnapshotEdge] Couldn't read Global ID!";
|
|
||||||
return std::experimental::nullopt;
|
|
||||||
}
|
|
||||||
edge.address = storage::EdgeAddress(static_cast<uint64_t>(dv.ValueInt()));
|
|
||||||
|
|
||||||
// Read global vertex id of the other side of the edge
|
|
||||||
// (global id of from/to vertexes).
|
|
||||||
if (!communication::bolt::Decoder<Buffer>::ReadValue(
|
|
||||||
&dv, communication::bolt::Value::Type::Int)) {
|
|
||||||
DLOG(WARNING) << "[ReadSnapshotEdge] Couldn't read from/to address!";
|
|
||||||
return std::experimental::nullopt;
|
|
||||||
}
|
|
||||||
edge.vertex = storage::VertexAddress(static_cast<uint64_t>(dv.ValueInt()));
|
|
||||||
|
|
||||||
// Read edge type
|
|
||||||
if (!communication::bolt::Decoder<Buffer>::ReadValue(
|
|
||||||
&dv, communication::bolt::Value::Type::String)) {
|
|
||||||
DLOG(WARNING) << "[ReadSnapshotEdge] Couldn't read type!";
|
|
||||||
return std::experimental::nullopt;
|
|
||||||
}
|
|
||||||
edge.type = dv.ValueString();
|
|
||||||
|
|
||||||
VLOG(20) << "[ReadSnapshotEdge] Success";
|
|
||||||
return edge;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}; // namespace durability
|
|
@ -1,58 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include "communication/bolt/v1/encoder/base_encoder.hpp"
|
|
||||||
#include "database/single_node/graph_db_accessor.hpp"
|
|
||||||
#include "glue/communication.hpp"
|
|
||||||
#include "utils/cast.hpp"
|
|
||||||
|
|
||||||
namespace durability {
|
|
||||||
|
|
||||||
template <typename Buffer>
|
|
||||||
class SnapshotEncoder : public communication::bolt::BaseEncoder<Buffer> {
|
|
||||||
public:
|
|
||||||
explicit SnapshotEncoder(Buffer &buffer)
|
|
||||||
: communication::bolt::BaseEncoder<Buffer>(buffer) {}
|
|
||||||
void WriteSnapshotVertex(const VertexAccessor &vertex) {
|
|
||||||
communication::bolt::BaseEncoder<Buffer>::WriteVertex(
|
|
||||||
glue::ToBoltVertex(vertex));
|
|
||||||
|
|
||||||
// Write cypher_id
|
|
||||||
this->WriteInt(vertex.CypherId());
|
|
||||||
|
|
||||||
// Write in edges without properties
|
|
||||||
this->WriteUInt(vertex.in_degree());
|
|
||||||
auto edges_in = vertex.in();
|
|
||||||
for (const auto &edge : edges_in) {
|
|
||||||
this->WriteSnapshotEdge(edge, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Write out edges without properties
|
|
||||||
this->WriteUInt(vertex.out_degree());
|
|
||||||
auto edges_out = vertex.out();
|
|
||||||
for (const auto &edge : edges_out) {
|
|
||||||
this->WriteSnapshotEdge(edge, false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
void WriteUInt(const uint64_t &value) {
|
|
||||||
this->WriteInt(utils::MemcpyCast<int64_t>(value));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Writes edge without properties
|
|
||||||
void WriteSnapshotEdge(const EdgeAccessor &edge, bool write_from) {
|
|
||||||
// Write global id of the edge
|
|
||||||
WriteUInt(edge.GlobalAddress().raw());
|
|
||||||
|
|
||||||
// Write to/from global id
|
|
||||||
if (write_from)
|
|
||||||
WriteUInt(edge.from().GlobalAddress().raw());
|
|
||||||
else
|
|
||||||
WriteUInt(edge.to().GlobalAddress().raw());
|
|
||||||
|
|
||||||
// Write type
|
|
||||||
this->WriteString(edge.db_accessor().EdgeTypeName(edge.EdgeType()));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace durability
|
|
@ -1,46 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include <map>
|
|
||||||
#include <string>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
#include "communication/bolt/v1/value.hpp"
|
|
||||||
// TODO: WTF is this doing here?
|
|
||||||
#include "query/typed_value.hpp"
|
|
||||||
#include "storage/common/property_value.hpp"
|
|
||||||
#include "storage/single_node/address_types.hpp"
|
|
||||||
#include "utils/algorithm.hpp"
|
|
||||||
#include "utils/exceptions.hpp"
|
|
||||||
|
|
||||||
namespace durability {
|
|
||||||
|
|
||||||
/** Forward declartion of SnapshotEdge. */
|
|
||||||
struct InlinedVertexEdge;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Structure used when reading a Vertex with the decoder.
|
|
||||||
* The decoder writes data into this structure.
|
|
||||||
*/
|
|
||||||
struct SnapshotVertex {
|
|
||||||
gid::Gid gid;
|
|
||||||
int64_t cypher_id;
|
|
||||||
std::vector<std::string> labels;
|
|
||||||
std::map<std::string, communication::bolt::Value> properties;
|
|
||||||
// Vector of edges without properties
|
|
||||||
std::vector<InlinedVertexEdge> in;
|
|
||||||
std::vector<InlinedVertexEdge> out;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Structure used when reading an Edge with the snapshot decoder.
|
|
||||||
* The decoder writes data into this structure.
|
|
||||||
*/
|
|
||||||
struct InlinedVertexEdge {
|
|
||||||
// Addresses down below must always be global_address and never direct
|
|
||||||
// pointers to a record.
|
|
||||||
storage::EdgeAddress address;
|
|
||||||
storage::VertexAddress vertex;
|
|
||||||
std::string type;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace durability
|
|
@ -20,22 +20,20 @@ StateDelta StateDelta::TxAbort(tx::TransactionId tx_id) {
|
|||||||
return {StateDelta::Type::TRANSACTION_ABORT, tx_id};
|
return {StateDelta::Type::TRANSACTION_ABORT, tx_id};
|
||||||
}
|
}
|
||||||
|
|
||||||
StateDelta StateDelta::CreateVertex(tx::TransactionId tx_id, gid::Gid vertex_id,
|
StateDelta StateDelta::CreateVertex(tx::TransactionId tx_id,
|
||||||
int64_t cypher_id) {
|
gid::Gid vertex_id) {
|
||||||
StateDelta op(StateDelta::Type::CREATE_VERTEX, tx_id);
|
StateDelta op(StateDelta::Type::CREATE_VERTEX, tx_id);
|
||||||
op.vertex_id = vertex_id;
|
op.vertex_id = vertex_id;
|
||||||
op.cypher_id = cypher_id;
|
|
||||||
return op;
|
return op;
|
||||||
}
|
}
|
||||||
|
|
||||||
StateDelta StateDelta::CreateEdge(tx::TransactionId tx_id, gid::Gid edge_id,
|
StateDelta StateDelta::CreateEdge(tx::TransactionId tx_id, gid::Gid edge_id,
|
||||||
int64_t cypher_id, gid::Gid vertex_from_id,
|
gid::Gid vertex_from_id,
|
||||||
gid::Gid vertex_to_id,
|
gid::Gid vertex_to_id,
|
||||||
storage::EdgeType edge_type,
|
storage::EdgeType edge_type,
|
||||||
const std::string &edge_type_name) {
|
const std::string &edge_type_name) {
|
||||||
StateDelta op(StateDelta::Type::CREATE_EDGE, tx_id);
|
StateDelta op(StateDelta::Type::CREATE_EDGE, tx_id);
|
||||||
op.edge_id = edge_id;
|
op.edge_id = edge_id;
|
||||||
op.cypher_id = cypher_id;
|
|
||||||
op.vertex_from_id = vertex_from_id;
|
op.vertex_from_id = vertex_from_id;
|
||||||
op.vertex_to_id = vertex_to_id;
|
op.vertex_to_id = vertex_to_id;
|
||||||
op.edge_type = edge_type;
|
op.edge_type = edge_type;
|
||||||
@ -43,53 +41,6 @@ StateDelta StateDelta::CreateEdge(tx::TransactionId tx_id, gid::Gid edge_id,
|
|||||||
return op;
|
return op;
|
||||||
}
|
}
|
||||||
|
|
||||||
StateDelta StateDelta::AddOutEdge(tx::TransactionId tx_id, gid::Gid vertex_id,
|
|
||||||
storage::VertexAddress vertex_to_address,
|
|
||||||
storage::EdgeAddress edge_address,
|
|
||||||
storage::EdgeType edge_type) {
|
|
||||||
CHECK(vertex_to_address.is_remote() && edge_address.is_remote())
|
|
||||||
<< "WAL can only contain global addresses.";
|
|
||||||
StateDelta op(StateDelta::Type::ADD_OUT_EDGE, tx_id);
|
|
||||||
op.vertex_id = vertex_id;
|
|
||||||
op.vertex_to_address = vertex_to_address;
|
|
||||||
op.edge_address = edge_address;
|
|
||||||
op.edge_type = edge_type;
|
|
||||||
return op;
|
|
||||||
}
|
|
||||||
|
|
||||||
StateDelta StateDelta::RemoveOutEdge(tx::TransactionId tx_id,
|
|
||||||
gid::Gid vertex_id,
|
|
||||||
storage::EdgeAddress edge_address) {
|
|
||||||
CHECK(edge_address.is_remote()) << "WAL can only contain global addresses.";
|
|
||||||
StateDelta op(StateDelta::Type::REMOVE_OUT_EDGE, tx_id);
|
|
||||||
op.vertex_id = vertex_id;
|
|
||||||
op.edge_address = edge_address;
|
|
||||||
return op;
|
|
||||||
}
|
|
||||||
|
|
||||||
StateDelta StateDelta::AddInEdge(tx::TransactionId tx_id, gid::Gid vertex_id,
|
|
||||||
storage::VertexAddress vertex_from_address,
|
|
||||||
storage::EdgeAddress edge_address,
|
|
||||||
storage::EdgeType edge_type) {
|
|
||||||
CHECK(vertex_from_address.is_remote() && edge_address.is_remote())
|
|
||||||
<< "WAL can only contain global addresses.";
|
|
||||||
StateDelta op(StateDelta::Type::ADD_IN_EDGE, tx_id);
|
|
||||||
op.vertex_id = vertex_id;
|
|
||||||
op.vertex_from_address = vertex_from_address;
|
|
||||||
op.edge_address = edge_address;
|
|
||||||
op.edge_type = edge_type;
|
|
||||||
return op;
|
|
||||||
}
|
|
||||||
|
|
||||||
StateDelta StateDelta::RemoveInEdge(tx::TransactionId tx_id, gid::Gid vertex_id,
|
|
||||||
storage::EdgeAddress edge_address) {
|
|
||||||
CHECK(edge_address.is_remote()) << "WAL can only contain global addresses.";
|
|
||||||
StateDelta op(StateDelta::Type::REMOVE_IN_EDGE, tx_id);
|
|
||||||
op.vertex_id = vertex_id;
|
|
||||||
op.edge_address = edge_address;
|
|
||||||
return op;
|
|
||||||
}
|
|
||||||
|
|
||||||
StateDelta StateDelta::PropsSetVertex(tx::TransactionId tx_id,
|
StateDelta StateDelta::PropsSetVertex(tx::TransactionId tx_id,
|
||||||
gid::Gid vertex_id,
|
gid::Gid vertex_id,
|
||||||
storage::Property property,
|
storage::Property property,
|
||||||
@ -174,36 +125,14 @@ void StateDelta::Encode(
|
|||||||
break;
|
break;
|
||||||
case Type::CREATE_VERTEX:
|
case Type::CREATE_VERTEX:
|
||||||
encoder.WriteInt(vertex_id);
|
encoder.WriteInt(vertex_id);
|
||||||
encoder.WriteInt(cypher_id);
|
|
||||||
break;
|
break;
|
||||||
case Type::CREATE_EDGE:
|
case Type::CREATE_EDGE:
|
||||||
encoder.WriteInt(edge_id);
|
encoder.WriteInt(edge_id);
|
||||||
encoder.WriteInt(cypher_id);
|
|
||||||
encoder.WriteInt(vertex_from_id);
|
encoder.WriteInt(vertex_from_id);
|
||||||
encoder.WriteInt(vertex_to_id);
|
encoder.WriteInt(vertex_to_id);
|
||||||
encoder.WriteInt(edge_type.Id());
|
encoder.WriteInt(edge_type.Id());
|
||||||
encoder.WriteString(edge_type_name);
|
encoder.WriteString(edge_type_name);
|
||||||
break;
|
break;
|
||||||
case Type::ADD_OUT_EDGE:
|
|
||||||
encoder.WriteInt(vertex_id);
|
|
||||||
encoder.WriteInt(vertex_to_address.raw());
|
|
||||||
encoder.WriteInt(edge_address.raw());
|
|
||||||
encoder.WriteInt(edge_type.Id());
|
|
||||||
break;
|
|
||||||
case Type::REMOVE_OUT_EDGE:
|
|
||||||
encoder.WriteInt(vertex_id);
|
|
||||||
encoder.WriteInt(edge_address.raw());
|
|
||||||
break;
|
|
||||||
case Type::ADD_IN_EDGE:
|
|
||||||
encoder.WriteInt(vertex_id);
|
|
||||||
encoder.WriteInt(vertex_from_address.raw());
|
|
||||||
encoder.WriteInt(edge_address.raw());
|
|
||||||
encoder.WriteInt(edge_type.Id());
|
|
||||||
break;
|
|
||||||
case Type::REMOVE_IN_EDGE:
|
|
||||||
encoder.WriteInt(vertex_id);
|
|
||||||
encoder.WriteInt(edge_address.raw());
|
|
||||||
break;
|
|
||||||
case Type::SET_PROPERTY_VERTEX:
|
case Type::SET_PROPERTY_VERTEX:
|
||||||
encoder.WriteInt(vertex_id);
|
encoder.WriteInt(vertex_id);
|
||||||
encoder.WriteInt(property.Id());
|
encoder.WriteInt(property.Id());
|
||||||
@ -268,37 +197,14 @@ std::experimental::optional<StateDelta> StateDelta::Decode(
|
|||||||
break;
|
break;
|
||||||
case Type::CREATE_VERTEX:
|
case Type::CREATE_VERTEX:
|
||||||
DECODE_MEMBER(vertex_id, ValueInt)
|
DECODE_MEMBER(vertex_id, ValueInt)
|
||||||
DECODE_MEMBER(cypher_id, ValueInt)
|
|
||||||
break;
|
break;
|
||||||
case Type::CREATE_EDGE:
|
case Type::CREATE_EDGE:
|
||||||
DECODE_MEMBER(edge_id, ValueInt)
|
DECODE_MEMBER(edge_id, ValueInt)
|
||||||
DECODE_MEMBER(cypher_id, ValueInt)
|
|
||||||
DECODE_MEMBER(vertex_from_id, ValueInt)
|
DECODE_MEMBER(vertex_from_id, ValueInt)
|
||||||
DECODE_MEMBER(vertex_to_id, ValueInt)
|
DECODE_MEMBER(vertex_to_id, ValueInt)
|
||||||
DECODE_MEMBER_CAST(edge_type, ValueInt, storage::EdgeType)
|
DECODE_MEMBER_CAST(edge_type, ValueInt, storage::EdgeType)
|
||||||
DECODE_MEMBER(edge_type_name, ValueString)
|
DECODE_MEMBER(edge_type_name, ValueString)
|
||||||
break;
|
break;
|
||||||
case Type::ADD_OUT_EDGE:
|
|
||||||
DECODE_MEMBER(vertex_id, ValueInt)
|
|
||||||
DECODE_MEMBER_CAST(vertex_to_address, ValueInt, storage::VertexAddress)
|
|
||||||
DECODE_MEMBER_CAST(edge_address, ValueInt, storage::EdgeAddress)
|
|
||||||
DECODE_MEMBER_CAST(edge_type, ValueInt, storage::EdgeType)
|
|
||||||
break;
|
|
||||||
case Type::REMOVE_OUT_EDGE:
|
|
||||||
DECODE_MEMBER(vertex_id, ValueInt)
|
|
||||||
DECODE_MEMBER_CAST(edge_address, ValueInt, storage::EdgeAddress)
|
|
||||||
break;
|
|
||||||
case Type::ADD_IN_EDGE:
|
|
||||||
DECODE_MEMBER(vertex_id, ValueInt)
|
|
||||||
DECODE_MEMBER_CAST(vertex_from_address, ValueInt,
|
|
||||||
storage::VertexAddress)
|
|
||||||
DECODE_MEMBER_CAST(edge_address, ValueInt, storage::EdgeAddress)
|
|
||||||
DECODE_MEMBER_CAST(edge_type, ValueInt, storage::EdgeType)
|
|
||||||
break;
|
|
||||||
case Type::REMOVE_IN_EDGE:
|
|
||||||
DECODE_MEMBER(vertex_id, ValueInt)
|
|
||||||
DECODE_MEMBER_CAST(edge_address, ValueInt, storage::EdgeAddress)
|
|
||||||
break;
|
|
||||||
case Type::SET_PROPERTY_VERTEX:
|
case Type::SET_PROPERTY_VERTEX:
|
||||||
DECODE_MEMBER(vertex_id, ValueInt)
|
DECODE_MEMBER(vertex_id, ValueInt)
|
||||||
DECODE_MEMBER_CAST(property, ValueInt, storage::Property)
|
DECODE_MEMBER_CAST(property, ValueInt, storage::Property)
|
||||||
@ -357,20 +263,14 @@ void StateDelta::Apply(GraphDbAccessor &dba) const {
|
|||||||
LOG(FATAL) << "Transaction handling not handled in Apply";
|
LOG(FATAL) << "Transaction handling not handled in Apply";
|
||||||
break;
|
break;
|
||||||
case Type::CREATE_VERTEX:
|
case Type::CREATE_VERTEX:
|
||||||
dba.InsertVertex(vertex_id, cypher_id);
|
dba.InsertVertex(vertex_id);
|
||||||
break;
|
break;
|
||||||
case Type::CREATE_EDGE: {
|
case Type::CREATE_EDGE: {
|
||||||
auto from = dba.FindVertex(vertex_from_id, true);
|
auto from = dba.FindVertex(vertex_from_id, true);
|
||||||
auto to = dba.FindVertex(vertex_to_id, true);
|
auto to = dba.FindVertex(vertex_to_id, true);
|
||||||
dba.InsertEdge(from, to, dba.EdgeType(edge_type_name), edge_id,
|
dba.InsertEdge(from, to, dba.EdgeType(edge_type_name), edge_id);
|
||||||
cypher_id);
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case Type::ADD_OUT_EDGE:
|
|
||||||
case Type::REMOVE_OUT_EDGE:
|
|
||||||
case Type::ADD_IN_EDGE:
|
|
||||||
case Type::REMOVE_IN_EDGE:
|
|
||||||
LOG(FATAL) << "Partial edge creation/deletion not yet supported in Apply";
|
|
||||||
case Type::SET_PROPERTY_VERTEX: {
|
case Type::SET_PROPERTY_VERTEX: {
|
||||||
auto vertex = dba.FindVertex(vertex_id, true);
|
auto vertex = dba.FindVertex(vertex_id, true);
|
||||||
vertex.PropsSet(dba.Property(property_name), value);
|
vertex.PropsSet(dba.Property(property_name), value);
|
||||||
|
@ -5,10 +5,13 @@
|
|||||||
#include "communication/bolt/v1/encoder/base_encoder.hpp"
|
#include "communication/bolt/v1/encoder/base_encoder.hpp"
|
||||||
#include "durability/hashed_file_reader.hpp"
|
#include "durability/hashed_file_reader.hpp"
|
||||||
#include "durability/hashed_file_writer.hpp"
|
#include "durability/hashed_file_writer.hpp"
|
||||||
|
#include "mvcc/single_node/version_list.hpp"
|
||||||
#include "storage/common/property_value.hpp"
|
#include "storage/common/property_value.hpp"
|
||||||
#include "storage/common/types.hpp"
|
#include "storage/common/types.hpp"
|
||||||
#include "storage/single_node/address_types.hpp"
|
|
||||||
#include "storage/single_node/gid.hpp"
|
#include "storage/single_node/gid.hpp"
|
||||||
|
|
||||||
|
class Vertex;
|
||||||
|
class Edge;
|
||||||
cpp<#
|
cpp<#
|
||||||
|
|
||||||
(lcp:namespace database)
|
(lcp:namespace database)
|
||||||
@ -27,12 +30,11 @@ cpp<#
|
|||||||
;; only keep addresses.
|
;; only keep addresses.
|
||||||
(vertex-id "gid::Gid")
|
(vertex-id "gid::Gid")
|
||||||
(edge-id "gid::Gid")
|
(edge-id "gid::Gid")
|
||||||
(cypher-id :int64_t)
|
(edge-address "mvcc::VersionList<Edge> *")
|
||||||
(edge-address "storage::EdgeAddress")
|
|
||||||
(vertex-from-id "gid::Gid")
|
(vertex-from-id "gid::Gid")
|
||||||
(vertex-from-address "storage::VertexAddress")
|
(vertex-from-address "mvcc::VersionList<Vertex> *")
|
||||||
(vertex-to-id "gid::Gid")
|
(vertex-to-id "gid::Gid")
|
||||||
(vertex-to-address "storage::VertexAddress")
|
(vertex-to-address "mvcc::VersionList<Vertex> *")
|
||||||
(edge-type "storage::EdgeType")
|
(edge-type "storage::EdgeType")
|
||||||
(edge-type-name "std::string")
|
(edge-type-name "std::string")
|
||||||
(property "storage::Property")
|
(property "storage::Property")
|
||||||
@ -60,10 +62,6 @@ in StateDeltas.")
|
|||||||
transaction-abort
|
transaction-abort
|
||||||
create-vertex ;; vertex_id
|
create-vertex ;; vertex_id
|
||||||
create-edge ;; edge_id, from_vertex_id, to_vertex_id, edge_type, edge_type_name
|
create-edge ;; edge_id, from_vertex_id, to_vertex_id, edge_type, edge_type_name
|
||||||
add-out-edge ;; vertex_id, edge_address, vertex_to_address, edge_type
|
|
||||||
remove-out-edge ;; vertex_id, edge_address
|
|
||||||
add-in-edge ;; vertex_id, edge_address, vertex_from_address, edge_type
|
|
||||||
remove-in-edge ;; vertex_id, edge_address
|
|
||||||
set-property-vertex ;; vertex_id, property, property_name, property_value
|
set-property-vertex ;; vertex_id, property, property_name, property_value
|
||||||
set-property-edge ;; edge_id, property, property_name, property_value
|
set-property-edge ;; edge_id, property, property_name, property_value
|
||||||
;; remove property is done by setting a PropertyValue::Null
|
;; remove property is done by setting a PropertyValue::Null
|
||||||
@ -98,27 +96,12 @@ omitted in the comment."))
|
|||||||
static StateDelta TxCommit(tx::TransactionId tx_id);
|
static StateDelta TxCommit(tx::TransactionId tx_id);
|
||||||
static StateDelta TxAbort(tx::TransactionId tx_id);
|
static StateDelta TxAbort(tx::TransactionId tx_id);
|
||||||
static StateDelta CreateVertex(tx::TransactionId tx_id,
|
static StateDelta CreateVertex(tx::TransactionId tx_id,
|
||||||
gid::Gid vertex_id,
|
gid::Gid vertex_id);
|
||||||
int64_t cypher_id);
|
|
||||||
static StateDelta CreateEdge(tx::TransactionId tx_id, gid::Gid edge_id,
|
static StateDelta CreateEdge(tx::TransactionId tx_id, gid::Gid edge_id,
|
||||||
int64_t cypher_id,
|
|
||||||
gid::Gid vertex_from_id,
|
gid::Gid vertex_from_id,
|
||||||
gid::Gid vertex_to_id,
|
gid::Gid vertex_to_id,
|
||||||
storage::EdgeType edge_type,
|
storage::EdgeType edge_type,
|
||||||
const std::string &edge_type_name);
|
const std::string &edge_type_name);
|
||||||
static StateDelta AddOutEdge(tx::TransactionId tx_id, gid::Gid vertex_id,
|
|
||||||
storage::VertexAddress vertex_to_address,
|
|
||||||
storage::EdgeAddress edge_address,
|
|
||||||
storage::EdgeType edge_type);
|
|
||||||
static StateDelta RemoveOutEdge(tx::TransactionId tx_id,
|
|
||||||
gid::Gid vertex_id,
|
|
||||||
storage::EdgeAddress edge_address);
|
|
||||||
static StateDelta AddInEdge(tx::TransactionId tx_id, gid::Gid vertex_id,
|
|
||||||
storage::VertexAddress vertex_from_address,
|
|
||||||
storage::EdgeAddress edge_address,
|
|
||||||
storage::EdgeType edge_type);
|
|
||||||
static StateDelta RemoveInEdge(tx::TransactionId tx_id, gid::Gid vertex_id,
|
|
||||||
storage::EdgeAddress edge_address);
|
|
||||||
static StateDelta PropsSetVertex(tx::TransactionId tx_id,
|
static StateDelta PropsSetVertex(tx::TransactionId tx_id,
|
||||||
gid::Gid vertex_id,
|
gid::Gid vertex_id,
|
||||||
storage::Property property,
|
storage::Property property,
|
||||||
|
@ -15,38 +15,29 @@ constexpr std::array<uint8_t, 4> kSnapshotMagic{{'M', 'G', 's', 'n'}};
|
|||||||
constexpr std::array<uint8_t, 4> kWalMagic{{'M', 'G', 'w', 'l'}};
|
constexpr std::array<uint8_t, 4> kWalMagic{{'M', 'G', 'w', 'l'}};
|
||||||
|
|
||||||
// The current default version of snapshot and WAL encoding / decoding.
|
// The current default version of snapshot and WAL encoding / decoding.
|
||||||
constexpr int64_t kVersion{6};
|
constexpr int64_t kVersion{7};
|
||||||
|
|
||||||
// Snapshot format (version 6):
|
// Snapshot format (version 7):
|
||||||
// 1) Magic number + snapshot version
|
// 1) Magic number + snapshot version
|
||||||
// 2) Distributed worker ID
|
|
||||||
//
|
|
||||||
// The following two entries indicate the starting points for generating new
|
|
||||||
// vertex/edge IDs in the DB. They are important when there are vertices/edges
|
|
||||||
// that were moved to another worker (in distributed Memgraph).
|
|
||||||
// 3) Vertex generator ID
|
|
||||||
// 4) Edge generator ID
|
|
||||||
//
|
//
|
||||||
// The following two entries are required when recovering from snapshot combined
|
// The following two entries are required when recovering from snapshot combined
|
||||||
// with WAL to determine record visibility.
|
// with WAL to determine record visibility.
|
||||||
// 5) Transactional ID of the snapshooter
|
// 2) Transactional ID of the snapshooter
|
||||||
// 6) Transactional snapshot of the snapshooter
|
// 3) Transactional snapshot of the snapshooter
|
||||||
//
|
//
|
||||||
// 7) A list of label+property indices.
|
// 4) A list of label+property indices.
|
||||||
//
|
//
|
||||||
// We must inline edges with nodes because some edges might be stored on other
|
// We must inline edges with nodes because some edges might be stored on other
|
||||||
// worker (edges are always stored only on the worker of the edge source).
|
// worker (edges are always stored only on the worker of the edge source).
|
||||||
// 8) Bolt encoded nodes. Each node is written in the following format:
|
// 5) Bolt encoded nodes. Each node is written in the following format:
|
||||||
// * gid, labels, properties
|
// * gid, labels, properties
|
||||||
// * cypher_id
|
|
||||||
// * inlined edges (edge address, other endpoint address and edge type)
|
// * inlined edges (edge address, other endpoint address and edge type)
|
||||||
// 9) Bolt encoded edges. Each edge is written in the following format:
|
// 6) Bolt encoded edges. Each edge is written in the following format:
|
||||||
// * gid
|
// * gid
|
||||||
// * from, to
|
// * from, to
|
||||||
// * edge_type
|
// * edge_type
|
||||||
// * properties
|
// * properties
|
||||||
// * cypher_id
|
|
||||||
//
|
//
|
||||||
// 10) Snapshot summary (number of nodes, number of edges, hash)
|
// 7) Snapshot summary (number of nodes, number of edges, hash)
|
||||||
|
|
||||||
} // namespace durability
|
} // namespace durability
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
#include "wal.hpp"
|
#include "wal.hpp"
|
||||||
|
|
||||||
#include "durability/paths.hpp"
|
#include "durability/single_node/paths.hpp"
|
||||||
#include "durability/single_node/version.hpp"
|
#include "durability/single_node/version.hpp"
|
||||||
#include "utils/file.hpp"
|
#include "utils/file.hpp"
|
||||||
#include "utils/flag_validation.hpp"
|
#include "utils/flag_validation.hpp"
|
||||||
@ -19,11 +19,12 @@ DEFINE_VALIDATED_HIDDEN_int32(wal_buffer_size, 4096,
|
|||||||
FLAG_IN_RANGE(1, 1 << 30));
|
FLAG_IN_RANGE(1, 1 << 30));
|
||||||
|
|
||||||
namespace durability {
|
namespace durability {
|
||||||
|
|
||||||
WriteAheadLog::WriteAheadLog(
|
WriteAheadLog::WriteAheadLog(
|
||||||
int worker_id, const std::experimental::filesystem::path &durability_dir,
|
const std::experimental::filesystem::path &durability_dir,
|
||||||
bool durability_enabled, bool synchronous_commit)
|
bool durability_enabled, bool synchronous_commit)
|
||||||
: deltas_{FLAGS_wal_buffer_size},
|
: deltas_{FLAGS_wal_buffer_size},
|
||||||
wal_file_{worker_id, durability_dir},
|
wal_file_{durability_dir},
|
||||||
durability_enabled_(durability_enabled),
|
durability_enabled_(durability_enabled),
|
||||||
synchronous_commit_(synchronous_commit) {
|
synchronous_commit_(synchronous_commit) {
|
||||||
if (durability_enabled_) {
|
if (durability_enabled_) {
|
||||||
@ -39,8 +40,8 @@ WriteAheadLog::~WriteAheadLog() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
WriteAheadLog::WalFile::WalFile(
|
WriteAheadLog::WalFile::WalFile(
|
||||||
int worker_id, const std::experimental::filesystem::path &durability_dir)
|
const std::experimental::filesystem::path &durability_dir)
|
||||||
: worker_id_(worker_id), wal_dir_{durability_dir / kWalDir} {}
|
: wal_dir_{durability_dir / kWalDir} {}
|
||||||
|
|
||||||
WriteAheadLog::WalFile::~WalFile() {
|
WriteAheadLog::WalFile::~WalFile() {
|
||||||
if (!current_wal_file_.empty()) writer_.Close();
|
if (!current_wal_file_.empty()) writer_.Close();
|
||||||
@ -51,7 +52,7 @@ void WriteAheadLog::WalFile::Init() {
|
|||||||
LOG(ERROR) << "Can't write to WAL directory: " << wal_dir_;
|
LOG(ERROR) << "Can't write to WAL directory: " << wal_dir_;
|
||||||
current_wal_file_ = std::experimental::filesystem::path();
|
current_wal_file_ = std::experimental::filesystem::path();
|
||||||
} else {
|
} else {
|
||||||
current_wal_file_ = WalFilenameForTransactionId(wal_dir_, worker_id_);
|
current_wal_file_ = WalFilenameForTransactionId(wal_dir_);
|
||||||
// TODO: Fix error handling, the encoder_ returns `true` or `false`.
|
// TODO: Fix error handling, the encoder_ returns `true` or `false`.
|
||||||
try {
|
try {
|
||||||
writer_.Open(current_wal_file_);
|
writer_.Open(current_wal_file_);
|
||||||
@ -104,7 +105,7 @@ void WriteAheadLog::WalFile::RotateFile() {
|
|||||||
writer_.Close();
|
writer_.Close();
|
||||||
std::experimental::filesystem::rename(
|
std::experimental::filesystem::rename(
|
||||||
current_wal_file_,
|
current_wal_file_,
|
||||||
WalFilenameForTransactionId(wal_dir_, worker_id_, latest_tx_));
|
WalFilenameForTransactionId(wal_dir_, latest_tx_));
|
||||||
Init();
|
Init();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -138,10 +139,6 @@ bool WriteAheadLog::IsStateDeltaTransactionEnd(
|
|||||||
case database::StateDelta::Type::TRANSACTION_BEGIN:
|
case database::StateDelta::Type::TRANSACTION_BEGIN:
|
||||||
case database::StateDelta::Type::CREATE_VERTEX:
|
case database::StateDelta::Type::CREATE_VERTEX:
|
||||||
case database::StateDelta::Type::CREATE_EDGE:
|
case database::StateDelta::Type::CREATE_EDGE:
|
||||||
case database::StateDelta::Type::ADD_OUT_EDGE:
|
|
||||||
case database::StateDelta::Type::REMOVE_OUT_EDGE:
|
|
||||||
case database::StateDelta::Type::ADD_IN_EDGE:
|
|
||||||
case database::StateDelta::Type::REMOVE_IN_EDGE:
|
|
||||||
case database::StateDelta::Type::SET_PROPERTY_VERTEX:
|
case database::StateDelta::Type::SET_PROPERTY_VERTEX:
|
||||||
case database::StateDelta::Type::SET_PROPERTY_EDGE:
|
case database::StateDelta::Type::SET_PROPERTY_EDGE:
|
||||||
case database::StateDelta::Type::ADD_LABEL:
|
case database::StateDelta::Type::ADD_LABEL:
|
||||||
|
@ -26,8 +26,7 @@ namespace durability {
|
|||||||
/// indeterminism.
|
/// indeterminism.
|
||||||
class WriteAheadLog {
|
class WriteAheadLog {
|
||||||
public:
|
public:
|
||||||
WriteAheadLog(int worker_id,
|
WriteAheadLog(const std::experimental::filesystem::path &durability_dir,
|
||||||
const std::experimental::filesystem::path &durability_dir,
|
|
||||||
bool durability_enabled, bool synchronous_commit);
|
bool durability_enabled, bool synchronous_commit);
|
||||||
~WriteAheadLog();
|
~WriteAheadLog();
|
||||||
|
|
||||||
@ -48,7 +47,7 @@ class WriteAheadLog {
|
|||||||
/// Groups the logic of WAL file handling (flushing, naming, rotating)
|
/// Groups the logic of WAL file handling (flushing, naming, rotating)
|
||||||
class WalFile {
|
class WalFile {
|
||||||
public:
|
public:
|
||||||
WalFile(int worker_id, const std::experimental::filesystem::path &wal__dir);
|
explicit WalFile(const std::experimental::filesystem::path &durability_dir);
|
||||||
~WalFile();
|
~WalFile();
|
||||||
|
|
||||||
/// Initializes the WAL file. Must be called before first flush. Can be
|
/// Initializes the WAL file. Must be called before first flush. Can be
|
||||||
@ -62,7 +61,6 @@ class WriteAheadLog {
|
|||||||
private:
|
private:
|
||||||
/// Mutex used for flushing wal data
|
/// Mutex used for flushing wal data
|
||||||
std::mutex flush_mutex_;
|
std::mutex flush_mutex_;
|
||||||
int worker_id_;
|
|
||||||
const std::experimental::filesystem::path wal_dir_;
|
const std::experimental::filesystem::path wal_dir_;
|
||||||
HashedFileWriter writer_;
|
HashedFileWriter writer_;
|
||||||
communication::bolt::BaseEncoder<HashedFileWriter> encoder_{writer_};
|
communication::bolt::BaseEncoder<HashedFileWriter> encoder_{writer_};
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
#include "storage/single_node/gid.hpp"
|
#include "storage/single_node/gid.hpp"
|
||||||
#include "storage/locking/record_lock.hpp"
|
#include "storage/locking/record_lock.hpp"
|
||||||
#include "transactions/transaction.hpp"
|
#include "transactions/transaction.hpp"
|
||||||
|
#include "utils/cast.hpp"
|
||||||
#include "utils/exceptions.hpp"
|
#include "utils/exceptions.hpp"
|
||||||
|
|
||||||
namespace mvcc {
|
namespace mvcc {
|
||||||
@ -25,14 +26,12 @@ class VersionList {
|
|||||||
* @param t - transaction
|
* @param t - transaction
|
||||||
* @param gid - Version list identifier. Uniqueness guaranteed by the code
|
* @param gid - Version list identifier. Uniqueness guaranteed by the code
|
||||||
* creating this version list.
|
* creating this version list.
|
||||||
* @param cypher_id - Number returned from the id function.
|
|
||||||
* @param args - args forwarded to constructor of item T (for
|
* @param args - args forwarded to constructor of item T (for
|
||||||
* creating the first Record (Version) in this VersionList.
|
* creating the first Record (Version) in this VersionList.
|
||||||
*/
|
*/
|
||||||
template <typename... Args>
|
template <typename... Args>
|
||||||
VersionList(const tx::Transaction &t, gid::Gid gid, int64_t cypher_id,
|
VersionList(const tx::Transaction &t, gid::Gid gid, Args &&... args)
|
||||||
Args &&... args)
|
: gid_(gid) {
|
||||||
: gid_(gid), cypher_id_(cypher_id) {
|
|
||||||
// TODO replace 'new' with something better
|
// TODO replace 'new' with something better
|
||||||
auto *v1 = new T(std::forward<Args>(args)...);
|
auto *v1 = new T(std::forward<Args>(args)...);
|
||||||
v1->mark_created(t);
|
v1->mark_created(t);
|
||||||
@ -226,7 +225,7 @@ class VersionList {
|
|||||||
|
|
||||||
const gid::Gid gid_;
|
const gid::Gid gid_;
|
||||||
|
|
||||||
auto cypher_id() { return cypher_id_; }
|
int64_t cypher_id() { return utils::MemcpyCast<int64_t>(gid_); }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void lock_and_validate(T *record, const tx::Transaction &t) {
|
void lock_and_validate(T *record, const tx::Transaction &t) {
|
||||||
@ -265,18 +264,6 @@ class VersionList {
|
|||||||
return updated;
|
return updated;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* The following member is here because Memgraph supports ID function from
|
|
||||||
* the Cypher query language. If you have plans to change this you have to
|
|
||||||
* consider the following:
|
|
||||||
* * If the id has to be durable. -> Snapshot and WAL have to be updated.
|
|
||||||
* * Impact on query execution. |
|
|
||||||
* * Impact on the communication stack. |-> The id has to be returned
|
|
||||||
* to the client.
|
|
||||||
* * Import tools bacause of the dependencies on the durability stack.
|
|
||||||
* * Implications on the distributed system.
|
|
||||||
*/
|
|
||||||
int64_t cypher_id_{0};
|
|
||||||
std::atomic<T *> head_{nullptr};
|
std::atomic<T *> head_{nullptr};
|
||||||
RecordLock lock_;
|
RecordLock lock_;
|
||||||
};
|
};
|
||||||
|
@ -625,6 +625,7 @@ TypedValue IndexInfo(TypedValue *, int64_t nargs, const EvaluationContext &,
|
|||||||
return std::vector<TypedValue>(info.begin(), info.end());
|
return std::vector<TypedValue>(info.begin(), info.end());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef MG_DISTRIBUTED
|
||||||
TypedValue WorkerId(TypedValue *args, int64_t nargs, const EvaluationContext &,
|
TypedValue WorkerId(TypedValue *args, int64_t nargs, const EvaluationContext &,
|
||||||
database::GraphDbAccessor *) {
|
database::GraphDbAccessor *) {
|
||||||
if (nargs != 1) {
|
if (nargs != 1) {
|
||||||
@ -641,6 +642,7 @@ TypedValue WorkerId(TypedValue *args, int64_t nargs, const EvaluationContext &,
|
|||||||
"'workerId' argument must be a node or an edge.");
|
"'workerId' argument must be a node or an edge.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
TypedValue Id(TypedValue *args, int64_t nargs, const EvaluationContext &,
|
TypedValue Id(TypedValue *args, int64_t nargs, const EvaluationContext &,
|
||||||
database::GraphDbAccessor *dba) {
|
database::GraphDbAccessor *dba) {
|
||||||
@ -919,7 +921,9 @@ NameToFunction(const std::string &function_name) {
|
|||||||
if (function_name == "COUNTER") return Counter;
|
if (function_name == "COUNTER") return Counter;
|
||||||
if (function_name == "COUNTERSET") return CounterSet;
|
if (function_name == "COUNTERSET") return CounterSet;
|
||||||
if (function_name == "INDEXINFO") return IndexInfo;
|
if (function_name == "INDEXINFO") return IndexInfo;
|
||||||
|
#ifdef MG_DISTRIBUTED
|
||||||
if (function_name == "WORKERID") return WorkerId;
|
if (function_name == "WORKERID") return WorkerId;
|
||||||
|
#endif
|
||||||
|
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
@ -176,8 +176,6 @@ bool CreateExpand::CreateExpandCursor::Pull(Frame &frame, Context &context) {
|
|||||||
TypedValue &vertex_value = frame[self_.input_symbol_];
|
TypedValue &vertex_value = frame[self_.input_symbol_];
|
||||||
ExpectType(self_.input_symbol_, vertex_value, TypedValue::Type::Vertex);
|
ExpectType(self_.input_symbol_, vertex_value, TypedValue::Type::Vertex);
|
||||||
auto &v1 = vertex_value.Value<VertexAccessor>();
|
auto &v1 = vertex_value.Value<VertexAccessor>();
|
||||||
CHECK(v1.GlobalAddress().worker_id() == 0 && v1.is_local())
|
|
||||||
<< "Expected CreateExpand only in single node execution";
|
|
||||||
|
|
||||||
// Similarly to CreateNode, newly created edges and nodes should use the
|
// Similarly to CreateNode, newly created edges and nodes should use the
|
||||||
// latest accesors.
|
// latest accesors.
|
||||||
@ -485,8 +483,6 @@ bool Expand::ExpandCursor::Pull(Frame &frame, Context &context) {
|
|||||||
// attempt to get a value from the incoming edges
|
// attempt to get a value from the incoming edges
|
||||||
if (in_edges_ && *in_edges_it_ != in_edges_->end()) {
|
if (in_edges_ && *in_edges_it_ != in_edges_->end()) {
|
||||||
auto edge = *(*in_edges_it_)++;
|
auto edge = *(*in_edges_it_)++;
|
||||||
CHECK(edge.address().is_local() && edge.from().address().is_local())
|
|
||||||
<< "Expected Expand only in single node execution";
|
|
||||||
frame[self_.edge_symbol_] = edge;
|
frame[self_.edge_symbol_] = edge;
|
||||||
pull_node(edge, EdgeAtom::Direction::IN);
|
pull_node(edge, EdgeAtom::Direction::IN);
|
||||||
return true;
|
return true;
|
||||||
@ -495,8 +491,6 @@ bool Expand::ExpandCursor::Pull(Frame &frame, Context &context) {
|
|||||||
// attempt to get a value from the outgoing edges
|
// attempt to get a value from the outgoing edges
|
||||||
if (out_edges_ && *out_edges_it_ != out_edges_->end()) {
|
if (out_edges_ && *out_edges_it_ != out_edges_->end()) {
|
||||||
auto edge = *(*out_edges_it_)++;
|
auto edge = *(*out_edges_it_)++;
|
||||||
CHECK(edge.address().is_local() && edge.to().address().is_local())
|
|
||||||
<< "Expected Expand only in single node execution";
|
|
||||||
// when expanding in EdgeAtom::Direction::BOTH directions
|
// when expanding in EdgeAtom::Direction::BOTH directions
|
||||||
// we should do only one expansion for cycles, and it was
|
// we should do only one expansion for cycles, and it was
|
||||||
// already done in the block above
|
// already done in the block above
|
||||||
|
@ -1,94 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include <cstdint>
|
|
||||||
|
|
||||||
#include <glog/logging.h>
|
|
||||||
|
|
||||||
#include "storage/single_node/gid.hpp"
|
|
||||||
|
|
||||||
namespace storage {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A data structure that tracks a Vertex/Edge location (address) that's either
|
|
||||||
* local or remote. The remote address is a global id alongside the id of the
|
|
||||||
* worker on which it's currently stored, while the local address is simply the
|
|
||||||
* memory address in the current local process. Both types of address are stored
|
|
||||||
* in the same storage space, so an Address always takes as much memory as a
|
|
||||||
* pointer does.
|
|
||||||
*
|
|
||||||
* The memory layout for storage is on x64 architecture is the following:
|
|
||||||
* - the lowest bit stores 0 if address is local and 1 if address is global
|
|
||||||
* - if the address is local all 64 bits store the local memory address
|
|
||||||
* - if the address is global then lowest bit stores 1. the following
|
|
||||||
* kWorkerIdSize bits contain the worker id and the final (upper) 64 - 1 -
|
|
||||||
* kWorkerIdSize bits contain the global id.
|
|
||||||
*
|
|
||||||
* @tparam TRecord - Type of record this address points to. Either Vertex or
|
|
||||||
* Edge.
|
|
||||||
*/
|
|
||||||
template <typename TLocalObj>
|
|
||||||
class Address {
|
|
||||||
static constexpr uint64_t kTypeMaskSize{1};
|
|
||||||
static constexpr uint64_t kTypeMask{(1ULL << kTypeMaskSize) - 1};
|
|
||||||
static constexpr uint64_t kWorkerIdSize{gid::kWorkerIdSize};
|
|
||||||
static constexpr uint64_t kLocal{0};
|
|
||||||
static constexpr uint64_t kRemote{1};
|
|
||||||
|
|
||||||
public:
|
|
||||||
using StorageT = uint64_t;
|
|
||||||
|
|
||||||
Address() {}
|
|
||||||
|
|
||||||
// Constructor for raw address value
|
|
||||||
explicit Address(StorageT storage) : storage_(storage) {}
|
|
||||||
|
|
||||||
// Constructor for local Address.
|
|
||||||
explicit Address(TLocalObj *ptr) {
|
|
||||||
uintptr_t ptr_no_type = reinterpret_cast<uintptr_t>(ptr);
|
|
||||||
DCHECK((ptr_no_type & kTypeMask) == 0) << "Ptr has type_mask bit set";
|
|
||||||
storage_ = ptr_no_type | kLocal;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Constructor for remote Address, takes worker_id which specifies the worker
|
|
||||||
// that is storing that vertex/edge
|
|
||||||
Address(gid::Gid global_id, int worker_id) {
|
|
||||||
CHECK(global_id <
|
|
||||||
(1ULL << (sizeof(StorageT) * 8 - kWorkerIdSize - kTypeMaskSize)))
|
|
||||||
<< "Too large global id";
|
|
||||||
CHECK(worker_id < (1ULL << kWorkerIdSize)) << "Too larger worker id";
|
|
||||||
|
|
||||||
storage_ = kRemote;
|
|
||||||
storage_ |= global_id << (kTypeMaskSize + kWorkerIdSize);
|
|
||||||
storage_ |= worker_id << kTypeMaskSize;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool is_local() const { return (storage_ & kTypeMask) == kLocal; }
|
|
||||||
bool is_remote() const { return (storage_ & kTypeMask) == kRemote; }
|
|
||||||
|
|
||||||
TLocalObj *local() const {
|
|
||||||
DCHECK(is_local()) << "Attempting to get local address from global";
|
|
||||||
return reinterpret_cast<TLocalObj *>(storage_);
|
|
||||||
}
|
|
||||||
|
|
||||||
gid::Gid gid() const {
|
|
||||||
DCHECK(is_remote()) << "Attempting to get global ID from local address";
|
|
||||||
return storage_ >> (kTypeMaskSize + kWorkerIdSize);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns worker id where the object is located
|
|
||||||
int worker_id() const {
|
|
||||||
DCHECK(is_remote()) << "Attempting to get worker ID from local address";
|
|
||||||
return (storage_ >> 1) & ((1ULL << kWorkerIdSize) - 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns raw address value
|
|
||||||
StorageT raw() const { return storage_; }
|
|
||||||
|
|
||||||
bool operator==(const Address<TLocalObj> &other) const {
|
|
||||||
return storage_ == other.storage_;
|
|
||||||
}
|
|
||||||
|
|
||||||
StorageT storage_{0};
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace storage
|
|
@ -1,12 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include "mvcc/single_node/version_list.hpp"
|
|
||||||
#include "storage/single_node/address.hpp"
|
|
||||||
|
|
||||||
class Edge;
|
|
||||||
class Vertex;
|
|
||||||
namespace storage {
|
|
||||||
using VertexAddress = Address<mvcc::VersionList<Vertex>>;
|
|
||||||
using EdgeAddress = Address<mvcc::VersionList<Edge>>;
|
|
||||||
|
|
||||||
} // namespace storage
|
|
@ -4,23 +4,21 @@
|
|||||||
#include "mvcc/single_node/version_list.hpp"
|
#include "mvcc/single_node/version_list.hpp"
|
||||||
#include "storage/common/property_value_store.hpp"
|
#include "storage/common/property_value_store.hpp"
|
||||||
#include "storage/common/types.hpp"
|
#include "storage/common/types.hpp"
|
||||||
#include "storage/single_node/address.hpp"
|
|
||||||
|
|
||||||
class Vertex;
|
class Vertex;
|
||||||
|
|
||||||
class Edge : public mvcc::Record<Edge> {
|
class Edge : public mvcc::Record<Edge> {
|
||||||
using VertexAddress = storage::Address<mvcc::VersionList<Vertex>>;
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
Edge(VertexAddress from, VertexAddress to, storage::EdgeType edge_type)
|
Edge(mvcc::VersionList<Vertex> *from, mvcc::VersionList<Vertex> *to,
|
||||||
|
storage::EdgeType edge_type)
|
||||||
: from_(from), to_(to), edge_type_(edge_type) {}
|
: from_(from), to_(to), edge_type_(edge_type) {}
|
||||||
|
|
||||||
// Returns new Edge with copy of data stored in this Edge, but without
|
// Returns new Edge with copy of data stored in this Edge, but without
|
||||||
// copying superclass' members.
|
// copying superclass' members.
|
||||||
Edge *CloneData() { return new Edge(*this); }
|
Edge *CloneData() { return new Edge(*this); }
|
||||||
|
|
||||||
VertexAddress from_;
|
mvcc::VersionList<Vertex> *from_;
|
||||||
VertexAddress to_;
|
mvcc::VersionList<Vertex> *to_;
|
||||||
storage::EdgeType edge_type_;
|
storage::EdgeType edge_type_;
|
||||||
PropertyValueStore properties_;
|
PropertyValueStore properties_;
|
||||||
|
|
||||||
|
@ -4,7 +4,7 @@
|
|||||||
#include "storage/vertex_accessor.hpp"
|
#include "storage/vertex_accessor.hpp"
|
||||||
#include "utils/algorithm.hpp"
|
#include "utils/algorithm.hpp"
|
||||||
|
|
||||||
EdgeAccessor::EdgeAccessor(EdgeAddress address,
|
EdgeAccessor::EdgeAccessor(mvcc::VersionList<Edge> *address,
|
||||||
database::GraphDbAccessor &db_accessor)
|
database::GraphDbAccessor &db_accessor)
|
||||||
: RecordAccessor(address, db_accessor),
|
: RecordAccessor(address, db_accessor),
|
||||||
from_(nullptr),
|
from_(nullptr),
|
||||||
@ -18,9 +18,10 @@ EdgeAccessor::EdgeAccessor(EdgeAddress address,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
EdgeAccessor::EdgeAccessor(EdgeAddress address,
|
EdgeAccessor::EdgeAccessor(mvcc::VersionList<Edge> *address,
|
||||||
database::GraphDbAccessor &db_accessor,
|
database::GraphDbAccessor &db_accessor,
|
||||||
VertexAddress from, VertexAddress to,
|
mvcc::VersionList<Vertex> *from,
|
||||||
|
mvcc::VersionList<Vertex> *to,
|
||||||
storage::EdgeType edge_type)
|
storage::EdgeType edge_type)
|
||||||
: RecordAccessor(address, db_accessor),
|
: RecordAccessor(address, db_accessor),
|
||||||
from_(from),
|
from_(from),
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "storage/single_node/address_types.hpp"
|
|
||||||
#include "storage/single_node/edge.hpp"
|
#include "storage/single_node/edge.hpp"
|
||||||
#include "storage/single_node/record_accessor.hpp"
|
#include "storage/single_node/record_accessor.hpp"
|
||||||
|
|
||||||
@ -20,20 +19,19 @@ class VertexAccessor;
|
|||||||
* location, which is often a performance bottleneck in traversals.
|
* location, which is often a performance bottleneck in traversals.
|
||||||
*/
|
*/
|
||||||
class EdgeAccessor final : public RecordAccessor<Edge> {
|
class EdgeAccessor final : public RecordAccessor<Edge> {
|
||||||
using EdgeAddress = storage::EdgeAddress;
|
|
||||||
using VertexAddress = storage::VertexAddress;
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
/** Constructor that reads data from the random memory location (lower
|
/** Constructor that reads data from the random memory location (lower
|
||||||
* performance, see class docs). */
|
* performance, see class docs). */
|
||||||
EdgeAccessor(EdgeAddress address, database::GraphDbAccessor &db_accessor);
|
EdgeAccessor(mvcc::VersionList<Edge> *address,
|
||||||
|
database::GraphDbAccessor &db_accessor);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructor that does NOT read data from the random memory location
|
* Constructor that does NOT read data from the random memory location
|
||||||
* (better performance, see class docs).
|
* (better performance, see class docs).
|
||||||
*/
|
*/
|
||||||
EdgeAccessor(EdgeAddress address, database::GraphDbAccessor &db_accessor,
|
EdgeAccessor(mvcc::VersionList<Edge> *address,
|
||||||
VertexAddress from, VertexAddress to,
|
database::GraphDbAccessor &db_accessor,
|
||||||
|
mvcc::VersionList<Vertex> *from, mvcc::VersionList<Vertex> *to,
|
||||||
storage::EdgeType edge_type);
|
storage::EdgeType edge_type);
|
||||||
|
|
||||||
storage::EdgeType EdgeType() const;
|
storage::EdgeType EdgeType() const;
|
||||||
@ -63,8 +61,8 @@ class EdgeAccessor final : public RecordAccessor<Edge> {
|
|||||||
bool is_cycle() const;
|
bool is_cycle() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
VertexAddress from_;
|
mvcc::VersionList<Vertex> *from_;
|
||||||
VertexAddress to_;
|
mvcc::VersionList<Vertex> *to_;
|
||||||
storage::EdgeType edge_type_;
|
storage::EdgeType edge_type_;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -7,8 +7,6 @@
|
|||||||
|
|
||||||
#include "mvcc/single_node/version_list.hpp"
|
#include "mvcc/single_node/version_list.hpp"
|
||||||
#include "storage/common/types.hpp"
|
#include "storage/common/types.hpp"
|
||||||
#include "storage/single_node/address.hpp"
|
|
||||||
#include "storage/single_node/address_types.hpp"
|
|
||||||
#include "utils/algorithm.hpp"
|
#include "utils/algorithm.hpp"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -19,8 +17,8 @@
|
|||||||
class Edges {
|
class Edges {
|
||||||
private:
|
private:
|
||||||
struct Element {
|
struct Element {
|
||||||
storage::VertexAddress vertex;
|
mvcc::VersionList<Vertex> *vertex;
|
||||||
storage::EdgeAddress edge;
|
mvcc::VersionList<Edge> *edge;
|
||||||
storage::EdgeType edge_type;
|
storage::EdgeType edge_type;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -49,7 +47,7 @@ class Edges {
|
|||||||
*/
|
*/
|
||||||
Iterator(std::vector<Element>::const_iterator position,
|
Iterator(std::vector<Element>::const_iterator position,
|
||||||
std::vector<Element>::const_iterator end,
|
std::vector<Element>::const_iterator end,
|
||||||
storage::VertexAddress vertex,
|
mvcc::VersionList<Vertex> *vertex,
|
||||||
const std::vector<storage::EdgeType> *edge_types)
|
const std::vector<storage::EdgeType> *edge_types)
|
||||||
: position_(position),
|
: position_(position),
|
||||||
end_(end),
|
end_(end),
|
||||||
@ -80,14 +78,14 @@ class Edges {
|
|||||||
|
|
||||||
// Optional predicates. If set they define which edges are skipped by the
|
// Optional predicates. If set they define which edges are skipped by the
|
||||||
// iterator. Only one can be not-null in the current implementation.
|
// iterator. Only one can be not-null in the current implementation.
|
||||||
storage::VertexAddress vertex_{nullptr};
|
mvcc::VersionList<Vertex> *vertex_{nullptr};
|
||||||
// For edge types we use a vector pointer because it's optional.
|
// For edge types we use a vector pointer because it's optional.
|
||||||
const std::vector<storage::EdgeType> *edge_types_ = nullptr;
|
const std::vector<storage::EdgeType> *edge_types_ = nullptr;
|
||||||
|
|
||||||
/** Helper function that skips edges that don't satisfy the predicate
|
/** Helper function that skips edges that don't satisfy the predicate
|
||||||
* present in this iterator. */
|
* present in this iterator. */
|
||||||
void update_position() {
|
void update_position() {
|
||||||
if (vertex_.local()) {
|
if (vertex_) {
|
||||||
position_ = std::find_if(position_,
|
position_ = std::find_if(position_,
|
||||||
end_, [v = this->vertex_](const Element &e) {
|
end_, [v = this->vertex_](const Element &e) {
|
||||||
return e.vertex == v;
|
return e.vertex == v;
|
||||||
@ -110,7 +108,7 @@ class Edges {
|
|||||||
* @param edge - The edge.
|
* @param edge - The edge.
|
||||||
* @param edge_type - Type of the edge.
|
* @param edge_type - Type of the edge.
|
||||||
*/
|
*/
|
||||||
void emplace(storage::VertexAddress vertex, storage::EdgeAddress edge,
|
void emplace(mvcc::VersionList<Vertex> *vertex, mvcc::VersionList<Edge> *edge,
|
||||||
storage::EdgeType edge_type) {
|
storage::EdgeType edge_type) {
|
||||||
storage_.emplace_back(Element{vertex, edge, edge_type});
|
storage_.emplace_back(Element{vertex, edge, edge_type});
|
||||||
}
|
}
|
||||||
@ -118,7 +116,7 @@ class Edges {
|
|||||||
/**
|
/**
|
||||||
* Removes an edge from this structure.
|
* Removes an edge from this structure.
|
||||||
*/
|
*/
|
||||||
void RemoveEdge(storage::EdgeAddress edge) {
|
void RemoveEdge(mvcc::VersionList<Edge> *edge) {
|
||||||
auto found = std::find_if(
|
auto found = std::find_if(
|
||||||
storage_.begin(), storage_.end(),
|
storage_.begin(), storage_.end(),
|
||||||
[edge](const Element &element) { return edge == element.edge; });
|
[edge](const Element &element) { return edge == element.edge; });
|
||||||
@ -143,7 +141,7 @@ class Edges {
|
|||||||
* @param edge_types - The edge types at least one of which must be matched.
|
* @param edge_types - The edge types at least one of which must be matched.
|
||||||
* If nullptr edges are not filtered on type.
|
* If nullptr edges are not filtered on type.
|
||||||
*/
|
*/
|
||||||
auto begin(storage::VertexAddress vertex,
|
auto begin(mvcc::VersionList<Vertex> *vertex,
|
||||||
const std::vector<storage::EdgeType> *edge_types) const {
|
const std::vector<storage::EdgeType> *edge_types) const {
|
||||||
if (edge_types && edge_types->empty()) edge_types = nullptr;
|
if (edge_types && edge_types->empty()) edge_types = nullptr;
|
||||||
return Iterator(storage_.begin(), storage_.end(), vertex, edge_types);
|
return Iterator(storage_.begin(), storage_.end(), vertex, edge_types);
|
||||||
|
@ -16,16 +16,6 @@ namespace gid {
|
|||||||
*/
|
*/
|
||||||
using Gid = uint64_t;
|
using Gid = uint64_t;
|
||||||
|
|
||||||
static constexpr std::size_t kWorkerIdSize{10};
|
|
||||||
|
|
||||||
/// Returns `local` id from global id.
|
|
||||||
static inline uint64_t LocalId(Gid gid) { return gid >> kWorkerIdSize; }
|
|
||||||
|
|
||||||
/// Returns id of the worker that created this gid.
|
|
||||||
static inline int CreatorWorker(Gid gid) {
|
|
||||||
return gid & ((1ULL << kWorkerIdSize) - 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Threadsafe generation of new global ids which belong to the
|
* Threadsafe generation of new global ids which belong to the
|
||||||
* worker_id machine. Never call SetId after calling Next without an Id you are
|
* worker_id machine. Never call SetId after calling Next without an Id you are
|
||||||
@ -36,8 +26,6 @@ static inline int CreatorWorker(Gid gid) {
|
|||||||
*/
|
*/
|
||||||
class Generator {
|
class Generator {
|
||||||
public:
|
public:
|
||||||
Generator(int worker_id) : worker_id_(worker_id) {}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns a globally unique identifier.
|
* Returns a globally unique identifier.
|
||||||
*
|
*
|
||||||
@ -47,29 +35,14 @@ class Generator {
|
|||||||
gid::Gid Next(std::experimental::optional<gid::Gid> requested_gid =
|
gid::Gid Next(std::experimental::optional<gid::Gid> requested_gid =
|
||||||
std::experimental::nullopt) {
|
std::experimental::nullopt) {
|
||||||
if (requested_gid) {
|
if (requested_gid) {
|
||||||
if (gid::CreatorWorker(*requested_gid) == worker_id_)
|
utils::EnsureAtomicGe(next_local_id_, *requested_gid + 1);
|
||||||
utils::EnsureAtomicGe(next_local_id_, gid::LocalId(*requested_gid) + 1);
|
|
||||||
return *requested_gid;
|
return *requested_gid;
|
||||||
} else {
|
} else {
|
||||||
generated_id_ = true;
|
return next_local_id_++;
|
||||||
return worker_id_ | next_local_id_++ << kWorkerIdSize;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns number of locally generated ids
|
|
||||||
uint64_t LocalCount() const { return next_local_id_; };
|
|
||||||
|
|
||||||
// Sets a new id from which every new gid will be generated, should only be
|
|
||||||
// set before first Next is called
|
|
||||||
void SetId(uint64_t id) {
|
|
||||||
DCHECK(!generated_id_)
|
|
||||||
<< "Id should be set only before first id is generated";
|
|
||||||
next_local_id_ = id;
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool generated_id_{false};
|
|
||||||
int worker_id_;
|
|
||||||
std::atomic<uint64_t> next_local_id_{0};
|
std::atomic<uint64_t> next_local_id_{0};
|
||||||
};
|
};
|
||||||
} // namespace gid
|
} // namespace gid
|
||||||
|
@ -10,11 +10,9 @@
|
|||||||
using database::StateDelta;
|
using database::StateDelta;
|
||||||
|
|
||||||
template <typename TRecord>
|
template <typename TRecord>
|
||||||
RecordAccessor<TRecord>::RecordAccessor(AddressT address,
|
RecordAccessor<TRecord>::RecordAccessor(mvcc::VersionList<TRecord> *address,
|
||||||
database::GraphDbAccessor &db_accessor)
|
database::GraphDbAccessor &db_accessor)
|
||||||
: db_accessor_(&db_accessor),
|
: db_accessor_(&db_accessor), address_(address) {}
|
||||||
address_(db_accessor.db().storage().LocalizedAddressIfPossible(address)) {
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename TRecord>
|
template <typename TRecord>
|
||||||
PropertyValue RecordAccessor<TRecord>::PropsAt(storage::Property key) const {
|
PropertyValue RecordAccessor<TRecord>::PropsAt(storage::Property key) const {
|
||||||
@ -28,10 +26,8 @@ void RecordAccessor<Vertex>::PropsSet(storage::Property key,
|
|||||||
auto delta = StateDelta::PropsSetVertex(dba.transaction_id(), gid(), key,
|
auto delta = StateDelta::PropsSetVertex(dba.transaction_id(), gid(), key,
|
||||||
dba.PropertyName(key), value);
|
dba.PropertyName(key), value);
|
||||||
update().properties_.set(key, value);
|
update().properties_.set(key, value);
|
||||||
if (is_local()) {
|
dba.UpdatePropertyIndex(key, *this, &update());
|
||||||
dba.UpdatePropertyIndex(key, *this, &update());
|
db_accessor().wal().Emplace(delta);
|
||||||
}
|
|
||||||
ProcessDelta(delta);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template <>
|
template <>
|
||||||
@ -42,7 +38,7 @@ void RecordAccessor<Edge>::PropsSet(storage::Property key,
|
|||||||
dba.PropertyName(key), value);
|
dba.PropertyName(key), value);
|
||||||
|
|
||||||
update().properties_.set(key, value);
|
update().properties_.set(key, value);
|
||||||
ProcessDelta(delta);
|
db_accessor().wal().Emplace(delta);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <>
|
template <>
|
||||||
@ -52,7 +48,7 @@ void RecordAccessor<Vertex>::PropsErase(storage::Property key) {
|
|||||||
StateDelta::PropsSetVertex(dba.transaction_id(), gid(), key,
|
StateDelta::PropsSetVertex(dba.transaction_id(), gid(), key,
|
||||||
dba.PropertyName(key), PropertyValue::Null);
|
dba.PropertyName(key), PropertyValue::Null);
|
||||||
update().properties_.set(key, PropertyValue::Null);
|
update().properties_.set(key, PropertyValue::Null);
|
||||||
ProcessDelta(delta);
|
db_accessor().wal().Emplace(delta);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <>
|
template <>
|
||||||
@ -62,7 +58,7 @@ void RecordAccessor<Edge>::PropsErase(storage::Property key) {
|
|||||||
StateDelta::PropsSetEdge(dba.transaction_id(), gid(), key,
|
StateDelta::PropsSetEdge(dba.transaction_id(), gid(), key,
|
||||||
dba.PropertyName(key), PropertyValue::Null);
|
dba.PropertyName(key), PropertyValue::Null);
|
||||||
update().properties_.set(key, PropertyValue::Null);
|
update().properties_.set(key, PropertyValue::Null);
|
||||||
ProcessDelta(delta);
|
db_accessor().wal().Emplace(delta);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename TRecord>
|
template <typename TRecord>
|
||||||
@ -93,39 +89,24 @@ database::GraphDbAccessor &RecordAccessor<TRecord>::db_accessor() const {
|
|||||||
|
|
||||||
template <typename TRecord>
|
template <typename TRecord>
|
||||||
gid::Gid RecordAccessor<TRecord>::gid() const {
|
gid::Gid RecordAccessor<TRecord>::gid() const {
|
||||||
return is_local() ? address_.local()->gid_ : address_.gid();
|
return address_->gid_;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename TRecord>
|
template <typename TRecord>
|
||||||
typename RecordAccessor<TRecord>::AddressT RecordAccessor<TRecord>::address()
|
typename mvcc::VersionList<TRecord> *RecordAccessor<TRecord>::address() const {
|
||||||
const {
|
|
||||||
return address_;
|
return address_;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename TRecord>
|
|
||||||
typename RecordAccessor<TRecord>::AddressT
|
|
||||||
RecordAccessor<TRecord>::GlobalAddress() const {
|
|
||||||
// TODO: This is still coupled to distributed storage, albeit loosely.
|
|
||||||
int worker_id = 0;
|
|
||||||
CHECK(is_local());
|
|
||||||
return storage::Address<mvcc::VersionList<TRecord>>(gid(), worker_id);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename TRecord>
|
template <typename TRecord>
|
||||||
RecordAccessor<TRecord> &RecordAccessor<TRecord>::SwitchNew() {
|
RecordAccessor<TRecord> &RecordAccessor<TRecord>::SwitchNew() {
|
||||||
if (is_local()) {
|
if (!new_) {
|
||||||
if (!new_) {
|
// if new_ is not set yet, look for it
|
||||||
// if new_ is not set yet, look for it
|
// we can just Reconstruct the pointers, old_ will get initialized
|
||||||
// we can just Reconstruct the pointers, old_ will get initialized
|
// to the same value as it has now, and the amount of work is the
|
||||||
// to the same value as it has now, and the amount of work is the
|
// same as just looking for a new_ record
|
||||||
// same as just looking for a new_ record
|
if (!Reconstruct())
|
||||||
if (!Reconstruct())
|
DLOG(FATAL)
|
||||||
DLOG(FATAL)
|
<< "RecordAccessor::SwitchNew - accessor invalid after Reconstruct";
|
||||||
<< "RecordAccessor::SwitchNew - accessor invalid after Reconstruct";
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// A remote record only sees local updates, until the command is advanced.
|
|
||||||
// So this does nothing, as the old/new switch happens below.
|
|
||||||
}
|
}
|
||||||
current_ = new_ ? new_ : old_;
|
current_ = new_ ? new_ : old_;
|
||||||
return *this;
|
return *this;
|
||||||
@ -141,8 +122,7 @@ template <typename TRecord>
|
|||||||
bool RecordAccessor<TRecord>::Reconstruct() const {
|
bool RecordAccessor<TRecord>::Reconstruct() const {
|
||||||
auto &dba = db_accessor();
|
auto &dba = db_accessor();
|
||||||
const auto &addr = address();
|
const auto &addr = address();
|
||||||
CHECK(is_local());
|
addr->find_set_old_new(dba.transaction(), &old_, &new_);
|
||||||
addr.local()->find_set_old_new(dba.transaction(), &old_, &new_);
|
|
||||||
current_ = old_ ? old_ : new_;
|
current_ = old_ ? old_ : new_;
|
||||||
return old_ != nullptr || new_ != nullptr;
|
return old_ != nullptr || new_ != nullptr;
|
||||||
}
|
}
|
||||||
@ -165,8 +145,7 @@ TRecord &RecordAccessor<TRecord>::update() const {
|
|||||||
if (new_) return *new_;
|
if (new_) return *new_;
|
||||||
|
|
||||||
const auto &addr = address();
|
const auto &addr = address();
|
||||||
CHECK(addr.is_local());
|
new_ = addr->update(dba.transaction());
|
||||||
new_ = addr.local()->update(dba.transaction());
|
|
||||||
|
|
||||||
DCHECK(new_ != nullptr) << "RecordAccessor.new_ is null after update";
|
DCHECK(new_ != nullptr) << "RecordAccessor.new_ is null after update";
|
||||||
return *new_;
|
return *new_;
|
||||||
@ -174,7 +153,7 @@ TRecord &RecordAccessor<TRecord>::update() const {
|
|||||||
|
|
||||||
template <typename TRecord>
|
template <typename TRecord>
|
||||||
int64_t RecordAccessor<TRecord>::CypherId() const {
|
int64_t RecordAccessor<TRecord>::CypherId() const {
|
||||||
return address().local()->cypher_id();
|
return address()->cypher_id();
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename TRecord>
|
template <typename TRecord>
|
||||||
@ -188,12 +167,5 @@ const TRecord &RecordAccessor<TRecord>::current() const {
|
|||||||
return *current_;
|
return *current_;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename TRecord>
|
|
||||||
void RecordAccessor<TRecord>::ProcessDelta(
|
|
||||||
const database::StateDelta &delta) const {
|
|
||||||
CHECK(is_local());
|
|
||||||
db_accessor().wal().Emplace(delta);
|
|
||||||
}
|
|
||||||
|
|
||||||
template class RecordAccessor<Vertex>;
|
template class RecordAccessor<Vertex>;
|
||||||
template class RecordAccessor<Edge>;
|
template class RecordAccessor<Edge>;
|
||||||
|
@ -7,7 +7,6 @@
|
|||||||
#include "storage/common/property_value.hpp"
|
#include "storage/common/property_value.hpp"
|
||||||
#include "storage/common/property_value_store.hpp"
|
#include "storage/common/property_value_store.hpp"
|
||||||
#include "storage/common/types.hpp"
|
#include "storage/common/types.hpp"
|
||||||
#include "storage/single_node/address.hpp"
|
|
||||||
#include "storage/single_node/gid.hpp"
|
#include "storage/single_node/gid.hpp"
|
||||||
#include "utils/total_ordering.hpp"
|
#include "utils/total_ordering.hpp"
|
||||||
|
|
||||||
@ -28,8 +27,6 @@ struct StateDelta;
|
|||||||
template <typename TRecord>
|
template <typename TRecord>
|
||||||
class RecordAccessor : public utils::TotalOrdering<RecordAccessor<TRecord>> {
|
class RecordAccessor : public utils::TotalOrdering<RecordAccessor<TRecord>> {
|
||||||
protected:
|
protected:
|
||||||
using AddressT = storage::Address<mvcc::VersionList<TRecord>>;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The database::GraphDbAccessor is friend to this accessor so it can
|
* The database::GraphDbAccessor is friend to this accessor so it can
|
||||||
* operate on it's data (mvcc version-list and the record itself).
|
* operate on it's data (mvcc version-list and the record itself).
|
||||||
@ -47,7 +44,7 @@ class RecordAccessor : public utils::TotalOrdering<RecordAccessor<TRecord>> {
|
|||||||
* accessor.
|
* accessor.
|
||||||
* @param db_accessor The DB accessor that "owns" this record accessor.
|
* @param db_accessor The DB accessor that "owns" this record accessor.
|
||||||
*/
|
*/
|
||||||
RecordAccessor(AddressT address, database::GraphDbAccessor &db_accessor);
|
RecordAccessor(mvcc::VersionList<TRecord> *address, database::GraphDbAccessor &db_accessor);
|
||||||
|
|
||||||
// this class is default copyable, movable and assignable
|
// this class is default copyable, movable and assignable
|
||||||
RecordAccessor(const RecordAccessor &other) = default;
|
RecordAccessor(const RecordAccessor &other) = default;
|
||||||
@ -82,9 +79,7 @@ class RecordAccessor : public utils::TotalOrdering<RecordAccessor<TRecord>> {
|
|||||||
*/
|
*/
|
||||||
gid::Gid gid() const;
|
gid::Gid gid() const;
|
||||||
|
|
||||||
AddressT address() const;
|
mvcc::VersionList<TRecord> *address() const;
|
||||||
|
|
||||||
AddressT GlobalAddress() const;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Switches this record accessor to use the latest version visible to the
|
* Switches this record accessor to use the latest version visible to the
|
||||||
@ -146,20 +141,12 @@ class RecordAccessor : public utils::TotalOrdering<RecordAccessor<TRecord>> {
|
|||||||
(current_state && new_ && !new_->is_expired_by(t));
|
(current_state && new_ && !new_->is_expired_by(t));
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: This shouldn't be here, because it's only relevant in distributed.
|
|
||||||
/** Indicates if this accessor represents a local Vertex/Edge, or one whose
|
|
||||||
* owner is some other worker in a distributed system. */
|
|
||||||
bool is_local() const { return address_.is_local(); }
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns Cypher Id of this record.
|
* Returns Cypher Id of this record.
|
||||||
*/
|
*/
|
||||||
int64_t CypherId() const;
|
int64_t CypherId() const;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
/** Process a change delta, e.g. by writing WAL. */
|
|
||||||
void ProcessDelta(const database::StateDelta &delta) const;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Pointer to the version (either old_ or new_) that READ operations
|
* Pointer to the version (either old_ or new_) that READ operations
|
||||||
* in the accessor should take data from. Note that WRITE operations
|
* in the accessor should take data from. Note that WRITE operations
|
||||||
@ -180,7 +167,7 @@ class RecordAccessor : public utils::TotalOrdering<RecordAccessor<TRecord>> {
|
|||||||
// Immutable, set in the constructor and never changed.
|
// Immutable, set in the constructor and never changed.
|
||||||
database::GraphDbAccessor *db_accessor_;
|
database::GraphDbAccessor *db_accessor_;
|
||||||
|
|
||||||
AddressT address_;
|
mvcc::VersionList<TRecord> *address_;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Latest version which is visible to the current transaction+command
|
* Latest version which is visible to the current transaction+command
|
||||||
|
@ -8,17 +8,12 @@
|
|||||||
#include "mvcc/single_node/version_list.hpp"
|
#include "mvcc/single_node/version_list.hpp"
|
||||||
#include "storage/common/types.hpp"
|
#include "storage/common/types.hpp"
|
||||||
#include "storage/kvstore/kvstore.hpp"
|
#include "storage/kvstore/kvstore.hpp"
|
||||||
#include "storage/single_node/address.hpp"
|
|
||||||
#include "storage/single_node/edge.hpp"
|
#include "storage/single_node/edge.hpp"
|
||||||
#include "storage/single_node/indexes/key_index.hpp"
|
#include "storage/single_node/indexes/key_index.hpp"
|
||||||
#include "storage/single_node/indexes/label_property_index.hpp"
|
#include "storage/single_node/indexes/label_property_index.hpp"
|
||||||
#include "storage/single_node/vertex.hpp"
|
#include "storage/single_node/vertex.hpp"
|
||||||
#include "transactions/type.hpp"
|
#include "transactions/type.hpp"
|
||||||
|
|
||||||
namespace distributed {
|
|
||||||
class IndexRpcServer;
|
|
||||||
};
|
|
||||||
|
|
||||||
namespace database {
|
namespace database {
|
||||||
class GraphDb;
|
class GraphDb;
|
||||||
};
|
};
|
||||||
@ -28,11 +23,8 @@ namespace database {
|
|||||||
/** A data structure containing the main data members of a graph database. */
|
/** A data structure containing the main data members of a graph database. */
|
||||||
class Storage {
|
class Storage {
|
||||||
public:
|
public:
|
||||||
Storage(int worker_id, const std::vector<std::string> &properties_on_disk)
|
explicit Storage(const std::vector<std::string> &properties_on_disk)
|
||||||
: worker_id_(worker_id),
|
: properties_on_disk_{properties_on_disk} {}
|
||||||
vertex_generator_{worker_id},
|
|
||||||
edge_generator_{worker_id},
|
|
||||||
properties_on_disk_{properties_on_disk} {}
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
~Storage() {
|
~Storage() {
|
||||||
@ -64,41 +56,13 @@ class Storage {
|
|||||||
return found->second;
|
return found->second;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Converts an address to local, if possible. Returns the same address if
|
|
||||||
/// not.
|
|
||||||
template <typename TRecord>
|
|
||||||
storage::Address<mvcc::VersionList<TRecord>> LocalizedAddressIfPossible(
|
|
||||||
storage::Address<mvcc::VersionList<TRecord>> address) const {
|
|
||||||
if (address.is_local()) return address;
|
|
||||||
if (address.worker_id() == worker_id_) {
|
|
||||||
auto vlist = LocalAddress<TRecord>(address.gid());
|
|
||||||
if constexpr (std::is_same<TRecord, Vertex>::value)
|
|
||||||
return storage::VertexAddress(vlist);
|
|
||||||
else
|
|
||||||
return storage::EdgeAddress(vlist);
|
|
||||||
}
|
|
||||||
return address;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns remote address for the given local or remote address.
|
|
||||||
template <typename TAddress>
|
|
||||||
TAddress GlobalizedAddress(TAddress address) const {
|
|
||||||
if (address.is_remote()) return address;
|
|
||||||
return {address.local()->gid_, worker_id_};
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Gets the local edge address for the given gid. Fails if not present.
|
|
||||||
mvcc::VersionList<Edge> *LocalEdgeAddress(gid::Gid gid) const;
|
|
||||||
|
|
||||||
/// Gets names of properties stored on disk
|
/// Gets names of properties stored on disk
|
||||||
std::vector<std::string> &PropertiesOnDisk() { return properties_on_disk_; }
|
std::vector<std::string> &PropertiesOnDisk() { return properties_on_disk_; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
friend class GraphDbAccessor;
|
friend class GraphDbAccessor;
|
||||||
friend class StorageGc;
|
friend class StorageGc;
|
||||||
friend class distributed::IndexRpcServer;
|
|
||||||
|
|
||||||
int worker_id_;
|
|
||||||
gid::Generator vertex_generator_;
|
gid::Generator vertex_generator_;
|
||||||
gid::Generator edge_generator_;
|
gid::Generator edge_generator_;
|
||||||
|
|
||||||
|
@ -4,7 +4,6 @@
|
|||||||
#include "mvcc/single_node/version_list.hpp"
|
#include "mvcc/single_node/version_list.hpp"
|
||||||
#include "storage/common/property_value_store.hpp"
|
#include "storage/common/property_value_store.hpp"
|
||||||
#include "storage/common/types.hpp"
|
#include "storage/common/types.hpp"
|
||||||
#include "storage/single_node/address.hpp"
|
|
||||||
#include "storage/single_node/edges.hpp"
|
#include "storage/single_node/edges.hpp"
|
||||||
|
|
||||||
class Vertex : public mvcc::Record<Vertex> {
|
class Vertex : public mvcc::Record<Vertex> {
|
||||||
|
@ -6,7 +6,7 @@
|
|||||||
#include "durability/single_node/state_delta.hpp"
|
#include "durability/single_node/state_delta.hpp"
|
||||||
#include "utils/algorithm.hpp"
|
#include "utils/algorithm.hpp"
|
||||||
|
|
||||||
VertexAccessor::VertexAccessor(VertexAddress address,
|
VertexAccessor::VertexAccessor(mvcc::VersionList<Vertex> *address,
|
||||||
database::GraphDbAccessor &db_accessor)
|
database::GraphDbAccessor &db_accessor)
|
||||||
: RecordAccessor(address, db_accessor) {
|
: RecordAccessor(address, db_accessor) {
|
||||||
Reconstruct();
|
Reconstruct();
|
||||||
@ -17,7 +17,6 @@ size_t VertexAccessor::out_degree() const { return current().out_.size(); }
|
|||||||
size_t VertexAccessor::in_degree() const { return current().in_.size(); }
|
size_t VertexAccessor::in_degree() const { return current().in_.size(); }
|
||||||
|
|
||||||
void VertexAccessor::add_label(storage::Label label) {
|
void VertexAccessor::add_label(storage::Label label) {
|
||||||
CHECK(is_local());
|
|
||||||
auto &dba = db_accessor();
|
auto &dba = db_accessor();
|
||||||
auto delta = database::StateDelta::AddLabel(dba.transaction_id(), gid(),
|
auto delta = database::StateDelta::AddLabel(dba.transaction_id(), gid(),
|
||||||
label, dba.LabelName(label));
|
label, dba.LabelName(label));
|
||||||
@ -31,7 +30,6 @@ void VertexAccessor::add_label(storage::Label label) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void VertexAccessor::remove_label(storage::Label label) {
|
void VertexAccessor::remove_label(storage::Label label) {
|
||||||
CHECK(is_local());
|
|
||||||
auto &dba = db_accessor();
|
auto &dba = db_accessor();
|
||||||
auto delta = database::StateDelta::RemoveLabel(dba.transaction_id(), gid(),
|
auto delta = database::StateDelta::RemoveLabel(dba.transaction_id(), gid(),
|
||||||
label, dba.LabelName(label));
|
label, dba.LabelName(label));
|
||||||
@ -54,28 +52,22 @@ const std::vector<storage::Label> &VertexAccessor::labels() const {
|
|||||||
return this->current().labels_;
|
return this->current().labels_;
|
||||||
}
|
}
|
||||||
|
|
||||||
void VertexAccessor::RemoveOutEdge(storage::EdgeAddress edge) {
|
void VertexAccessor::RemoveOutEdge(mvcc::VersionList<Edge> *edge) {
|
||||||
auto &dba = db_accessor();
|
auto &dba = db_accessor();
|
||||||
auto delta = database::StateDelta::RemoveOutEdge(
|
|
||||||
dba.transaction_id(), gid(), dba.db().storage().GlobalizedAddress(edge));
|
|
||||||
|
|
||||||
SwitchNew();
|
SwitchNew();
|
||||||
if (current().is_expired_by(dba.transaction())) return;
|
if (current().is_expired_by(dba.transaction())) return;
|
||||||
|
|
||||||
update().out_.RemoveEdge(dba.db().storage().LocalizedAddressIfPossible(edge));
|
update().out_.RemoveEdge(edge);
|
||||||
ProcessDelta(delta);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void VertexAccessor::RemoveInEdge(storage::EdgeAddress edge) {
|
void VertexAccessor::RemoveInEdge(mvcc::VersionList<Edge> *edge) {
|
||||||
auto &dba = db_accessor();
|
auto &dba = db_accessor();
|
||||||
auto delta = database::StateDelta::RemoveInEdge(
|
|
||||||
dba.transaction_id(), gid(), dba.db().storage().GlobalizedAddress(edge));
|
|
||||||
|
|
||||||
SwitchNew();
|
SwitchNew();
|
||||||
if (current().is_expired_by(dba.transaction())) return;
|
if (current().is_expired_by(dba.transaction())) return;
|
||||||
|
|
||||||
update().in_.RemoveEdge(dba.db().storage().LocalizedAddressIfPossible(edge));
|
update().in_.RemoveEdge(edge);
|
||||||
ProcessDelta(delta);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::ostream &operator<<(std::ostream &os, const VertexAccessor &va) {
|
std::ostream &operator<<(std::ostream &os, const VertexAccessor &va) {
|
||||||
|
@ -20,7 +20,6 @@
|
|||||||
* takes care of MVCC versioning.
|
* takes care of MVCC versioning.
|
||||||
*/
|
*/
|
||||||
class VertexAccessor final : public RecordAccessor<Vertex> {
|
class VertexAccessor final : public RecordAccessor<Vertex> {
|
||||||
using VertexAddress = storage::Address<mvcc::VersionList<Vertex>>;
|
|
||||||
// Helper function for creating an iterator over edges.
|
// Helper function for creating an iterator over edges.
|
||||||
// @param begin - begin iterator
|
// @param begin - begin iterator
|
||||||
// @param end - end iterator
|
// @param end - end iterator
|
||||||
@ -31,7 +30,8 @@ class VertexAccessor final : public RecordAccessor<Vertex> {
|
|||||||
// @return - Iterator over EdgeAccessors
|
// @return - Iterator over EdgeAccessors
|
||||||
template <typename TIterator>
|
template <typename TIterator>
|
||||||
static inline auto MakeAccessorIterator(
|
static inline auto MakeAccessorIterator(
|
||||||
TIterator &&begin, TIterator &&end, bool from, VertexAddress vertex,
|
TIterator &&begin, TIterator &&end, bool from,
|
||||||
|
mvcc::VersionList<Vertex> *vertex,
|
||||||
database::GraphDbAccessor &db_accessor) {
|
database::GraphDbAccessor &db_accessor) {
|
||||||
return iter::imap(
|
return iter::imap(
|
||||||
[from, vertex, &db_accessor](auto &edges_element) {
|
[from, vertex, &db_accessor](auto &edges_element) {
|
||||||
@ -49,7 +49,8 @@ class VertexAccessor final : public RecordAccessor<Vertex> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
VertexAccessor(VertexAddress address, database::GraphDbAccessor &db_accessor);
|
VertexAccessor(mvcc::VersionList<Vertex> *address,
|
||||||
|
database::GraphDbAccessor &db_accessor);
|
||||||
|
|
||||||
/** Returns the number of outgoing edges. */
|
/** Returns the number of outgoing edges. */
|
||||||
size_t out_degree() const;
|
size_t out_degree() const;
|
||||||
@ -97,9 +98,9 @@ class VertexAccessor final : public RecordAccessor<Vertex> {
|
|||||||
* or empty, the parameter is ignored.
|
* or empty, the parameter is ignored.
|
||||||
*/
|
*/
|
||||||
auto in(const std::vector<storage::EdgeType> *edge_types) const {
|
auto in(const std::vector<storage::EdgeType> *edge_types) const {
|
||||||
return MakeAccessorIterator(
|
return MakeAccessorIterator(current().in_.begin(nullptr, edge_types),
|
||||||
current().in_.begin(storage::VertexAddress(nullptr), edge_types),
|
current().in_.end(), false, address(),
|
||||||
current().in_.end(), false, address(), db_accessor());
|
db_accessor());
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Returns EdgeAccessors for all outgoing edges. */
|
/** Returns EdgeAccessors for all outgoing edges. */
|
||||||
@ -130,20 +131,20 @@ class VertexAccessor final : public RecordAccessor<Vertex> {
|
|||||||
* or empty, the parameter is ignored.
|
* or empty, the parameter is ignored.
|
||||||
*/
|
*/
|
||||||
auto out(const std::vector<storage::EdgeType> *edge_types) const {
|
auto out(const std::vector<storage::EdgeType> *edge_types) const {
|
||||||
return MakeAccessorIterator(
|
return MakeAccessorIterator(current().out_.begin(nullptr, edge_types),
|
||||||
current().out_.begin(storage::VertexAddress(nullptr), edge_types),
|
current().out_.end(), true, address(),
|
||||||
current().out_.end(), true, address(), db_accessor());
|
db_accessor());
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Removes the given edge from the outgoing edges of this vertex. Note that
|
/** Removes the given edge from the outgoing edges of this vertex. Note that
|
||||||
* this operation should always be accompanied by the removal of the edge from
|
* this operation should always be accompanied by the removal of the edge from
|
||||||
* the incoming edges on the other side and edge deletion. */
|
* the incoming edges on the other side and edge deletion. */
|
||||||
void RemoveOutEdge(storage::EdgeAddress edge);
|
void RemoveOutEdge(mvcc::VersionList<Edge> *edge);
|
||||||
|
|
||||||
/** Removes the given edge from the incoming edges of this vertex. Note that
|
/** Removes the given edge from the incoming edges of this vertex. Note that
|
||||||
* this operation should always be accompanied by the removal of the edge from
|
* this operation should always be accompanied by the removal of the edge from
|
||||||
* the outgoing edges on the other side and edge deletion. */
|
* the outgoing edges on the other side and edge deletion. */
|
||||||
void RemoveInEdge(storage::EdgeAddress edge);
|
void RemoveInEdge(mvcc::VersionList<Edge> *edge);
|
||||||
};
|
};
|
||||||
|
|
||||||
std::ostream &operator<<(std::ostream &, const VertexAccessor &);
|
std::ostream &operator<<(std::ostream &, const VertexAccessor &);
|
||||||
|
@ -21,7 +21,7 @@ void MvccMix(benchmark::State &state) {
|
|||||||
state.PauseTiming();
|
state.PauseTiming();
|
||||||
tx::Engine engine;
|
tx::Engine engine;
|
||||||
auto t1 = engine.Begin();
|
auto t1 = engine.Begin();
|
||||||
mvcc::VersionList<Prop> version_list(*t1, 0, 0);
|
mvcc::VersionList<Prop> version_list(*t1, 0);
|
||||||
|
|
||||||
engine.Commit(*t1);
|
engine.Commit(*t1);
|
||||||
auto t2 = engine.Begin();
|
auto t2 = engine.Begin();
|
||||||
|
@ -12,7 +12,7 @@
|
|||||||
#include "communication/bolt/v1/encoder/base_encoder.hpp"
|
#include "communication/bolt/v1/encoder/base_encoder.hpp"
|
||||||
#include "durability/distributed/snapshot_encoder.hpp"
|
#include "durability/distributed/snapshot_encoder.hpp"
|
||||||
#include "durability/distributed/version.hpp"
|
#include "durability/distributed/version.hpp"
|
||||||
#include "durability/paths.hpp"
|
#include "durability/distributed/paths.hpp"
|
||||||
#include "storage/distributed/address_types.hpp"
|
#include "storage/distributed/address_types.hpp"
|
||||||
#include "utils/string.hpp"
|
#include "utils/string.hpp"
|
||||||
#include "utils/timer.hpp"
|
#include "utils/timer.hpp"
|
||||||
|
@ -4,10 +4,9 @@
|
|||||||
#include <gflags/gflags.h>
|
#include <gflags/gflags.h>
|
||||||
#include <glog/logging.h>
|
#include <glog/logging.h>
|
||||||
|
|
||||||
|
#include "communication/bolt/v1/decoder/decoder.hpp"
|
||||||
#include "durability/hashed_file_reader.hpp"
|
#include "durability/hashed_file_reader.hpp"
|
||||||
#include "durability/single_node/recovery.hpp"
|
#include "durability/single_node/recovery.hpp"
|
||||||
#include "durability/single_node/snapshot_decoder.hpp"
|
|
||||||
#include "durability/single_node/snapshot_value.hpp"
|
|
||||||
#include "durability/single_node/version.hpp"
|
#include "durability/single_node/version.hpp"
|
||||||
|
|
||||||
DEFINE_string(snapshot_file, "", "Snapshot file location");
|
DEFINE_string(snapshot_file, "", "Snapshot file location");
|
||||||
@ -19,16 +18,16 @@ int main(int argc, char *argv[]) {
|
|||||||
gflags::ParseCommandLineFlags(&argc, &argv, true);
|
gflags::ParseCommandLineFlags(&argc, &argv, true);
|
||||||
google::InitGoogleLogging(argv[0]);
|
google::InitGoogleLogging(argv[0]);
|
||||||
|
|
||||||
// At the time this was written, the version was 6. This makes sure we update
|
// At the time this was written, the version was 7. This makes sure we update
|
||||||
// the explorer when we bump the snapshot version.
|
// the explorer when we bump the snapshot version.
|
||||||
static_assert(durability::kVersion == 6,
|
static_assert(durability::kVersion == 7,
|
||||||
"Wrong snapshot version, please update!");
|
"Wrong snapshot version, please update!");
|
||||||
|
|
||||||
fs::path snapshot_path(FLAGS_snapshot_file);
|
fs::path snapshot_path(FLAGS_snapshot_file);
|
||||||
CHECK(fs::exists(snapshot_path)) << "File doesn't exist!";
|
CHECK(fs::exists(snapshot_path)) << "File doesn't exist!";
|
||||||
|
|
||||||
HashedFileReader reader;
|
HashedFileReader reader;
|
||||||
durability::SnapshotDecoder<HashedFileReader> decoder(reader);
|
communication::bolt::Decoder<HashedFileReader> decoder(reader);
|
||||||
|
|
||||||
CHECK(reader.Open(snapshot_path)) << "Couldn't open snapshot file!";
|
CHECK(reader.Open(snapshot_path)) << "Couldn't open snapshot file!";
|
||||||
|
|
||||||
@ -53,9 +52,6 @@ int main(int argc, char *argv[]) {
|
|||||||
<< "Snapshot version mismatch"
|
<< "Snapshot version mismatch"
|
||||||
<< ", got " << dv.ValueInt() << " expected " << durability::kVersion;
|
<< ", got " << dv.ValueInt() << " expected " << durability::kVersion;
|
||||||
|
|
||||||
decoder.ReadValue(&dv, Value::Type::Int);
|
|
||||||
LOG(INFO) << "Snapshot was generated for worker id: " << dv.ValueInt();
|
|
||||||
|
|
||||||
decoder.ReadValue(&dv, Value::Type::Int);
|
decoder.ReadValue(&dv, Value::Type::Int);
|
||||||
LOG(INFO) << "Vertex generator last id: " << dv.ValueInt();
|
LOG(INFO) << "Vertex generator last id: " << dv.ValueInt();
|
||||||
|
|
||||||
@ -87,7 +83,7 @@ int main(int argc, char *argv[]) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for (int64_t i = 0; i < vertex_count; ++i) {
|
for (int64_t i = 0; i < vertex_count; ++i) {
|
||||||
auto vertex = decoder.ReadSnapshotVertex();
|
auto vertex = decoder.ReadValue(&dv, Value::Type::Vertex);
|
||||||
CHECK(vertex) << "Failed to read vertex " << i;
|
CHECK(vertex) << "Failed to read vertex " << i;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4,9 +4,9 @@
|
|||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include "communication/bolt/v1/encoder/base_encoder.hpp"
|
#include "communication/bolt/v1/encoder/base_encoder.hpp"
|
||||||
|
#include "durability/distributed/paths.hpp"
|
||||||
#include "durability/distributed/version.hpp"
|
#include "durability/distributed/version.hpp"
|
||||||
#include "durability/hashed_file_writer.hpp"
|
#include "durability/hashed_file_writer.hpp"
|
||||||
#include "durability/paths.hpp"
|
|
||||||
#include "glue/communication.hpp"
|
#include "glue/communication.hpp"
|
||||||
#include "query/typed_value.hpp"
|
#include "query/typed_value.hpp"
|
||||||
#include "utils/file.hpp"
|
#include "utils/file.hpp"
|
||||||
|
@ -29,14 +29,6 @@ std::string StateDeltaTypeToString(database::StateDelta::Type type) {
|
|||||||
return "CREATE_VERTEX";
|
return "CREATE_VERTEX";
|
||||||
case database::StateDelta::Type::CREATE_EDGE:
|
case database::StateDelta::Type::CREATE_EDGE:
|
||||||
return "CREATE_EDGE";
|
return "CREATE_EDGE";
|
||||||
case database::StateDelta::Type::ADD_OUT_EDGE:
|
|
||||||
return "ADD_OUT_EDGE";
|
|
||||||
case database::StateDelta::Type::REMOVE_OUT_EDGE:
|
|
||||||
return "REMOVE_OUT_EDGE";
|
|
||||||
case database::StateDelta::Type::ADD_IN_EDGE:
|
|
||||||
return "ADD_IN_EDGE";
|
|
||||||
case database::StateDelta::Type::REMOVE_IN_EDGE:
|
|
||||||
return "REMOVE_IN_EDGE";
|
|
||||||
case database::StateDelta::Type::SET_PROPERTY_VERTEX:
|
case database::StateDelta::Type::SET_PROPERTY_VERTEX:
|
||||||
return "SET_PROPERTY_VERTEX";
|
return "SET_PROPERTY_VERTEX";
|
||||||
case database::StateDelta::Type::SET_PROPERTY_EDGE:
|
case database::StateDelta::Type::SET_PROPERTY_EDGE:
|
||||||
|
@ -809,7 +809,7 @@ Feature: Functions
|
|||||||
MATCH (n) WITH n ORDER BY id(n)
|
MATCH (n) WITH n ORDER BY id(n)
|
||||||
WITH COLLECT(id(n)) AS node_ids
|
WITH COLLECT(id(n)) AS node_ids
|
||||||
UNWIND node_ids AS node_id
|
UNWIND node_ids AS node_id
|
||||||
RETURN (node_id - node_ids[0]) / 1024 AS id;
|
RETURN node_id - node_ids[0] AS id;
|
||||||
"""
|
"""
|
||||||
Then the result should be:
|
Then the result should be:
|
||||||
| id |
|
| id |
|
||||||
@ -828,7 +828,7 @@ Feature: Functions
|
|||||||
MATCH ()-[e]->() WITH e ORDER BY id(e)
|
MATCH ()-[e]->() WITH e ORDER BY id(e)
|
||||||
WITH COLLECT(id(e)) AS edge_ids
|
WITH COLLECT(id(e)) AS edge_ids
|
||||||
UNWIND edge_ids AS edge_id
|
UNWIND edge_ids AS edge_id
|
||||||
RETURN (edge_id - edge_ids[0]) / 1024 AS id;
|
RETURN edge_id - edge_ids[0] AS id;
|
||||||
"""
|
"""
|
||||||
Then the result should be:
|
Then the result should be:
|
||||||
| id |
|
| id |
|
||||||
|
@ -24,6 +24,15 @@ void PrintTo(const query::EdgeAtom::Direction &dir, std::ostream *os) {
|
|||||||
}
|
}
|
||||||
} // namespace query
|
} // namespace query
|
||||||
|
|
||||||
|
#ifdef MG_SINGLE_NODE
|
||||||
|
using VertexAddress = mvcc::VersionList<Vertex> *;
|
||||||
|
using EdgeAddress = mvcc::VersionList<Edge> *;
|
||||||
|
#endif
|
||||||
|
#ifdef MG_DISTRIBUTED
|
||||||
|
using VertexAddress = storage::Address<mvcc::VersionList<Vertex>>;
|
||||||
|
using EdgeAddress = storage::Address<mvcc::VersionList<Edge>>;
|
||||||
|
#endif
|
||||||
|
|
||||||
const auto kVertexCount = 6;
|
const auto kVertexCount = 6;
|
||||||
// Maps vertices to workers
|
// Maps vertices to workers
|
||||||
const std::vector<int> kVertexLocations = {0, 1, 1, 0, 2, 2};
|
const std::vector<int> kVertexLocations = {0, 1, 1, 0, 2, 2};
|
||||||
@ -195,8 +204,8 @@ class Database {
|
|||||||
bool existing_node, query::Expression *lower_bound,
|
bool existing_node, query::Expression *lower_bound,
|
||||||
query::Expression *upper_bound,
|
query::Expression *upper_bound,
|
||||||
const query::plan::ExpansionLambda &filter_lambda) = 0;
|
const query::plan::ExpansionLambda &filter_lambda) = 0;
|
||||||
virtual std::pair<std::vector<storage::VertexAddress>,
|
virtual std::pair<std::vector<VertexAddress>,
|
||||||
std::vector<storage::EdgeAddress>>
|
std::vector<EdgeAddress>>
|
||||||
BuildGraph(database::GraphDbAccessor *dba,
|
BuildGraph(database::GraphDbAccessor *dba,
|
||||||
const std::vector<int> &vertex_locations,
|
const std::vector<int> &vertex_locations,
|
||||||
const std::vector<std::tuple<int, int, std::string>> &edges) = 0;
|
const std::vector<std::tuple<int, int, std::string>> &edges) = 0;
|
||||||
@ -208,7 +217,7 @@ class Database {
|
|||||||
// include query::TypedValue::Null to account for the optional match case.
|
// include query::TypedValue::Null to account for the optional match case.
|
||||||
std::unique_ptr<query::plan::LogicalOperator> YieldVertices(
|
std::unique_ptr<query::plan::LogicalOperator> YieldVertices(
|
||||||
database::GraphDbAccessor *dba,
|
database::GraphDbAccessor *dba,
|
||||||
std::vector<storage::VertexAddress> vertices, query::Symbol symbol,
|
std::vector<VertexAddress> vertices, query::Symbol symbol,
|
||||||
std::shared_ptr<query::plan::LogicalOperator> input_op) {
|
std::shared_ptr<query::plan::LogicalOperator> input_op) {
|
||||||
std::vector<std::vector<query::TypedValue>> frames;
|
std::vector<std::vector<query::TypedValue>> frames;
|
||||||
frames.push_back(std::vector<query::TypedValue>{query::TypedValue::Null});
|
frames.push_back(std::vector<query::TypedValue>{query::TypedValue::Null});
|
||||||
@ -223,8 +232,8 @@ std::unique_ptr<query::plan::LogicalOperator> YieldVertices(
|
|||||||
// Returns an operator that yields edges and vertices given by their address.
|
// Returns an operator that yields edges and vertices given by their address.
|
||||||
std::unique_ptr<query::plan::LogicalOperator> YieldEntities(
|
std::unique_ptr<query::plan::LogicalOperator> YieldEntities(
|
||||||
database::GraphDbAccessor *dba,
|
database::GraphDbAccessor *dba,
|
||||||
std::vector<storage::VertexAddress> vertices,
|
std::vector<VertexAddress> vertices,
|
||||||
std::vector<storage::EdgeAddress> edges, query::Symbol symbol,
|
std::vector<EdgeAddress> edges, query::Symbol symbol,
|
||||||
std::shared_ptr<query::plan::LogicalOperator> input_op) {
|
std::shared_ptr<query::plan::LogicalOperator> input_op) {
|
||||||
std::vector<std::vector<query::TypedValue>> frames;
|
std::vector<std::vector<query::TypedValue>> frames;
|
||||||
for (const auto &vertex : vertices) {
|
for (const auto &vertex : vertices) {
|
||||||
@ -312,8 +321,8 @@ void BfsTest(Database *db, int lower_bound, int upper_bound,
|
|||||||
context.symbol_table_[*inner_node] = inner_node_sym;
|
context.symbol_table_[*inner_node] = inner_node_sym;
|
||||||
context.symbol_table_[*inner_edge] = inner_edge_sym;
|
context.symbol_table_[*inner_edge] = inner_edge_sym;
|
||||||
|
|
||||||
std::vector<storage::VertexAddress> vertices;
|
std::vector<VertexAddress> vertices;
|
||||||
std::vector<storage::EdgeAddress> edges;
|
std::vector<EdgeAddress> edges;
|
||||||
|
|
||||||
std::tie(vertices, edges) =
|
std::tie(vertices, edges) =
|
||||||
db->BuildGraph(dba_ptr.get(), kVertexLocations, kEdges);
|
db->BuildGraph(dba_ptr.get(), kVertexLocations, kEdges);
|
||||||
|
@ -30,18 +30,18 @@ class SingleNodeDb : public Database {
|
|||||||
std::experimental::nullopt, GraphView::OLD);
|
std::experimental::nullopt, GraphView::OLD);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::pair<std::vector<storage::VertexAddress>,
|
std::pair<std::vector<VertexAddress>,
|
||||||
std::vector<storage::EdgeAddress>>
|
std::vector<EdgeAddress>>
|
||||||
BuildGraph(
|
BuildGraph(
|
||||||
database::GraphDbAccessor *dba, const std::vector<int> &vertex_locations,
|
database::GraphDbAccessor *dba, const std::vector<int> &vertex_locations,
|
||||||
const std::vector<std::tuple<int, int, std::string>> &edges) override {
|
const std::vector<std::tuple<int, int, std::string>> &edges) override {
|
||||||
std::vector<storage::VertexAddress> vertex_addr;
|
std::vector<VertexAddress> vertex_addr;
|
||||||
std::vector<storage::EdgeAddress> edge_addr;
|
std::vector<EdgeAddress> edge_addr;
|
||||||
|
|
||||||
for (size_t id = 0; id < vertex_locations.size(); ++id) {
|
for (size_t id = 0; id < vertex_locations.size(); ++id) {
|
||||||
auto vertex = dba->InsertVertex();
|
auto vertex = dba->InsertVertex();
|
||||||
vertex.PropsSet(dba->Property("id"), (int64_t)id);
|
vertex.PropsSet(dba->Property("id"), (int64_t)id);
|
||||||
vertex_addr.push_back(vertex.GlobalAddress());
|
vertex_addr.push_back(vertex.address());
|
||||||
}
|
}
|
||||||
|
|
||||||
for (auto e : edges) {
|
for (auto e : edges) {
|
||||||
@ -53,7 +53,7 @@ class SingleNodeDb : public Database {
|
|||||||
auto edge = dba->InsertEdge(from, to, dba->EdgeType(type));
|
auto edge = dba->InsertEdge(from, to, dba->EdgeType(type));
|
||||||
edge.PropsSet(dba->Property("from"), u);
|
edge.PropsSet(dba->Property("from"), u);
|
||||||
edge.PropsSet(dba->Property("to"), v);
|
edge.PropsSet(dba->Property("to"), v);
|
||||||
edge_addr.push_back(edge.GlobalAddress());
|
edge_addr.push_back(edge.address());
|
||||||
}
|
}
|
||||||
|
|
||||||
return std::make_pair(vertex_addr, edge_addr);
|
return std::make_pair(vertex_addr, edge_addr);
|
||||||
|
@ -19,7 +19,7 @@ TEST(LabelsIndex, UniqueInsert) {
|
|||||||
tx::Engine engine;
|
tx::Engine engine;
|
||||||
|
|
||||||
auto t1 = engine.Begin();
|
auto t1 = engine.Begin();
|
||||||
mvcc::VersionList<Vertex> vlist(*t1, 0, 0);
|
mvcc::VersionList<Vertex> vlist(*t1, 0);
|
||||||
engine.Commit(*t1);
|
engine.Commit(*t1);
|
||||||
auto t2 = engine.Begin();
|
auto t2 = engine.Begin();
|
||||||
|
|
||||||
@ -48,8 +48,8 @@ TEST(LabelsIndex, UniqueFilter) {
|
|||||||
tx::Engine engine;
|
tx::Engine engine;
|
||||||
|
|
||||||
auto t1 = engine.Begin();
|
auto t1 = engine.Begin();
|
||||||
mvcc::VersionList<Vertex> vlist1(*t1, 0, 0);
|
mvcc::VersionList<Vertex> vlist1(*t1, 0);
|
||||||
mvcc::VersionList<Vertex> vlist2(*t1, 1, 1);
|
mvcc::VersionList<Vertex> vlist2(*t1, 1);
|
||||||
engine.Advance(t1->id_);
|
engine.Advance(t1->id_);
|
||||||
auto r1v1 = vlist1.find(*t1);
|
auto r1v1 = vlist1.find(*t1);
|
||||||
auto r1v2 = vlist2.find(*t1);
|
auto r1v2 = vlist2.find(*t1);
|
||||||
@ -89,8 +89,8 @@ TEST(LabelsIndex, Refresh) {
|
|||||||
|
|
||||||
// add two vertices to database
|
// add two vertices to database
|
||||||
auto t1 = engine.Begin();
|
auto t1 = engine.Begin();
|
||||||
mvcc::VersionList<Vertex> vlist1(*t1, 0, 0);
|
mvcc::VersionList<Vertex> vlist1(*t1, 0);
|
||||||
mvcc::VersionList<Vertex> vlist2(*t1, 1, 1);
|
mvcc::VersionList<Vertex> vlist2(*t1, 1);
|
||||||
engine.Advance(t1->id_);
|
engine.Advance(t1->id_);
|
||||||
|
|
||||||
auto v1r1 = vlist1.find(*t1);
|
auto v1r1 = vlist1.find(*t1);
|
||||||
|
@ -25,7 +25,7 @@ class LabelPropertyIndexComplexTest : public ::testing::Test {
|
|||||||
index.IndexFinishedBuilding(*key);
|
index.IndexFinishedBuilding(*key);
|
||||||
|
|
||||||
t = engine.Begin();
|
t = engine.Begin();
|
||||||
vlist = new mvcc::VersionList<Vertex>(*t, 0, 0);
|
vlist = new mvcc::VersionList<Vertex>(*t, 0);
|
||||||
engine.Advance(t->id_);
|
engine.Advance(t->id_);
|
||||||
|
|
||||||
vertex = vlist->find(*t);
|
vertex = vlist->find(*t);
|
||||||
|
@ -5,7 +5,7 @@
|
|||||||
#include "database/distributed/graph_db_accessor.hpp"
|
#include "database/distributed/graph_db_accessor.hpp"
|
||||||
#include "durability/distributed/snapshooter.hpp"
|
#include "durability/distributed/snapshooter.hpp"
|
||||||
#include "durability/distributed/version.hpp"
|
#include "durability/distributed/version.hpp"
|
||||||
#include "durability/paths.hpp"
|
#include "durability/distributed/paths.hpp"
|
||||||
#include "utils/string.hpp"
|
#include "utils/string.hpp"
|
||||||
|
|
||||||
std::vector<fs::path> DirFiles(fs::path dir) {
|
std::vector<fs::path> DirFiles(fs::path dir) {
|
||||||
|
@ -13,15 +13,17 @@
|
|||||||
|
|
||||||
// TODO: FIXME
|
// TODO: FIXME
|
||||||
// #include "database/distributed/distributed_graph_db.hpp"
|
// #include "database/distributed/distributed_graph_db.hpp"
|
||||||
|
#include "communication/bolt/v1/decoder/decoder.hpp"
|
||||||
#include "database/single_node/graph_db.hpp"
|
#include "database/single_node/graph_db.hpp"
|
||||||
#include "database/single_node/graph_db_accessor.hpp"
|
#include "database/single_node/graph_db_accessor.hpp"
|
||||||
#include "durability/hashed_file_reader.hpp"
|
#include "durability/hashed_file_reader.hpp"
|
||||||
#include "durability/paths.hpp"
|
#include "durability/single_node/paths.hpp"
|
||||||
#include "durability/single_node/recovery.hpp"
|
#include "durability/single_node/recovery.hpp"
|
||||||
#include "durability/single_node/snapshooter.hpp"
|
#include "durability/single_node/snapshooter.hpp"
|
||||||
#include "durability/single_node/snapshot_decoder.hpp"
|
|
||||||
#include "durability/single_node/state_delta.hpp"
|
#include "durability/single_node/state_delta.hpp"
|
||||||
#include "durability/single_node/version.hpp"
|
#include "durability/single_node/version.hpp"
|
||||||
|
// TODO: Why do we depend on TypedValue here?
|
||||||
|
#include "query/typed_value.hpp"
|
||||||
#include "utils/string.hpp"
|
#include "utils/string.hpp"
|
||||||
|
|
||||||
DECLARE_int32(wal_flush_interval_millis);
|
DECLARE_int32(wal_flush_interval_millis);
|
||||||
@ -314,10 +316,9 @@ class Durability : public ::testing::Test {
|
|||||||
return config;
|
return config;
|
||||||
}
|
}
|
||||||
|
|
||||||
void MakeSnapshot(int worker_id, database::GraphDb &db,
|
void MakeSnapshot(database::GraphDb &db, int snapshot_max_retained = -1) {
|
||||||
int snapshot_max_retained = -1) {
|
|
||||||
auto dba = db.Access();
|
auto dba = db.Access();
|
||||||
ASSERT_TRUE(durability::MakeSnapshot(db, *dba, worker_id, durability_dir_,
|
ASSERT_TRUE(durability::MakeSnapshot(db, *dba, durability_dir_,
|
||||||
snapshot_max_retained));
|
snapshot_max_retained));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -337,7 +338,7 @@ class Durability : public ::testing::Test {
|
|||||||
// Tests wal encoder to encode correctly non-CRUD deltas, and that all deltas
|
// Tests wal encoder to encode correctly non-CRUD deltas, and that all deltas
|
||||||
// are written in the correct order
|
// are written in the correct order
|
||||||
TEST_F(Durability, WalEncoding) {
|
TEST_F(Durability, WalEncoding) {
|
||||||
gid::Generator generator(0);
|
gid::Generator generator;
|
||||||
auto gid0 = generator.Next();
|
auto gid0 = generator.Next();
|
||||||
auto gid1 = generator.Next();
|
auto gid1 = generator.Next();
|
||||||
{
|
{
|
||||||
@ -413,7 +414,7 @@ TEST_F(Durability, WalEncoding) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(Durability, SnapshotEncoding) {
|
TEST_F(Durability, SnapshotEncoding) {
|
||||||
gid::Generator generator(0);
|
gid::Generator generator;
|
||||||
auto gid0 = generator.Next();
|
auto gid0 = generator.Next();
|
||||||
auto gid1 = generator.Next();
|
auto gid1 = generator.Next();
|
||||||
auto gid2 = generator.Next();
|
auto gid2 = generator.Next();
|
||||||
@ -439,12 +440,12 @@ TEST_F(Durability, SnapshotEncoding) {
|
|||||||
ASSERT_EQ(e1.gid(), gid1);
|
ASSERT_EQ(e1.gid(), gid1);
|
||||||
dba->BuildIndex(dba->Label("l1"), dba->Property("p1"));
|
dba->BuildIndex(dba->Label("l1"), dba->Property("p1"));
|
||||||
dba->Commit();
|
dba->Commit();
|
||||||
MakeSnapshot(0, db);
|
MakeSnapshot(db);
|
||||||
}
|
}
|
||||||
|
|
||||||
auto snapshot = GetLastFile(snapshot_dir_);
|
auto snapshot = GetLastFile(snapshot_dir_);
|
||||||
HashedFileReader buffer;
|
HashedFileReader buffer;
|
||||||
durability::SnapshotDecoder<HashedFileReader> decoder(buffer);
|
communication::bolt::Decoder<HashedFileReader> decoder(buffer);
|
||||||
|
|
||||||
int64_t vertex_count, edge_count;
|
int64_t vertex_count, edge_count;
|
||||||
uint64_t hash;
|
uint64_t hash;
|
||||||
@ -461,15 +462,6 @@ TEST_F(Durability, SnapshotEncoding) {
|
|||||||
communication::bolt::Value dv;
|
communication::bolt::Value dv;
|
||||||
decoder.ReadValue(&dv);
|
decoder.ReadValue(&dv);
|
||||||
ASSERT_EQ(dv.ValueInt(), durability::kVersion);
|
ASSERT_EQ(dv.ValueInt(), durability::kVersion);
|
||||||
// Worker id
|
|
||||||
decoder.ReadValue(&dv);
|
|
||||||
ASSERT_EQ(dv.ValueInt(), 0);
|
|
||||||
// Number of generated vertex ids.
|
|
||||||
decoder.ReadValue(&dv);
|
|
||||||
ASSERT_TRUE(dv.IsInt());
|
|
||||||
// Number of generated edge ids.
|
|
||||||
decoder.ReadValue(&dv);
|
|
||||||
ASSERT_TRUE(dv.IsInt());
|
|
||||||
// Transaction ID.
|
// Transaction ID.
|
||||||
decoder.ReadValue(&dv);
|
decoder.ReadValue(&dv);
|
||||||
ASSERT_TRUE(dv.IsInt());
|
ASSERT_TRUE(dv.IsInt());
|
||||||
@ -482,13 +474,14 @@ TEST_F(Durability, SnapshotEncoding) {
|
|||||||
EXPECT_EQ(dv.ValueList()[0].ValueString(), "l1");
|
EXPECT_EQ(dv.ValueList()[0].ValueString(), "l1");
|
||||||
EXPECT_EQ(dv.ValueList()[1].ValueString(), "p1");
|
EXPECT_EQ(dv.ValueList()[1].ValueString(), "p1");
|
||||||
|
|
||||||
std::map<gid::Gid, durability::SnapshotVertex> decoded_vertices;
|
std::map<gid::Gid, communication::bolt::Vertex> decoded_vertices;
|
||||||
|
|
||||||
// Decode vertices.
|
// Decode vertices.
|
||||||
for (int i = 0; i < vertex_count; ++i) {
|
for (int i = 0; i < vertex_count; ++i) {
|
||||||
auto vertex = decoder.ReadSnapshotVertex();
|
ASSERT_TRUE(
|
||||||
ASSERT_NE(vertex, std::experimental::nullopt);
|
decoder.ReadValue(&dv, communication::bolt::Value::Type::Vertex));
|
||||||
decoded_vertices.emplace(vertex->gid, *vertex);
|
auto &vertex = dv.ValueVertex();
|
||||||
|
decoded_vertices.emplace(vertex.id.AsUint(), vertex);
|
||||||
}
|
}
|
||||||
ASSERT_EQ(decoded_vertices.size(), 3);
|
ASSERT_EQ(decoded_vertices.size(), 3);
|
||||||
ASSERT_EQ(decoded_vertices[gid0].labels.size(), 1);
|
ASSERT_EQ(decoded_vertices[gid0].labels.size(), 1);
|
||||||
@ -504,13 +497,9 @@ TEST_F(Durability, SnapshotEncoding) {
|
|||||||
|
|
||||||
// Decode edges.
|
// Decode edges.
|
||||||
for (int i = 0; i < edge_count; ++i) {
|
for (int i = 0; i < edge_count; ++i) {
|
||||||
decoder.ReadValue(&dv);
|
ASSERT_TRUE(decoder.ReadValue(&dv, communication::bolt::Value::Type::Edge));
|
||||||
ASSERT_EQ(dv.type(), communication::bolt::Value::Type::Edge);
|
|
||||||
auto &edge = dv.ValueEdge();
|
auto &edge = dv.ValueEdge();
|
||||||
decoded_edges.emplace(edge.id.AsUint(), edge);
|
decoded_edges.emplace(edge.id.AsUint(), edge);
|
||||||
// Read cypher_id.
|
|
||||||
decoder.ReadValue(&dv);
|
|
||||||
ASSERT_EQ(dv.type(), communication::bolt::Value::Type::Int);
|
|
||||||
}
|
}
|
||||||
EXPECT_EQ(decoded_edges.size(), 2);
|
EXPECT_EQ(decoded_edges.size(), 2);
|
||||||
EXPECT_EQ(decoded_edges[gid0].from.AsUint(), gid0);
|
EXPECT_EQ(decoded_edges[gid0].from.AsUint(), gid0);
|
||||||
@ -536,7 +525,7 @@ TEST_F(Durability, SnapshotRecovery) {
|
|||||||
MakeDb(db, 300, {0, 1, 2});
|
MakeDb(db, 300, {0, 1, 2});
|
||||||
MakeDb(db, 300);
|
MakeDb(db, 300);
|
||||||
MakeDb(db, 300, {3, 4});
|
MakeDb(db, 300, {3, 4});
|
||||||
MakeSnapshot(0, db);
|
MakeSnapshot(db);
|
||||||
{
|
{
|
||||||
auto recovered_config = DbConfig();
|
auto recovered_config = DbConfig();
|
||||||
recovered_config.db_recover_on_startup = true;
|
recovered_config.db_recover_on_startup = true;
|
||||||
@ -545,71 +534,6 @@ TEST_F(Durability, SnapshotRecovery) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(Durability, SnapshotNoVerticesIdRecovery) {
|
|
||||||
database::GraphDb db{DbConfig()};
|
|
||||||
MakeDb(db, 10);
|
|
||||||
|
|
||||||
// Erase all vertices, this should cause snapshot to not have any more
|
|
||||||
// vertices which should make it not change any id after snapshot recovery,
|
|
||||||
// but we still have to make sure that the id for generators is recovered
|
|
||||||
{
|
|
||||||
auto dba = db.Access();
|
|
||||||
for (auto vertex : dba->Vertices(false)) dba->RemoveVertex(vertex);
|
|
||||||
dba->Commit();
|
|
||||||
}
|
|
||||||
|
|
||||||
MakeSnapshot(0, db);
|
|
||||||
{
|
|
||||||
auto recovered_config = DbConfig();
|
|
||||||
recovered_config.db_recover_on_startup = true;
|
|
||||||
database::GraphDb recovered{recovered_config};
|
|
||||||
EXPECT_EQ(db.storage().VertexGenerator().LocalCount(),
|
|
||||||
recovered.storage().VertexGenerator().LocalCount());
|
|
||||||
EXPECT_EQ(db.storage().EdgeGenerator().LocalCount(),
|
|
||||||
recovered.storage().EdgeGenerator().LocalCount());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_F(Durability, SnapshotAndWalIdRecovery) {
|
|
||||||
auto config = DbConfig();
|
|
||||||
config.durability_enabled = true;
|
|
||||||
database::GraphDb db{config};
|
|
||||||
MakeDb(db, 300);
|
|
||||||
MakeSnapshot(0, db);
|
|
||||||
MakeDb(db, 300);
|
|
||||||
db.wal().Flush();
|
|
||||||
ASSERT_EQ(DirFiles(snapshot_dir_).size(), 1);
|
|
||||||
EXPECT_GT(DirFiles(wal_dir_).size(), 1);
|
|
||||||
{
|
|
||||||
auto recovered_config = DbConfig();
|
|
||||||
recovered_config.db_recover_on_startup = true;
|
|
||||||
database::GraphDb recovered{recovered_config};
|
|
||||||
EXPECT_EQ(db.storage().VertexGenerator().LocalCount(),
|
|
||||||
recovered.storage().VertexGenerator().LocalCount());
|
|
||||||
EXPECT_EQ(db.storage().EdgeGenerator().LocalCount(),
|
|
||||||
recovered.storage().EdgeGenerator().LocalCount());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_F(Durability, OnlyWalIdRecovery) {
|
|
||||||
auto config = DbConfig();
|
|
||||||
config.durability_enabled = true;
|
|
||||||
database::GraphDb db{config};
|
|
||||||
MakeDb(db, 300);
|
|
||||||
db.wal().Flush();
|
|
||||||
ASSERT_EQ(DirFiles(snapshot_dir_).size(), 0);
|
|
||||||
EXPECT_GT(DirFiles(wal_dir_).size(), 1);
|
|
||||||
{
|
|
||||||
auto recovered_config = DbConfig();
|
|
||||||
recovered_config.db_recover_on_startup = true;
|
|
||||||
database::GraphDb recovered{recovered_config};
|
|
||||||
EXPECT_EQ(db.storage().VertexGenerator().LocalCount(),
|
|
||||||
recovered.storage().VertexGenerator().LocalCount());
|
|
||||||
EXPECT_EQ(db.storage().EdgeGenerator().LocalCount(),
|
|
||||||
recovered.storage().EdgeGenerator().LocalCount());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_F(Durability, WalRecovery) {
|
TEST_F(Durability, WalRecovery) {
|
||||||
auto modify_config = [](database::Config config, bool durability_enabled,
|
auto modify_config = [](database::Config config, bool durability_enabled,
|
||||||
bool synchronous_commit) {
|
bool synchronous_commit) {
|
||||||
@ -649,7 +573,7 @@ TEST_F(Durability, SnapshotAndWalRecovery) {
|
|||||||
database::GraphDb db{config};
|
database::GraphDb db{config};
|
||||||
MakeDb(db, 300, {0, 1, 2});
|
MakeDb(db, 300, {0, 1, 2});
|
||||||
MakeDb(db, 300);
|
MakeDb(db, 300);
|
||||||
MakeSnapshot(0, db);
|
MakeSnapshot(db);
|
||||||
MakeDb(db, 300, {3, 4});
|
MakeDb(db, 300, {3, 4});
|
||||||
MakeDb(db, 300);
|
MakeDb(db, 300);
|
||||||
MakeDb(db, 300, {5});
|
MakeDb(db, 300, {5});
|
||||||
@ -685,7 +609,7 @@ TEST_F(Durability, SnapshotAndWalRecoveryAfterComplexTxSituation) {
|
|||||||
MakeDb(*dba_3, 100);
|
MakeDb(*dba_3, 100);
|
||||||
dba_3->Commit();
|
dba_3->Commit();
|
||||||
|
|
||||||
MakeSnapshot(0, db); // Snapshooter takes the fourth transaction.
|
MakeSnapshot(db); // Snapshooter takes the fourth transaction.
|
||||||
dba_2->Commit();
|
dba_2->Commit();
|
||||||
|
|
||||||
// The fifth transaction starts and commits after snapshot.
|
// The fifth transaction starts and commits after snapshot.
|
||||||
@ -747,7 +671,7 @@ TEST_F(Durability, SnapshotRetention) {
|
|||||||
// Track the added snapshots to ensure the correct ones are pruned.
|
// Track the added snapshots to ensure the correct ones are pruned.
|
||||||
std::unordered_set<std::string> snapshots;
|
std::unordered_set<std::string> snapshots;
|
||||||
for (int i = 0; i < count; ++i) {
|
for (int i = 0; i < count; ++i) {
|
||||||
MakeSnapshot(0, db, retain);
|
MakeSnapshot(db, retain);
|
||||||
auto latest = GetLastFile(snapshot_dir_);
|
auto latest = GetLastFile(snapshot_dir_);
|
||||||
snapshots.emplace(GetLastFile(snapshot_dir_));
|
snapshots.emplace(GetLastFile(snapshot_dir_));
|
||||||
// Ensures that the latest snapshot was not in the snapshots collection
|
// Ensures that the latest snapshot was not in the snapshots collection
|
||||||
@ -767,13 +691,13 @@ TEST_F(Durability, WalRetention) {
|
|||||||
config.durability_enabled = true;
|
config.durability_enabled = true;
|
||||||
database::GraphDb db{config};
|
database::GraphDb db{config};
|
||||||
MakeDb(db, 100);
|
MakeDb(db, 100);
|
||||||
MakeSnapshot(0, db);
|
MakeSnapshot(db);
|
||||||
MakeDb(db, 100);
|
MakeDb(db, 100);
|
||||||
EXPECT_EQ(DirFiles(snapshot_dir_).size(), 1);
|
EXPECT_EQ(DirFiles(snapshot_dir_).size(), 1);
|
||||||
db.wal().Flush();
|
db.wal().Flush();
|
||||||
// 1 current WAL file, plus retained ones
|
// 1 current WAL file, plus retained ones
|
||||||
EXPECT_GT(DirFiles(wal_dir_).size(), 1);
|
EXPECT_GT(DirFiles(wal_dir_).size(), 1);
|
||||||
MakeSnapshot(0, db);
|
MakeSnapshot(db);
|
||||||
db.wal().Flush();
|
db.wal().Flush();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -882,13 +806,13 @@ TEST_F(Durability, SequentialRecovery) {
|
|||||||
auto update_theads = run_updates(db, keep_running);
|
auto update_theads = run_updates(db, keep_running);
|
||||||
std::this_thread::sleep_for(25ms);
|
std::this_thread::sleep_for(25ms);
|
||||||
if (snapshot_during) {
|
if (snapshot_during) {
|
||||||
MakeSnapshot(0, db);
|
MakeSnapshot(db);
|
||||||
}
|
}
|
||||||
std::this_thread::sleep_for(25ms);
|
std::this_thread::sleep_for(25ms);
|
||||||
keep_running = false;
|
keep_running = false;
|
||||||
for (auto &t : update_theads) t.join();
|
for (auto &t : update_theads) t.join();
|
||||||
if (snapshot_after) {
|
if (snapshot_after) {
|
||||||
MakeSnapshot(0, db);
|
MakeSnapshot(db);
|
||||||
}
|
}
|
||||||
|
|
||||||
db.wal().Flush();
|
db.wal().Flush();
|
||||||
@ -926,7 +850,7 @@ TEST_F(Durability, ContainsDurabilityFilesSnapshot) {
|
|||||||
database::GraphDb db{DbConfig()};
|
database::GraphDb db{DbConfig()};
|
||||||
auto dba = db.Access();
|
auto dba = db.Access();
|
||||||
auto v0 = dba->InsertVertex();
|
auto v0 = dba->InsertVertex();
|
||||||
MakeSnapshot(0, db);
|
MakeSnapshot(db);
|
||||||
}
|
}
|
||||||
ASSERT_TRUE(durability::ContainsDurabilityFiles(durability_dir_));
|
ASSERT_TRUE(durability::ContainsDurabilityFiles(durability_dir_));
|
||||||
}
|
}
|
||||||
@ -949,7 +873,7 @@ TEST_F(Durability, MoveToBackupSnapshot) {
|
|||||||
database::GraphDb db{DbConfig()};
|
database::GraphDb db{DbConfig()};
|
||||||
auto dba = db.Access();
|
auto dba = db.Access();
|
||||||
auto v0 = dba->InsertVertex();
|
auto v0 = dba->InsertVertex();
|
||||||
MakeSnapshot(0, db);
|
MakeSnapshot(db);
|
||||||
}
|
}
|
||||||
|
|
||||||
// durability-enabled=true, db-recover-on-startup=false
|
// durability-enabled=true, db-recover-on-startup=false
|
||||||
|
@ -19,7 +19,7 @@ auto Count(TIterable iterable) {
|
|||||||
TEST(GraphDbAccessorTest, InsertVertex) {
|
TEST(GraphDbAccessorTest, InsertVertex) {
|
||||||
GraphDb db;
|
GraphDb db;
|
||||||
auto accessor = db.Access();
|
auto accessor = db.Access();
|
||||||
gid::Generator generator(0);
|
gid::Generator generator;
|
||||||
|
|
||||||
EXPECT_EQ(Count(accessor->Vertices(false)), 0);
|
EXPECT_EQ(Count(accessor->Vertices(false)), 0);
|
||||||
|
|
||||||
|
@ -3,7 +3,6 @@
|
|||||||
#include <glog/logging.h>
|
#include <glog/logging.h>
|
||||||
#include <gtest/gtest.h>
|
#include <gtest/gtest.h>
|
||||||
|
|
||||||
#include "durability/paths.hpp"
|
|
||||||
#include "storage/kvstore/kvstore.hpp"
|
#include "storage/kvstore/kvstore.hpp"
|
||||||
#include "utils/file.hpp"
|
#include "utils/file.hpp"
|
||||||
|
|
||||||
|
@ -15,8 +15,8 @@ TEST(MVCC, Deadlock) {
|
|||||||
tx::Engine engine;
|
tx::Engine engine;
|
||||||
|
|
||||||
auto t0 = engine.Begin();
|
auto t0 = engine.Begin();
|
||||||
mvcc::VersionList<Prop> version_list1(*t0, 0, 0);
|
mvcc::VersionList<Prop> version_list1(*t0, 0);
|
||||||
mvcc::VersionList<Prop> version_list2(*t0, 1, 1);
|
mvcc::VersionList<Prop> version_list2(*t0, 1);
|
||||||
engine.Commit(*t0);
|
engine.Commit(*t0);
|
||||||
|
|
||||||
auto t1 = engine.Begin();
|
auto t1 = engine.Begin();
|
||||||
@ -34,7 +34,7 @@ TEST(MVCC, UpdateDontDelete) {
|
|||||||
{
|
{
|
||||||
tx::Engine engine;
|
tx::Engine engine;
|
||||||
auto t1 = engine.Begin();
|
auto t1 = engine.Begin();
|
||||||
mvcc::VersionList<DestrCountRec> version_list(*t1, 0, 0, count);
|
mvcc::VersionList<DestrCountRec> version_list(*t1, 0, count);
|
||||||
engine.Commit(*t1);
|
engine.Commit(*t1);
|
||||||
|
|
||||||
auto t2 = engine.Begin();
|
auto t2 = engine.Begin();
|
||||||
@ -58,7 +58,7 @@ TEST(MVCC, UpdateDontDelete) {
|
|||||||
TEST(MVCC, Oldest) {
|
TEST(MVCC, Oldest) {
|
||||||
tx::Engine engine;
|
tx::Engine engine;
|
||||||
auto t1 = engine.Begin();
|
auto t1 = engine.Begin();
|
||||||
mvcc::VersionList<Prop> version_list(*t1, 0, 0);
|
mvcc::VersionList<Prop> version_list(*t1, 0);
|
||||||
auto first = version_list.Oldest();
|
auto first = version_list.Oldest();
|
||||||
EXPECT_NE(first, nullptr);
|
EXPECT_NE(first, nullptr);
|
||||||
// TODO Gleich: no need to do 10 checks of the same thing
|
// TODO Gleich: no need to do 10 checks of the same thing
|
||||||
|
@ -60,7 +60,7 @@ class Mvcc : public ::testing::Test {
|
|||||||
int version_list_size = 0;
|
int version_list_size = 0;
|
||||||
tx::Engine engine;
|
tx::Engine engine;
|
||||||
tx::Transaction *t1 = engine.Begin();
|
tx::Transaction *t1 = engine.Begin();
|
||||||
mvcc::VersionList<TestClass> version_list{*t1, 0, 0, version_list_size};
|
mvcc::VersionList<TestClass> version_list{*t1, 0, version_list_size};
|
||||||
TestClass *v1 = nullptr;
|
TestClass *v1 = nullptr;
|
||||||
tx::Transaction *t2 = nullptr;
|
tx::Transaction *t2 = nullptr;
|
||||||
tx::TransactionId id0, id1, id2;
|
tx::TransactionId id0, id1, id2;
|
||||||
|
@ -24,7 +24,7 @@ class MvccGcTest : public ::testing::Test {
|
|||||||
|
|
||||||
protected:
|
protected:
|
||||||
std::atomic<int> record_destruction_count{0};
|
std::atomic<int> record_destruction_count{0};
|
||||||
mvcc::VersionList<DestrCountRec> version_list{*t0, 0, 0,
|
mvcc::VersionList<DestrCountRec> version_list{*t0, 0,
|
||||||
record_destruction_count};
|
record_destruction_count};
|
||||||
std::vector<tx::Transaction *> transactions{t0};
|
std::vector<tx::Transaction *> transactions{t0};
|
||||||
|
|
||||||
@ -125,7 +125,7 @@ TEST(GarbageCollector, GcClean) {
|
|||||||
auto t1 = engine.Begin();
|
auto t1 = engine.Begin();
|
||||||
std::atomic<int> record_destruction_count{0};
|
std::atomic<int> record_destruction_count{0};
|
||||||
auto vl =
|
auto vl =
|
||||||
new mvcc::VersionList<DestrCountRec>(*t1, 0, 0, record_destruction_count);
|
new mvcc::VersionList<DestrCountRec>(*t1, 0, record_destruction_count);
|
||||||
auto access = collection.access();
|
auto access = collection.access();
|
||||||
access.insert(0, vl);
|
access.insert(0, vl);
|
||||||
engine.Commit(*t1);
|
engine.Commit(*t1);
|
||||||
|
@ -1351,22 +1351,26 @@ TEST_F(FunctionTest, Id) {
|
|||||||
auto vb = dba->InsertVertex();
|
auto vb = dba->InsertVertex();
|
||||||
EXPECT_EQ(EvaluateFunction("ID", {va}).ValueInt(), 0);
|
EXPECT_EQ(EvaluateFunction("ID", {va}).ValueInt(), 0);
|
||||||
EXPECT_EQ(EvaluateFunction("ID", {ea}).ValueInt(), 0);
|
EXPECT_EQ(EvaluateFunction("ID", {ea}).ValueInt(), 0);
|
||||||
EXPECT_EQ(EvaluateFunction("ID", {vb}).ValueInt(), 1024);
|
EXPECT_EQ(EvaluateFunction("ID", {vb}).ValueInt(), 1);
|
||||||
EXPECT_THROW(EvaluateFunction("ID", {}), QueryRuntimeException);
|
EXPECT_THROW(EvaluateFunction("ID", {}), QueryRuntimeException);
|
||||||
EXPECT_THROW(EvaluateFunction("ID", {0}), QueryRuntimeException);
|
EXPECT_THROW(EvaluateFunction("ID", {0}), QueryRuntimeException);
|
||||||
EXPECT_THROW(EvaluateFunction("ID", {va, ea}), QueryRuntimeException);
|
EXPECT_THROW(EvaluateFunction("ID", {va, ea}), QueryRuntimeException);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* TODO: FIXME
|
||||||
TEST_F(FunctionTest, WorkerIdException) {
|
TEST_F(FunctionTest, WorkerIdException) {
|
||||||
auto va = dba->InsertVertex();
|
auto va = dba->InsertVertex();
|
||||||
EXPECT_THROW(EvaluateFunction("WORKERID", {}), QueryRuntimeException);
|
EXPECT_THROW(EvaluateFunction("WORKERID", {}), QueryRuntimeException);
|
||||||
EXPECT_THROW(EvaluateFunction("WORKERID", {va, va}), QueryRuntimeException);
|
EXPECT_THROW(EvaluateFunction("WORKERID", {va, va}), QueryRuntimeException);
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* TODO: FIXME
|
||||||
TEST_F(FunctionTest, WorkerIdSingleNode) {
|
TEST_F(FunctionTest, WorkerIdSingleNode) {
|
||||||
auto va = dba->InsertVertex();
|
auto va = dba->InsertVertex();
|
||||||
EXPECT_EQ(EvaluateFunction("WORKERID", {va}).ValueInt(), 0);
|
EXPECT_EQ(EvaluateFunction("WORKERID", {va}).ValueInt(), 0);
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
TEST_F(FunctionTest, ToStringNull) {
|
TEST_F(FunctionTest, ToStringNull) {
|
||||||
EXPECT_TRUE(EvaluateFunction("TOSTRING", {TypedValue::Null}).IsNull());
|
EXPECT_TRUE(EvaluateFunction("TOSTRING", {TypedValue::Null}).IsNull());
|
||||||
|
@ -7,7 +7,6 @@
|
|||||||
#include "database/single_node/graph_db_accessor.hpp"
|
#include "database/single_node/graph_db_accessor.hpp"
|
||||||
#include "mvcc/single_node/version_list.hpp"
|
#include "mvcc/single_node/version_list.hpp"
|
||||||
#include "storage/common/property_value.hpp"
|
#include "storage/common/property_value.hpp"
|
||||||
#include "storage/single_node/address.hpp"
|
|
||||||
#include "storage/single_node/edge_accessor.hpp"
|
#include "storage/single_node/edge_accessor.hpp"
|
||||||
#include "storage/single_node/vertex.hpp"
|
#include "storage/single_node/vertex.hpp"
|
||||||
#include "storage/single_node/vertex_accessor.hpp"
|
#include "storage/single_node/vertex_accessor.hpp"
|
||||||
@ -60,18 +59,6 @@ TEST(RecordAccessor, RecordEquality) {
|
|||||||
EXPECT_NE(e1, e2);
|
EXPECT_NE(e1, e2);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(RecordAccessor, GlobalToLocalAddressConversion) {
|
|
||||||
database::GraphDb db;
|
|
||||||
auto dba = db.Access();
|
|
||||||
|
|
||||||
auto v1 = dba->InsertVertex();
|
|
||||||
storage::Address<mvcc::VersionList<Vertex>> global_address{v1.gid(), 0};
|
|
||||||
EXPECT_FALSE(global_address.is_local());
|
|
||||||
auto v1_from_global = VertexAccessor(global_address, *dba);
|
|
||||||
EXPECT_TRUE(v1_from_global.address().is_local());
|
|
||||||
EXPECT_EQ(v1_from_global.address(), v1.address());
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(RecordAccessor, SwitchOldAndSwitchNewMemberFunctionTest) {
|
TEST(RecordAccessor, SwitchOldAndSwitchNewMemberFunctionTest) {
|
||||||
database::GraphDb db;
|
database::GraphDb db;
|
||||||
|
|
||||||
|
@ -6,12 +6,12 @@
|
|||||||
|
|
||||||
TEST(StateDelta, CreateVertex) {
|
TEST(StateDelta, CreateVertex) {
|
||||||
database::GraphDb db;
|
database::GraphDb db;
|
||||||
gid::Generator generator(0);
|
gid::Generator generator;
|
||||||
auto gid0 = generator.Next();
|
auto gid0 = generator.Next();
|
||||||
{
|
{
|
||||||
auto dba = db.Access();
|
auto dba = db.Access();
|
||||||
auto delta =
|
auto delta =
|
||||||
database::StateDelta::CreateVertex(dba->transaction_id(), gid0, 0);
|
database::StateDelta::CreateVertex(dba->transaction_id(), gid0);
|
||||||
delta.Apply(*dba);
|
delta.Apply(*dba);
|
||||||
dba->Commit();
|
dba->Commit();
|
||||||
}
|
}
|
||||||
@ -25,7 +25,7 @@ TEST(StateDelta, CreateVertex) {
|
|||||||
|
|
||||||
TEST(StateDelta, RemoveVertex) {
|
TEST(StateDelta, RemoveVertex) {
|
||||||
database::GraphDb db;
|
database::GraphDb db;
|
||||||
gid::Generator generator(0);
|
gid::Generator generator;
|
||||||
auto gid0 = generator.Next();
|
auto gid0 = generator.Next();
|
||||||
{
|
{
|
||||||
auto dba = db.Access();
|
auto dba = db.Access();
|
||||||
@ -48,7 +48,7 @@ TEST(StateDelta, RemoveVertex) {
|
|||||||
|
|
||||||
TEST(StateDelta, CreateEdge) {
|
TEST(StateDelta, CreateEdge) {
|
||||||
database::GraphDb db;
|
database::GraphDb db;
|
||||||
gid::Generator generator(0);
|
gid::Generator generator;
|
||||||
auto gid0 = generator.Next();
|
auto gid0 = generator.Next();
|
||||||
auto gid1 = generator.Next();
|
auto gid1 = generator.Next();
|
||||||
auto gid2 = generator.Next();
|
auto gid2 = generator.Next();
|
||||||
@ -61,7 +61,7 @@ TEST(StateDelta, CreateEdge) {
|
|||||||
{
|
{
|
||||||
auto dba = db.Access();
|
auto dba = db.Access();
|
||||||
auto delta =
|
auto delta =
|
||||||
database::StateDelta::CreateEdge(dba->transaction_id(), gid2, 0, gid0,
|
database::StateDelta::CreateEdge(dba->transaction_id(), gid2, gid0,
|
||||||
gid1, dba->EdgeType("edge"), "edge");
|
gid1, dba->EdgeType("edge"), "edge");
|
||||||
delta.Apply(*dba);
|
delta.Apply(*dba);
|
||||||
dba->Commit();
|
dba->Commit();
|
||||||
@ -75,7 +75,7 @@ TEST(StateDelta, CreateEdge) {
|
|||||||
|
|
||||||
TEST(StateDelta, RemoveEdge) {
|
TEST(StateDelta, RemoveEdge) {
|
||||||
database::GraphDb db;
|
database::GraphDb db;
|
||||||
gid::Generator generator(0);
|
gid::Generator generator;
|
||||||
auto gid0 = generator.Next();
|
auto gid0 = generator.Next();
|
||||||
auto gid1 = generator.Next();
|
auto gid1 = generator.Next();
|
||||||
auto gid2 = generator.Next();
|
auto gid2 = generator.Next();
|
||||||
@ -101,7 +101,7 @@ TEST(StateDelta, RemoveEdge) {
|
|||||||
|
|
||||||
TEST(StateDelta, AddLabel) {
|
TEST(StateDelta, AddLabel) {
|
||||||
database::GraphDb db;
|
database::GraphDb db;
|
||||||
gid::Generator generator(0);
|
gid::Generator generator;
|
||||||
auto gid0 = generator.Next();
|
auto gid0 = generator.Next();
|
||||||
{
|
{
|
||||||
auto dba = db.Access();
|
auto dba = db.Access();
|
||||||
@ -127,7 +127,7 @@ TEST(StateDelta, AddLabel) {
|
|||||||
|
|
||||||
TEST(StateDelta, RemoveLabel) {
|
TEST(StateDelta, RemoveLabel) {
|
||||||
database::GraphDb db;
|
database::GraphDb db;
|
||||||
gid::Generator generator(0);
|
gid::Generator generator;
|
||||||
auto gid0 = generator.Next();
|
auto gid0 = generator.Next();
|
||||||
{
|
{
|
||||||
auto dba = db.Access();
|
auto dba = db.Access();
|
||||||
@ -153,7 +153,7 @@ TEST(StateDelta, RemoveLabel) {
|
|||||||
|
|
||||||
TEST(StateDelta, SetPropertyVertex) {
|
TEST(StateDelta, SetPropertyVertex) {
|
||||||
database::GraphDb db;
|
database::GraphDb db;
|
||||||
gid::Generator generator(0);
|
gid::Generator generator;
|
||||||
auto gid0 = generator.Next();
|
auto gid0 = generator.Next();
|
||||||
{
|
{
|
||||||
auto dba = db.Access();
|
auto dba = db.Access();
|
||||||
@ -179,7 +179,7 @@ TEST(StateDelta, SetPropertyVertex) {
|
|||||||
|
|
||||||
TEST(StateDelta, SetPropertyEdge) {
|
TEST(StateDelta, SetPropertyEdge) {
|
||||||
database::GraphDb db;
|
database::GraphDb db;
|
||||||
gid::Generator generator(0);
|
gid::Generator generator;
|
||||||
auto gid0 = generator.Next();
|
auto gid0 = generator.Next();
|
||||||
auto gid1 = generator.Next();
|
auto gid1 = generator.Next();
|
||||||
auto gid2 = generator.Next();
|
auto gid2 = generator.Next();
|
||||||
|
@ -11,6 +11,7 @@
|
|||||||
- \./memgraph/tools/apollo/\.cppcheck_errors
|
- \./memgraph/tools/apollo/\.cppcheck_errors
|
||||||
|
|
||||||
- name: code_coverage
|
- name: code_coverage
|
||||||
|
project: ^NEVER$ # TODO (mferencevic): remove when the coverage is split into two (single node and distributed)
|
||||||
type: data process
|
type: data process
|
||||||
require_runs: ^unit__.+ # regex to match all unit runs
|
require_runs: ^unit__.+ # regex to match all unit runs
|
||||||
commands: TIMEOUT=300 ./coverage_convert
|
commands: TIMEOUT=300 ./coverage_convert
|
||||||
|
@ -10,13 +10,11 @@
|
|||||||
#include <glog/logging.h>
|
#include <glog/logging.h>
|
||||||
|
|
||||||
#include "config.hpp"
|
#include "config.hpp"
|
||||||
|
#include "communication/bolt/v1/encoder/base_encoder.hpp"
|
||||||
#include "durability/hashed_file_writer.hpp"
|
#include "durability/hashed_file_writer.hpp"
|
||||||
#include "durability/paths.hpp"
|
#include "durability/single_node/paths.hpp"
|
||||||
#include "durability/single_node/snapshooter.hpp"
|
#include "durability/single_node/snapshooter.hpp"
|
||||||
#include "durability/single_node/snapshot_encoder.hpp"
|
|
||||||
#include "durability/single_node/snapshot_value.hpp"
|
|
||||||
#include "durability/single_node/version.hpp"
|
#include "durability/single_node/version.hpp"
|
||||||
#include "storage/single_node/address_types.hpp"
|
|
||||||
#include "utils/cast.hpp"
|
#include "utils/cast.hpp"
|
||||||
#include "utils/string.hpp"
|
#include "utils/string.hpp"
|
||||||
#include "utils/timer.hpp"
|
#include "utils/timer.hpp"
|
||||||
@ -156,7 +154,7 @@ class MemgraphNodeIdMap {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
gid::Generator generator_{0};
|
gid::Generator generator_;
|
||||||
std::unordered_map<NodeId, int64_t> node_id_to_mg_;
|
std::unordered_map<NodeId, int64_t> node_id_to_mg_;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -262,7 +260,7 @@ std::string GetIdSpace(const std::string &type) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void WriteNodeRow(
|
void WriteNodeRow(
|
||||||
std::unordered_map<gid::Gid, durability::SnapshotVertex> &partial_vertices,
|
std::unordered_map<gid::Gid, communication::bolt::Vertex> &partial_vertices,
|
||||||
const std::vector<Field> &fields, const std::vector<std::string> &row,
|
const std::vector<Field> &fields, const std::vector<std::string> &row,
|
||||||
const std::vector<std::string> &additional_labels,
|
const std::vector<std::string> &additional_labels,
|
||||||
MemgraphNodeIdMap &node_id_map) {
|
MemgraphNodeIdMap &node_id_map) {
|
||||||
@ -298,12 +296,12 @@ void WriteNodeRow(
|
|||||||
labels.insert(labels.end(), additional_labels.begin(),
|
labels.insert(labels.end(), additional_labels.begin(),
|
||||||
additional_labels.end());
|
additional_labels.end());
|
||||||
CHECK(id) << "Node ID must be specified";
|
CHECK(id) << "Node ID must be specified";
|
||||||
partial_vertices[*id] = {
|
partial_vertices[*id] = {communication::bolt::Id::FromUint(*id), labels,
|
||||||
*id, utils::MemcpyCast<int64_t>(*id), labels, properties, {}};
|
properties};
|
||||||
}
|
}
|
||||||
|
|
||||||
auto PassNodes(
|
auto PassNodes(
|
||||||
std::unordered_map<gid::Gid, durability::SnapshotVertex> &partial_vertices,
|
std::unordered_map<gid::Gid, communication::bolt::Vertex> &partial_vertices,
|
||||||
const std::string &nodes_path, MemgraphNodeIdMap &node_id_map,
|
const std::string &nodes_path, MemgraphNodeIdMap &node_id_map,
|
||||||
const std::vector<std::string> &additional_labels) {
|
const std::vector<std::string> &additional_labels) {
|
||||||
int64_t node_count = 0;
|
int64_t node_count = 0;
|
||||||
@ -395,10 +393,10 @@ void Convert(const std::vector<std::string> &nodes,
|
|||||||
const std::string &output_path) {
|
const std::string &output_path) {
|
||||||
try {
|
try {
|
||||||
HashedFileWriter buffer(output_path);
|
HashedFileWriter buffer(output_path);
|
||||||
durability::SnapshotEncoder<HashedFileWriter> encoder(buffer);
|
communication::bolt::BaseEncoder<HashedFileWriter> encoder(buffer);
|
||||||
int64_t node_count = 0;
|
int64_t node_count = 0;
|
||||||
int64_t edge_count = 0;
|
int64_t edge_count = 0;
|
||||||
gid::Generator relationship_id_generator(0);
|
gid::Generator relationship_id_generator;
|
||||||
MemgraphNodeIdMap node_id_map;
|
MemgraphNodeIdMap node_id_map;
|
||||||
// Snapshot file has the following contents in order:
|
// Snapshot file has the following contents in order:
|
||||||
// 1) Magic number.
|
// 1) Magic number.
|
||||||
@ -413,20 +411,10 @@ void Convert(const std::vector<std::string> &nodes,
|
|||||||
durability::kSnapshotMagic.size());
|
durability::kSnapshotMagic.size());
|
||||||
encoder.WriteValue(durability::kVersion);
|
encoder.WriteValue(durability::kVersion);
|
||||||
|
|
||||||
encoder.WriteInt(0); // Worker Id - for this use case it's okay to set to 0
|
|
||||||
// since we are using a single-node version of
|
|
||||||
// memgraph here
|
|
||||||
// The following two entries indicate the starting points for generating new
|
|
||||||
// Vertex/Edge IDs in the DB. They are only important when there are
|
|
||||||
// vertices/edges that were moved to another worker (in distributed
|
|
||||||
// Memgraph), so it's safe to set them to 0 in snapshot generation.
|
|
||||||
encoder.WriteInt(0); // Internal Id of vertex generator
|
|
||||||
encoder.WriteInt(0); // Internal Id of edge generator
|
|
||||||
|
|
||||||
encoder.WriteInt(0); // Id of transaction that is snapshooting.
|
encoder.WriteInt(0); // Id of transaction that is snapshooting.
|
||||||
encoder.WriteList({}); // Transactional snapshot.
|
encoder.WriteList({}); // Transactional snapshot.
|
||||||
encoder.WriteList({}); // Label + property indexes.
|
encoder.WriteList({}); // Label + property indexes.
|
||||||
std::unordered_map<gid::Gid, durability::SnapshotVertex> vertices;
|
std::unordered_map<gid::Gid, communication::bolt::Vertex> vertices;
|
||||||
std::unordered_map<gid::Gid, communication::bolt::Edge> edges;
|
std::unordered_map<gid::Gid, communication::bolt::Edge> edges;
|
||||||
for (const auto &nodes_file : nodes) {
|
for (const auto &nodes_file : nodes) {
|
||||||
node_count +=
|
node_count +=
|
||||||
@ -436,66 +424,15 @@ void Convert(const std::vector<std::string> &nodes,
|
|||||||
edge_count += PassRelationships(edges, relationships_file, node_id_map,
|
edge_count += PassRelationships(edges, relationships_file, node_id_map,
|
||||||
relationship_id_generator);
|
relationship_id_generator);
|
||||||
}
|
}
|
||||||
for (auto edge : edges) {
|
|
||||||
auto encoded = edge.second;
|
|
||||||
auto edge_address = storage::EdgeAddress(encoded.id.AsUint(), 0);
|
|
||||||
vertices[encoded.from.AsUint()].out.push_back(
|
|
||||||
{edge_address, storage::VertexAddress(encoded.to.AsUint(), 0),
|
|
||||||
encoded.type});
|
|
||||||
vertices[encoded.to.AsUint()].in.push_back(
|
|
||||||
{edge_address, storage::VertexAddress(encoded.from.AsUint(), 0),
|
|
||||||
encoded.type});
|
|
||||||
}
|
|
||||||
for (auto vertex_pair : vertices) {
|
for (auto vertex_pair : vertices) {
|
||||||
auto &vertex = vertex_pair.second;
|
auto &vertex = vertex_pair.second;
|
||||||
// write node
|
encoder.WriteVertex(vertex);
|
||||||
encoder.WriteRAW(
|
|
||||||
utils::UnderlyingCast(communication::bolt::Marker::TinyStruct) + 3);
|
|
||||||
encoder.WriteRAW(
|
|
||||||
utils::UnderlyingCast(communication::bolt::Signature::Node));
|
|
||||||
|
|
||||||
encoder.WriteInt(vertex.gid);
|
|
||||||
auto &labels = vertex.labels;
|
|
||||||
std::vector<communication::bolt::Value> transformed;
|
|
||||||
std::transform(labels.begin(), labels.end(),
|
|
||||||
std::back_inserter(transformed),
|
|
||||||
[](const std::string &str) {
|
|
||||||
return communication::bolt::Value(str);
|
|
||||||
});
|
|
||||||
encoder.WriteList(transformed);
|
|
||||||
encoder.WriteMap(vertex.properties);
|
|
||||||
|
|
||||||
encoder.WriteInt(vertex.cypher_id);
|
|
||||||
|
|
||||||
encoder.WriteInt(vertex.in.size());
|
|
||||||
for (auto edge : vertex.in) {
|
|
||||||
encoder.WriteInt(edge.address.raw());
|
|
||||||
encoder.WriteInt(edge.vertex.raw());
|
|
||||||
encoder.WriteString(edge.type);
|
|
||||||
}
|
|
||||||
encoder.WriteInt(vertex.out.size());
|
|
||||||
for (auto edge : vertex.out) {
|
|
||||||
encoder.WriteInt(edge.address.raw());
|
|
||||||
encoder.WriteInt(edge.vertex.raw());
|
|
||||||
encoder.WriteString(edge.type);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for (auto edge_pair : edges) {
|
for (auto edge_pair : edges) {
|
||||||
auto &edge = edge_pair.second;
|
auto &edge = edge_pair.second;
|
||||||
// write relationship
|
encoder.WriteEdge(edge);
|
||||||
encoder.WriteRAW(
|
|
||||||
utils::UnderlyingCast(communication::bolt::Marker::TinyStruct) + 5);
|
|
||||||
encoder.WriteRAW(
|
|
||||||
utils::UnderlyingCast(communication::bolt::Signature::Relationship));
|
|
||||||
encoder.WriteInt(edge.id.AsInt());
|
|
||||||
encoder.WriteInt(edge.from.AsInt());
|
|
||||||
encoder.WriteInt(edge.to.AsInt());
|
|
||||||
encoder.WriteString(edge.type);
|
|
||||||
encoder.WriteMap(edge.properties);
|
|
||||||
|
|
||||||
// cypher_id
|
|
||||||
encoder.WriteInt(edge.id.AsInt());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
buffer.WriteValue(node_count);
|
buffer.WriteValue(node_count);
|
||||||
@ -557,10 +494,8 @@ std::string GetOutputPath() {
|
|||||||
} catch (const std::experimental::filesystem::filesystem_error &error) {
|
} catch (const std::experimental::filesystem::filesystem_error &error) {
|
||||||
LOG(FATAL) << error.what();
|
LOG(FATAL) << error.what();
|
||||||
}
|
}
|
||||||
int worker_id = 0;
|
|
||||||
// TODO(dgleich): Remove this transaction id hack
|
|
||||||
return std::string(
|
return std::string(
|
||||||
durability::MakeSnapshotPath(durability_dir, worker_id, 0));
|
durability::MakeSnapshotPath(durability_dir, 0));
|
||||||
}
|
}
|
||||||
|
|
||||||
int main(int argc, char *argv[]) {
|
int main(int argc, char *argv[]) {
|
||||||
|
@ -23,7 +23,7 @@ class RecoveryTest : public ::testing::Test {
|
|||||||
std::string durability_dir(FLAGS_durability_dir);
|
std::string durability_dir(FLAGS_durability_dir);
|
||||||
durability::RecoveryData recovery_data;
|
durability::RecoveryData recovery_data;
|
||||||
durability::RecoverOnlySnapshot(durability_dir, &db_, &recovery_data,
|
durability::RecoverOnlySnapshot(durability_dir, &db_, &recovery_data,
|
||||||
std::experimental::nullopt, 0);
|
std::experimental::nullopt);
|
||||||
durability::RecoveryTransactions recovery_transactions(&db_);
|
durability::RecoveryTransactions recovery_transactions(&db_);
|
||||||
durability::RecoverWal(durability_dir, &db_, &recovery_data,
|
durability::RecoverWal(durability_dir, &db_, &recovery_data,
|
||||||
&recovery_transactions);
|
&recovery_transactions);
|
||||||
|
Loading…
Reference in New Issue
Block a user