Serialize storage and durability via SLK

Reviewers: mferencevic, msantl, ipaljak

Reviewed By: mferencevic

Subscribers: pullbot

Differential Revision: https://phabricator.memgraph.io/D1759
This commit is contained in:
Teon Banek 2018-12-07 14:02:53 +01:00
parent e70ac03607
commit f5b39cfc41
12 changed files with 431 additions and 114 deletions

View File

@ -152,6 +152,7 @@ set(mg_distributed_sources
storage/common/locking/record_lock.cpp
storage/common/types/property_value.cpp
storage/common/types/property_value_store.cpp
storage/common/types/slk.cpp
storage/distributed/edge_accessor.cpp
storage/distributed/record_accessor.cpp
storage/distributed/rpc/serialization.cpp
@ -275,6 +276,7 @@ set(mg_single_node_ha_sources
query/repl.cpp
query/typed_value.cpp
storage/common/types/property_value.cpp
storage/common/types/slk.cpp
storage/common/types/property_value_store.cpp
storage/common/locking/record_lock.cpp
storage/single_node_ha/edge_accessor.cpp

View File

@ -171,6 +171,6 @@ omitted in the comment.")
/// Applies CRUD delta to database accessor. Fails on other types of deltas
void Apply(GraphDbAccessor &dba) const;
cpp<#)
(:serialize (:capnp)))
(:serialize (:slk) (:capnp)))
(lcp:pop-namespace) ;; database

View File

@ -153,6 +153,6 @@ omitted in the comment.")
/// Applies CRUD delta to database accessor. Fails on other types of deltas
void Apply(GraphDbAccessor &dba) const;
cpp<#)
(:serialize (:capnp)))
(:serialize (:slk) (:capnp)))
(lcp:pop-namespace) ;; database

View File

@ -14,6 +14,6 @@ cpp<#
(lcp:define-struct log-entry ()
((deltas "std::vector<database::StateDelta>" :capnp-type "List(Database.StateDelta)"))
(:serialize (:capnp)))
(:serialize (:slk) (:capnp)))
(lcp:pop-namespace) ;; raft

View File

@ -1,109 +0,0 @@
#pragma once
#include "communication/rpc/serialization.hpp"
#include "communication/rpc/streams.hpp"
#include "storage/common/types/property_value.hpp"
namespace slk {
inline void Save(const PropertyValue &obj, Builder *builder) {
switch (obj.type()) {
case PropertyValue::Type::Null: {
uint8_t type = 0;
Save(type, builder);
return;
}
case PropertyValue::Type::Bool: {
uint8_t type = 1;
Save(type, builder);
Save(obj.Value<bool>(), builder);
return;
}
case PropertyValue::Type::Int: {
uint8_t type = 2;
Save(type, builder);
Save(obj.Value<int64_t>(), builder);
return;
}
case PropertyValue::Type::Double: {
uint8_t type = 3;
Save(type, builder);
Save(obj.Value<double>(), builder);
return;
}
case PropertyValue::Type::String: {
uint8_t type = 4;
Save(type, builder);
Save(obj.Value<std::string>(), builder);
return;
}
case PropertyValue::Type::List: {
uint8_t type = 5;
Save(type, builder);
Save(obj.Value<std::vector<PropertyValue>>(), builder);
return;
}
case PropertyValue::Type::Map: {
uint8_t type = 6;
Save(type, builder);
Save(obj.Value<std::map<std::string, PropertyValue>>(), builder);
return;
}
}
}
inline void Load(PropertyValue *obj, Reader *reader) {
uint8_t type = 0;
Load(&type, reader);
switch (type) {
// Null
case 0: {
*obj = PropertyValue();
return;
}
// Bool
case 1: {
bool value = false;
Load(&value, reader);
*obj = PropertyValue(value);
return;
}
// Int
case 2: {
int64_t value = 0;
Load(&value, reader);
*obj = PropertyValue(value);
return;
}
// Double
case 3: {
double value = 0.0;
Load(&value, reader);
*obj = PropertyValue(value);
return;
}
// String
case 4: {
std::string value;
Load(&value, reader);
*obj = PropertyValue(std::move(value));
return;
}
// List
case 5: {
std::vector<PropertyValue> value;
Load(&value, reader);
*obj = PropertyValue(std::move(value));
return;
}
// Map
case 6: {
std::map<std::string, PropertyValue> value;
Load(&value, reader);
*obj = PropertyValue(std::move(value));
return;
}
// Invalid type
default: { throw SlkDecodeException("Couldn't load property value!"); }
}
}
} // namespace slk

View File

@ -0,0 +1,126 @@
#include "storage/common/types/slk.hpp"
namespace slk {
void Save(const PropertyValue &value, slk::Builder *builder) {
switch (value.type()) {
case PropertyValue::Type::Null:
slk::Save(static_cast<uint8_t>(0), builder);
return;
case PropertyValue::Type::Bool:
slk::Save(static_cast<uint8_t>(1), builder);
slk::Save(value.Value<bool>(), builder);
return;
case PropertyValue::Type::Int:
slk::Save(static_cast<uint8_t>(2), builder);
slk::Save(value.Value<int64_t>(), builder);
return;
case PropertyValue::Type::Double:
slk::Save(static_cast<uint8_t>(3), builder);
slk::Save(value.Value<double>(), builder);
return;
case PropertyValue::Type::String:
slk::Save(static_cast<uint8_t>(4), builder);
slk::Save(value.Value<std::string>(), builder);
return;
case PropertyValue::Type::List: {
slk::Save(static_cast<uint8_t>(5), builder);
const auto &values = value.Value<std::vector<PropertyValue>>();
size_t size = values.size();
slk::Save(size, builder);
for (const auto &v : values) {
slk::Save(v, builder);
}
return;
}
case PropertyValue::Type::Map: {
slk::Save(static_cast<uint8_t>(6), builder);
const auto &map = value.Value<std::map<std::string, PropertyValue>>();
size_t size = map.size();
slk::Save(size, builder);
for (const auto &kv : map) {
slk::Save(kv, builder);
}
return;
}
}
}
void Load(PropertyValue *value, slk::Reader *reader) {
uint8_t type;
slk::Load(&type, reader);
switch (type) {
case static_cast<uint8_t>(0):
*value = PropertyValue::Null;
return;
case static_cast<uint8_t>(1): {
bool v;
slk::Load(&v, reader);
*value = v;
return;
}
case static_cast<uint8_t>(2): {
int64_t v;
slk::Load(&v, reader);
*value = v;
return;
}
case static_cast<uint8_t>(3): {
double v;
slk::Load(&v, reader);
*value = v;
return;
}
case static_cast<uint8_t>(4): {
std::string v;
slk::Load(&v, reader);
*value = std::move(v);
return;
}
case static_cast<uint8_t>(5): {
size_t size;
slk::Load(&size, reader);
std::vector<PropertyValue> list(size);
for (size_t i = 0; i < size; ++i) {
slk::Load(&list[i], reader);
}
*value = std::move(list);
return;
}
case static_cast<uint8_t>(6): {
size_t size;
slk::Load(&size, reader);
std::map<std::string, PropertyValue> map;
for (size_t i = 0; i < size; ++i) {
std::pair<std::string, PropertyValue> kv;
slk::Load(&kv, reader);
map.insert(kv);
}
*value = std::move(map);
return;
}
default:
throw slk::SlkDecodeException("Trying to load unknown PropertyValue!");
}
}
void Save(const PropertyValueStore &properties, slk::Builder *builder) {
size_t size = properties.size();
slk::Save(size, builder);
for (const auto &kv : properties) {
slk::Save(kv, builder);
}
}
void Load(PropertyValueStore *properties, slk::Reader *reader) {
properties->clear();
size_t size;
slk::Load(&size, reader);
for (size_t i = 0; i < size; ++i) {
std::pair<storage::Property, PropertyValue> kv;
slk::Load(&kv, reader);
properties->set(kv.first, kv.second);
}
}
} // namespace slk

View File

@ -0,0 +1,42 @@
#pragma once
#include "communication/rpc/serialization.hpp"
#include "communication/rpc/streams.hpp"
#include "storage/common/types/property_value.hpp"
#include "storage/common/types/property_value_store.hpp"
#include "storage/common/types/types.hpp"
namespace slk {
inline void Save(const storage::Label &common, slk::Builder *builder) {
slk::Save(common.id_, builder);
}
inline void Load(storage::Label *common, slk::Reader *reader) {
slk::Load(&common->id_, reader);
}
inline void Save(const storage::EdgeType &common, slk::Builder *builder) {
slk::Save(common.id_, builder);
}
inline void Load(storage::EdgeType *common, slk::Reader *reader) {
slk::Load(&common->id_, reader);
}
inline void Save(const storage::Property &common, slk::Builder *builder) {
slk::Save(common.id_, builder);
}
inline void Load(storage::Property *common, slk::Reader *reader) {
slk::Load(&common->id_, reader);
}
void Save(const PropertyValue &value, slk::Builder *builder);
void Load(PropertyValue *value, slk::Reader *reader);
void Save(const PropertyValueStore &properties, slk::Builder *builder);
void Load(PropertyValueStore *properties, slk::Reader *reader);
} // namespace slk

View File

@ -313,3 +313,213 @@ EdgeAccessor LoadEdgeAccessor(const capnp::EdgeAccessor::Reader &reader,
}
} // namespace storage
namespace slk {
void Save(const storage::SendVersions &versions, slk::Builder *builder) {
uint8_t enum_value;
switch (versions) {
case storage::SendVersions::BOTH:
enum_value = 0;
break;
case storage::SendVersions::ONLY_OLD:
enum_value = 1;
break;
case storage::SendVersions::ONLY_NEW:
enum_value = 2;
break;
}
slk::Save(enum_value, builder);
}
void Load(storage::SendVersions *versions, slk::Reader *reader) {
uint8_t enum_value;
slk::Load(&enum_value, reader);
switch (enum_value) {
case static_cast<uint8_t>(0):
*versions = storage::SendVersions::BOTH;
break;
case static_cast<uint8_t>(1):
*versions = storage::SendVersions::ONLY_OLD;
break;
case static_cast<uint8_t>(2):
*versions = storage::SendVersions::ONLY_NEW;
break;
default:
throw slk::SlkDecodeException("Trying to load unknown enum value!");
}
}
namespace {
template <class TRecordAccessor>
void SaveRecordAccessor(const TRecordAccessor &accessor, slk::Builder *builder,
storage::SendVersions versions, int16_t worker_id) {
bool reconstructed = false;
if (!accessor.GetOld() && !accessor.GetNew()) {
reconstructed = true;
bool result = accessor.Reconstruct();
CHECK(result) << "Attempting to serialize an element not visible to "
"current transaction.";
}
auto save_optional_record = [builder, worker_id](const auto *rec) {
slk::Save(static_cast<bool>(rec), builder);
if (rec) {
slk::Save(*rec, builder, worker_id);
}
};
slk::Save(accessor.CypherId(), builder);
slk::Save(accessor.GlobalAddress(), builder);
// Save old record first
if (versions != storage::SendVersions::ONLY_NEW) {
save_optional_record(accessor.GetOld());
} else {
// Mark old record as empty
slk::Save(false, builder);
}
// Save new record
if (versions != storage::SendVersions::ONLY_OLD) {
if (!reconstructed && !accessor.GetNew()) {
bool result = accessor.Reconstruct();
CHECK(result) << "Attempting to serialize an element not visible to "
"current transaction.";
}
save_optional_record(accessor.GetNew());
} else {
// Mark new record as empty
slk::Save(false, builder);
}
}
} // namespace
void Save(const VertexAccessor &value, slk::Builder *builder,
storage::SendVersions versions, int16_t worker_id) {
SaveRecordAccessor(value, builder, versions, worker_id);
}
VertexAccessor LoadVertexAccessor(slk::Reader *reader,
database::GraphDbAccessor *dba,
distributed::DataManager *data_manager) {
auto load_optional_record = [reader]() {
std::unique_ptr<Vertex> rec;
bool has_ptr;
slk::Load(&has_ptr, reader);
if (has_ptr) {
rec = std::make_unique<Vertex>();
slk::Load(rec.get(), reader);
}
return rec;
};
int64_t cypher_id;
slk::Load(&cypher_id, reader);
storage::VertexAddress global_address;
slk::Load(&global_address, reader);
auto old_record = load_optional_record();
auto new_record = load_optional_record();
data_manager->Emplace(
dba->transaction_id(), global_address.gid(),
distributed::CachedRecordData<Vertex>(cypher_id, std::move(old_record),
std::move(new_record)));
return VertexAccessor(global_address, *dba);
}
void Save(const EdgeAccessor &value, slk::Builder *builder,
storage::SendVersions versions, int16_t worker_id) {
SaveRecordAccessor(value, builder, versions, worker_id);
}
EdgeAccessor LoadEdgeAccessor(slk::Reader *reader,
database::GraphDbAccessor *dba,
distributed::DataManager *data_manager) {
auto load_optional_record = [reader]() {
std::unique_ptr<Edge> rec;
bool has_ptr;
slk::Load(&has_ptr, reader);
if (has_ptr) {
slk::Load(&rec, reader);
}
return rec;
};
int64_t cypher_id;
slk::Load(&cypher_id, reader);
storage::EdgeAddress global_address;
slk::Load(&global_address, reader);
auto old_record = load_optional_record();
auto new_record = load_optional_record();
data_manager->Emplace(
dba->transaction_id(), global_address.gid(),
distributed::CachedRecordData<Edge>(cypher_id, std::move(old_record),
std::move(new_record)));
return EdgeAccessor(global_address, *dba);
}
namespace {
// TODO: WTF is with saving adress in a special way?
template <class TAddress>
void SaveAddress(TAddress address, slk::Builder *builder, int16_t worker_id) {
TAddress global_address =
address.is_local() ? TAddress(address.local()->gid_, worker_id) : address;
slk::Save(global_address.raw(), builder);
}
} // namespace
void Save(const Vertex &vertex, slk::Builder *builder, int16_t worker_id) {
auto save_edges = [builder, worker_id](const auto &edges) {
size_t size = edges.size();
slk::Save(size, builder);
for (const auto &edge : edges) {
SaveAddress(edge.vertex, builder, worker_id);
SaveAddress(edge.edge, builder, worker_id);
slk::Save(edge.edge_type, builder);
}
};
save_edges(vertex.out_);
save_edges(vertex.in_);
slk::Save(vertex.labels_, builder);
slk::Save(vertex.properties_, builder);
}
void Load(Vertex *vertex, slk::Reader *reader) {
auto load_edges = [reader]() {
Edges edges;
size_t size;
slk::Load(&size, reader);
for (size_t i = 0; i < size; ++i) {
storage::VertexAddress vertex_address;
slk::Load(&vertex_address, reader);
storage::EdgeAddress edge_address;
slk::Load(&edge_address, reader);
storage::EdgeType edge_type;
slk::Load(&edge_type, reader);
edges.emplace(vertex_address, edge_address, edge_type);
}
return edges;
};
vertex->out_ = load_edges();
vertex->in_ = load_edges();
slk::Load(&vertex->labels_, reader);
slk::Load(&vertex->properties_, reader);
}
void Save(const Edge &edge, slk::Builder *builder, int16_t worker_id) {
SaveAddress(edge.from_, builder, worker_id);
SaveAddress(edge.to_, builder, worker_id);
slk::Save(edge.edge_type_, builder);
slk::Save(edge.properties_, builder);
}
void Load(std::unique_ptr<Edge> *edge, slk::Reader *reader) {
storage::VertexAddress from;
slk::Load(&from, reader);
storage::VertexAddress to;
slk::Load(&to, reader);
storage::EdgeType edge_type;
slk::Load(&edge_type, reader);
*edge = std::make_unique<Edge>(from, to, edge_type);
slk::Load(&(*edge)->properties_, reader);
}
} // namespace slk

View File

@ -1,7 +1,9 @@
#pragma once
#include "communication/rpc/serialization.hpp"
#include "storage/common/types/property_value.hpp"
#include "storage/common/types/property_value_store.hpp"
#include "storage/common/types/slk.hpp"
#include "storage/distributed/edge.hpp"
#include "storage/distributed/edge_accessor.hpp"
#include "storage/distributed/rpc/serialization.capnp.h"
@ -89,3 +91,45 @@ EdgeAccessor LoadEdgeAccessor(const capnp::EdgeAccessor::Reader &reader,
distributed::DataManager *data_manager);
} // namespace storage
namespace slk {
template <typename TLocalObj>
void Save(const storage::Address<TLocalObj> &address, slk::Builder *builder) {
// TODO: Is this even correct w.r.t. local and global address?
slk::Save(address.storage_, builder);
}
template <typename TLocalObj>
void Load(storage::Address<TLocalObj> *address, slk::Reader *reader) {
// TODO: Is this even correct w.r.t. local and global address?
slk::Load(&address->storage_, reader);
}
void Save(const storage::SendVersions &versions, slk::Builder *builder);
void Load(storage::SendVersions *versions, slk::Reader *reader);
void Save(const VertexAccessor &value, slk::Builder *builder,
storage::SendVersions versions, int16_t worker_id);
VertexAccessor LoadVertexAccessor(slk::Reader *reader,
database::GraphDbAccessor *dba,
distributed::DataManager *data_manager);
void Save(const EdgeAccessor &value, slk::Builder *builder,
storage::SendVersions versions, int16_t worker_id);
EdgeAccessor LoadEdgeAccessor(slk::Reader *reader,
database::GraphDbAccessor *dba,
distributed::DataManager *data_manager);
void Save(const Vertex &vertex, slk::Builder *builder, int16_t worker_id);
void Load(Vertex *vertex, slk::Reader *reader);
void Save(const Edge &edge, slk::Builder *builder, int16_t worker_id);
void Load(std::unique_ptr<Edge> *edge, slk::Reader *reader);
} // namespace slk

View File

@ -1,6 +1,8 @@
#pragma once
#include "communication/rpc/serialization.hpp"
#include "storage/common/types/property_value.hpp"
#include "storage/common/types/slk.hpp"
#include "storage/common/types/types.hpp"
#include "storage/single_node_ha/rpc/serialization.capnp.h"

View File

@ -216,7 +216,7 @@ add_unit_test(skiplist_suffix.cpp)
target_link_libraries(${test_prefix}skiplist_suffix mg-single-node kvstore_dummy_lib)
add_unit_test(slk_advanced.cpp)
target_link_libraries(${test_prefix}slk_advanced mg-single-node kvstore_dummy_lib)
target_link_libraries(${test_prefix}slk_advanced mg-distributed kvstore_dummy_lib)
# TODO (mferencevic): remove glog, gflags and mg-single-node
add_unit_test(slk_core.cpp)

View File

@ -1,6 +1,6 @@
#include <gtest/gtest.h>
#include "storage/common/types/property_value.slk.hpp"
#include "storage/common/types/slk.hpp"
TEST(SlkAdvanced, PropertyValueList) {
std::vector<PropertyValue> original{"hello world!", 5, 1.123423, true,