From 8dd2b8b1102d57f09f68281ed371485eae19fe10 Mon Sep 17 00:00:00 2001 From: Andi Skrgat Date: Tue, 16 May 2023 15:17:48 +0200 Subject: [PATCH] (de)serialization rocksdb unit tests --- src/storage/v2/disk/helper_storage.hpp | 30 +- src/storage/v2/disk/indices.cpp | 5 +- src/storage/v2/disk/rocksdb_storage.hpp | 3 + src/storage/v2/disk/storage.cpp | 139 +++------ src/storage/v2/disk/storage.hpp | 57 +--- src/utils/rocksdb.hpp | 95 ++++++ tests/unit/storage_rocks.cpp | 370 ++++++------------------ 7 files changed, 241 insertions(+), 458 deletions(-) diff --git a/src/storage/v2/disk/helper_storage.hpp b/src/storage/v2/disk/helper_storage.hpp index 0dfe3bab7..dcfefa781 100644 --- a/src/storage/v2/disk/helper_storage.hpp +++ b/src/storage/v2/disk/helper_storage.hpp @@ -70,12 +70,12 @@ class RocksDBStorage { // /// fetch the edge's source vertex by its GID // std::optional FromVertex(const query::EdgeAccessor &edge_acc, query::DbAccessor &dba) { - // return FindVertex(SerializeIdType(edge_acc.From().Gid()), dba); + // return FindVertex(utils::SerializeIdType(edge_acc.From().Gid()), dba); // } // /// fetch the edge's destination vertex by its GID // std::optional ToVertex(const query::EdgeAccessor &edge_acc, query::DbAccessor &dba) { - // return FindVertex(SerializeIdType(edge_acc.To().Gid()), dba); + // return FindVertex(utils::SerializeIdType(edge_acc.To().Gid()), dba); // } // /// VERTEX ACCESSOR FUNCTIONALITIES @@ -86,7 +86,7 @@ class RocksDBStorage { // /// other_vertex_gid | src_gid | 1 | ... // /// We use the firt way since this should be possible to optimize using Bloom filters and prefix search // std::vector OutEdges(const query::VertexAccessor &vertex_acc, query::DbAccessor &dba) { - // const auto vertex_acc_gid = SerializeIdType(vertex_acc.Gid()); + // const auto vertex_acc_gid = utils::SerializeIdType(vertex_acc.Gid()); // std::vector out_edges; // auto it = std::unique_ptr(db_->NewIterator(rocksdb::ReadOptions(), edge_chandle)); // for (it->SeekToFirst(); it->Valid(); it->Next()) { @@ -104,7 +104,7 @@ class RocksDBStorage { // /// dest_gid | other_verte_gid | 1 | ... // /// we use the second way since this should be possible to optimize using Bloom filters and prefix search. // std::vector InEdges(const query::VertexAccessor &vertex_acc, query::DbAccessor &dba) { - // const auto vertex_acc_gid = SerializeIdType(vertex_acc.Gid()); + // const auto vertex_acc_gid = utils::SerializeIdType(vertex_acc.Gid()); // std::vector in_edges; // auto it = std::unique_ptr(db_->NewIterator(rocksdb::ReadOptions(), edge_chandle)); // for (it->SeekToFirst(); it->Valid(); it->Next()) { @@ -187,13 +187,13 @@ class RocksDBStorage { // /// Properties are serialized as the value // void StoreVertex(const query::VertexAccessor &vertex_acc) { // AssertRocksDBStatus(db_->Put(rocksdb::WriteOptions(), vertex_chandle, SerializeVertex(vertex_acc), - // SerializeProperties(vertex_acc.PropertyStore()))); + // utils::SerializeProperties(vertex_acc.PropertyStore()))); // } // /// Store edge as two key-value entries in the RocksDB. // void StoreEdge(const query::EdgeAccessor &edge_acc) { // auto [src_dest_key, dest_src_key] = SerializeEdge(edge_acc); - // const std::string value = SerializeProperties(edge_acc.PropertyStore()); + // const std::string value = utils::SerializeProperties(edge_acc.PropertyStore()); // AssertRocksDBStatus(db_->Put(rocksdb::WriteOptions(), edge_chandle, src_dest_key, value)); // AssertRocksDBStatus(db_->Put(rocksdb::WriteOptions(), edge_chandle, dest_src_key, value)); // } @@ -268,10 +268,10 @@ class RocksDBStorage { // /// Serialization of properties is done by saving the property store buffer // /// If the data is stored in the local buffer of the property store, data from the buffer is copied to the string // /// If the data is stored in some external buffer, the data is read from that location and copied to the string - // inline std::string SerializeProperties(const auto &&properties) { return properties; } + // inline std::string utils::SerializeProperties(const auto &&properties) { return properties; } // /// Serialize labels delimitied by | to string - // std::string SerializeLabels(const auto &&labels) { + // std::string utils::SerializeLabels(const auto &&labels) { // if (labels.HasError() || (*labels).empty()) { // return ""; // } @@ -284,13 +284,13 @@ class RocksDBStorage { // } // /// Serializes id type to string - // inline std::string SerializeIdType(const auto &id) { return std::to_string(id.AsUint()); } + // inline std::string utils::SerializeIdType(const auto &id) { return std::to_string(id.AsUint()); } // /// Serialize vertex to string // /// The format: | label1,label2,label3 | gid // std::string SerializeVertex(const query::VertexAccessor &vertex_acc) { - // std::string result = SerializeLabels(vertex_acc.Labels(storage::View::OLD)) + "|"; - // result += SerializeIdType(vertex_acc.Gid()); + // std::string result = utils::SerializeLabels(vertex_acc.Labels(storage::View::OLD)) + "|"; + // result += utils::SerializeIdType(vertex_acc.Gid()); // return result; // } @@ -332,10 +332,10 @@ class RocksDBStorage { // // | from_gid | to_gid | direction | edge_type | edge_gid // std::pair SerializeEdge(const query::EdgeAccessor &edge_acc) { // // Serialized objects - // auto from_gid = SerializeIdType(edge_acc.From().Gid()); - // auto to_gid = SerializeIdType(edge_acc.To().Gid()); - // auto edge_type = SerializeIdType(edge_acc.EdgeType()); - // auto edge_gid = SerializeIdType(edge_acc.Gid()); + // auto from_gid = utils::SerializeIdType(edge_acc.From().Gid()); + // auto to_gid = utils::SerializeIdType(edge_acc.To().Gid()); + // auto edge_type = utils::SerializeIdType(edge_acc.EdgeType()); + // auto edge_gid = utils::SerializeIdType(edge_acc.Gid()); // // source->destination key // std::string src_dest_key = from_gid + "|"; // src_dest_key += to_gid + "|"; diff --git a/src/storage/v2/disk/indices.cpp b/src/storage/v2/disk/indices.cpp index 32d40f307..b06009639 100644 --- a/src/storage/v2/disk/indices.cpp +++ b/src/storage/v2/disk/indices.cpp @@ -54,8 +54,9 @@ // const auto &key = it->key().ToString(); // const auto vertex_parts = utils::Split(key, "|"); // if (const auto labels = utils::Split(vertex_parts[0], ","); -// // TODO: (andi): When you decouple SerializeIdType, modify this to_string call to use SerializeIdType. -// std::find(labels.begin(), labels.end(), std::to_string(label.AsUint())) != labels.end()) { +// // TODO: (andi): When you decouple utils::SerializeIdType, modify this to_string call to use +// utils::SerializeIdType. std::find(labels.begin(), labels.end(), std::to_string(label.AsUint())) != +// labels.end()) { // auto gid = storage::Gid::FromUint(std::stoull(vertex_parts[1])); // auto vertex_commit_ts = utils::ExtractTimestampFromDeserializedUserKey(key); // auto delta = CreateDeleteDeserializedObjectDelta(transaction, vertex_commit_ts); diff --git a/src/storage/v2/disk/rocksdb_storage.hpp b/src/storage/v2/disk/rocksdb_storage.hpp index 560254a61..5b553c52f 100644 --- a/src/storage/v2/disk/rocksdb_storage.hpp +++ b/src/storage/v2/disk/rocksdb_storage.hpp @@ -17,6 +17,8 @@ #include #include +#include "storage/v2/id_types.hpp" +#include "storage/v2/property_store.hpp" #include "utils/logging.hpp" namespace memgraph::storage { @@ -37,6 +39,7 @@ struct RocksDBStorage { rocksdb::ColumnFamilyHandle *edge_chandle = nullptr; }; +/// RocksDB comparator that compares keys with timestamps. class ComparatorWithU64TsImpl : public rocksdb::Comparator { public: explicit ComparatorWithU64TsImpl(); diff --git a/src/storage/v2/disk/storage.cpp b/src/storage/v2/disk/storage.cpp index 1a9755632..588d68abd 100644 --- a/src/storage/v2/disk/storage.cpp +++ b/src/storage/v2/disk/storage.cpp @@ -75,8 +75,6 @@ using OOMExceptionEnabler = utils::MemoryTracker::OutOfMemoryExceptionEnabler; namespace { -constexpr const char *outEdgeDirection = "0"; -constexpr const char *inEdgeDirection = "1"; constexpr const char *vertexHandle = "vertex"; constexpr const char *edgeHandle = "edge"; constexpr const char *main_storage_path = "./rocks_experiment"; @@ -266,84 +264,6 @@ DiskStorage::DiskAccessor::~DiskAccessor() { FinalizeTransaction(); } -// (De)serialization utilities - -/// TODO: (andi): Decouple this into some object/class related to rocksdb. -std::string DiskStorage::DiskAccessor::SerializeIdType(const auto &id) const { return std::to_string(id.AsUint()); } - -auto DiskStorage::DiskAccessor::DeserializeIdType(const std::string &str) { return Gid::FromUint(std::stoull(str)); } - -std::string DiskStorage::DiskAccessor::SerializeTimestamp(const uint64_t ts) { return std::to_string(ts); } - -std::string DiskStorage::DiskAccessor::SerializeLabels(const std::vector &labels) { - std::string result = std::to_string(labels[0].AsUint()); - std::string ser_labels = std::accumulate( - std::next(labels.begin()), labels.end(), result, - [](const std::string &join, const auto &label_id) { return join + "," + std::to_string(label_id.AsUint()); }); - return ser_labels; -} - -std::string DiskStorage::DiskAccessor::SerializeProperties(PropertyStore &properties) { - return properties.StringBuffer(); -} - -std::string DiskStorage::DiskAccessor::SerializeVertex(const Result> &labels, Gid gid) const { - std::string result = labels.HasError() || (*labels).empty() ? "" : SerializeLabels(*labels) + "|"; - result += SerializeIdType(gid); - return result; -} - -std::string DiskStorage::DiskAccessor::SerializeVertex(const Vertex &vertex) const { - std::string result = SerializeLabels(vertex.labels) + "|"; - result += SerializeIdType(vertex.gid); - return result; -} - -std::pair DiskStorage::DiskAccessor::SerializeEdge(EdgeAccessor *edge_acc) const { - // Serialized objects - auto from_gid = SerializeIdType(edge_acc->FromVertex().Gid()); - auto to_gid = SerializeIdType(edge_acc->ToVertex().Gid()); - auto edge_type = SerializeIdType(edge_acc->EdgeType()); - auto edge_gid = SerializeIdType(edge_acc->Gid()); - // source->destination key - std::string src_dest_key = from_gid + "|"; - src_dest_key += to_gid + "|"; - src_dest_key += outEdgeDirection; - src_dest_key += "|" + edge_type + "|"; - src_dest_key += edge_gid; - // destination->source key - std::string dest_src_key = to_gid + "|"; - dest_src_key += from_gid + "|"; - dest_src_key += inEdgeDirection; - dest_src_key += "|" + edge_type + "|"; - dest_src_key += edge_gid; - return {src_dest_key, dest_src_key}; -} - -std::pair DiskStorage::DiskAccessor::SerializeEdge(const Gid src_vertex_gid, - const Gid dest_vertex_gid, - EdgeTypeId edge_type_id, - const Edge *edge) const { - // Serialized objects - auto from_gid = SerializeIdType(src_vertex_gid); - auto to_gid = SerializeIdType(dest_vertex_gid); - auto edge_type = SerializeIdType(edge_type_id); - auto edge_gid = SerializeIdType(edge->gid); - // source->destination key - std::string src_dest_key = from_gid + "|"; - src_dest_key += to_gid + "|"; - src_dest_key += outEdgeDirection; - src_dest_key += "|" + edge_type + "|"; - src_dest_key += edge_gid; - // destination->source key - std::string dest_src_key = to_gid + "|"; - dest_src_key += from_gid + "|"; - dest_src_key += inEdgeDirection; - dest_src_key += "|" + edge_type + "|"; - dest_src_key += edge_gid; - return {src_dest_key, dest_src_key}; -} - std::optional DiskStorage::DiskAccessor::DeserializeVertex(const rocksdb::Slice &key, const rocksdb::Slice &value) { OOMExceptionEnabler oom_exception; @@ -371,7 +291,7 @@ std::optional DiskStorage::DiskAccessor::DeserializeVertex(const std::optional DiskStorage::DiskAccessor::DeserializeEdge(const rocksdb::Slice &key, const rocksdb::Slice &value) { const auto edge_parts = utils::Split(key.ToStringView(), "|"); - const Gid edge_gid = DeserializeIdType(edge_parts[4]); + const Gid edge_gid = utils::DeserializeIdType(edge_parts[4]); auto edge_acc = storage_->edges_.access(); auto res = edge_acc.find(edge_gid); @@ -392,8 +312,8 @@ std::optional DiskStorage::DiskAccessor::DeserializeEdge(const roc edge_parts); // load vertex accessors - auto from_acc = FindVertex(DeserializeIdType(from_gid), View::OLD); - auto to_acc = FindVertex(DeserializeIdType(to_gid), View::OLD); + auto from_acc = FindVertex(utils::DeserializeIdType(from_gid), View::OLD); + auto to_acc = FindVertex(utils::DeserializeIdType(to_gid), View::OLD); if (!from_acc || !to_acc) { throw utils::BasicException("Non-existing vertices found during edge deserialization"); } @@ -538,7 +458,7 @@ std::optional DiskStorage::DiskAccessor::FindVertex(storage::Gid for (it->SeekToFirst(); it->Valid(); it->Next()) { const auto &key = it->key(); // TODO(andi): If we change format of vertex serialization, change vertex_parts[1] to vertex_parts[0]. - if (const auto vertex_parts = utils::Split(key.ToString(), "|"); vertex_parts[1] == SerializeIdType(gid)) { + if (const auto vertex_parts = utils::Split(key.ToString(), "|"); vertex_parts[1] == utils::SerializeIdType(gid)) { return DeserializeVertex(key, it->value()); } } @@ -563,7 +483,7 @@ Result> DiskStorage::DiskAccessor::DeleteVertex(Ve CreateAndLinkDelta(&transaction_, vertex_ptr, Delta::RecreateObjectTag()); vertex_ptr->deleted = true; - vertices_to_delete_.emplace_back(SerializeVertex(*vertex_ptr)); + vertices_to_delete_.emplace_back(utils::SerializeVertex(*vertex_ptr)); return std::make_optional(vertex_ptr, &transaction_, &storage_->indices_, &storage_->constraints_, config_, true); @@ -633,7 +553,7 @@ DiskStorage::DiskAccessor::DetachDeleteVertex(VertexAccessor *vertex) { CreateAndLinkDelta(&transaction_, vertex_ptr, Delta::RecreateObjectTag()); vertex_ptr->deleted = true; - vertices_to_delete_.emplace_back(SerializeVertex(*vertex_ptr)); + vertices_to_delete_.emplace_back(utils::SerializeVertex(*vertex_ptr)); return std::make_optional( VertexAccessor{vertex_ptr, &transaction_, &storage_->indices_, &storage_->constraints_, config_, true}, @@ -657,18 +577,18 @@ void DiskStorage::DiskAccessor::PrefetchEdges(const auto &prefetch_edge_filter) /// TOOD(andi): Add support for fetching in edges only for one vertex. Currently, all in edges are fetched. void DiskStorage::DiskAccessor::PrefetchInEdges(const VertexAccessor &vertex_acc) { - PrefetchEdges( - [&vertex_acc, this](const std::string_view disk_edge_gid, const std::string_view disk_edge_direction) -> bool { - return disk_edge_gid == SerializeIdType(vertex_acc.Gid()) && disk_edge_direction == inEdgeDirection; - }); + PrefetchEdges([&vertex_acc](const std::string_view disk_edge_gid, + const std::string_view disk_edge_direction) -> bool { + return disk_edge_gid == utils::SerializeIdType(vertex_acc.Gid()) && disk_edge_direction == utils::inEdgeDirection; + }); } /// TODO(andi): Functionality of this method can probably be merged with the functionality of in-edges void DiskStorage::DiskAccessor::PrefetchOutEdges(const VertexAccessor &vertex_acc) { - PrefetchEdges( - [&vertex_acc, this](const std::string_view disk_edge_gid, const std::string_view disk_edge_direction) -> bool { - return disk_edge_gid == SerializeIdType(vertex_acc.Gid()) && disk_edge_direction == outEdgeDirection; - }); + PrefetchEdges([&vertex_acc](const std::string_view disk_edge_gid, + const std::string_view disk_edge_direction) -> bool { + return disk_edge_gid == utils::SerializeIdType(vertex_acc.Gid()) && disk_edge_direction == utils::outEdgeDirection; + }); } Result DiskStorage::DiskAccessor::CreateEdge(VertexAccessor *from, VertexAccessor *to, @@ -843,7 +763,7 @@ Result DiskStorage::DiskAccessor::CreateEdge(VertexAccessor *from, EdgeRef edge(gid); if (config_.properties_on_edges) { auto acc = storage_->edges_.access(); - auto delta = CreateDeleteObjectDelta(&transaction_); + auto *delta = CreateDeleteObjectDelta(&transaction_); auto [it, inserted] = acc.insert(Edge(gid, delta)); MG_ASSERT(inserted, "The edge must be inserted here!"); MG_ASSERT(it != acc.end(), "Invalid Edge accessor!"); @@ -921,7 +841,8 @@ Result> DiskStorage::DiskAccessor::DeleteEdge(EdgeAc auto op1 = delete_edge_from_storage(to_vertex, &from_vertex->out_edges); auto op2 = delete_edge_from_storage(from_vertex, &to_vertex->in_edges); - auto [src_dest_del_key, dest_src_del_key] = SerializeEdge(from_vertex->gid, to_vertex->gid, edge_type, edge_ref.ptr); + auto [src_dest_del_key, dest_src_del_key] = + utils::SerializeEdge(from_vertex->gid, to_vertex->gid, edge_type, edge_ref.ptr); edges_to_delete_.emplace_back(src_dest_del_key); edges_to_delete_.emplace_back(dest_src_del_key); @@ -990,18 +911,20 @@ void DiskStorage::DiskAccessor::FlushCache() { write_options.timestamp = &ts; for (Vertex &vertex : vertex_acc) { logging::AssertRocksDBStatus(storage_->kvstore_->db_->Put(write_options, storage_->kvstore_->vertex_chandle, - SerializeVertex(vertex), - SerializeProperties(vertex.properties))); - spdlog::debug("rocksdb: Saved vertex with key {} and ts {}", SerializeVertex(vertex), *commit_timestamp_); + utils::SerializeVertex(vertex), + utils::SerializeProperties(vertex.properties))); + spdlog::debug("rocksdb: Saved vertex with key {} and ts {}", utils::SerializeVertex(vertex), *commit_timestamp_); spdlog::debug("Vertex {} has {} out edges", vertex.gid.AsUint(), vertex.out_edges.size()); for (auto &edge_entry : vertex.out_edges) { Edge *edge_ptr = std::get<2>(edge_entry).ptr; auto [src_dest_key, dest_src_key] = - SerializeEdge(vertex.gid, std::get<1>(edge_entry)->gid, std::get<0>(edge_entry), edge_ptr); - logging::AssertRocksDBStatus(storage_->kvstore_->db_->Put( - write_options, storage_->kvstore_->edge_chandle, src_dest_key, SerializeProperties(edge_ptr->properties))); - logging::AssertRocksDBStatus(storage_->kvstore_->db_->Put( - write_options, storage_->kvstore_->edge_chandle, dest_src_key, SerializeProperties(edge_ptr->properties))); + utils::SerializeEdge(vertex.gid, std::get<1>(edge_entry)->gid, std::get<0>(edge_entry), edge_ptr); + logging::AssertRocksDBStatus(storage_->kvstore_->db_->Put(write_options, storage_->kvstore_->edge_chandle, + src_dest_key, + utils::SerializeProperties(edge_ptr->properties))); + logging::AssertRocksDBStatus(storage_->kvstore_->db_->Put(write_options, storage_->kvstore_->edge_chandle, + dest_src_key, + utils::SerializeProperties(edge_ptr->properties))); spdlog::debug("rocksdb: Saved edge with key {} and ts {}", src_dest_key, *commit_timestamp_); spdlog::debug("rocksdb: Saved edge with key {} and ts {}", dest_src_key, *commit_timestamp_); num_ser_edges++; @@ -1332,7 +1255,10 @@ void DiskStorage::DiskAccessor::FinalizeTransaction() { // this should be handled on an above level of abstraction std::optional DiskStorage::DiskAccessor::GetTransactionId() const { - throw utils::NotYetImplemented("GetTransactionId"); + if (is_transaction_active_) { + return transaction_.transaction_id.load(std::memory_order_acquire); + } + return {}; } // this should be handled on an above level of abstraction @@ -1380,7 +1306,8 @@ utils::BasicResult DiskStorage::CreateIndex( const auto &key = it->key(); const auto vertex_parts = utils::Split(key.ToStringView(), "|"); if (const auto labels = utils::Split(vertex_parts[0], ","); - // TODO: (andi): When you decouple SerializeIdType, modify this to_string call to use SerializeIdType. + // TODO: (andi): When you decouple utils::SerializeIdType, modify this to_string call to use + // utils::SerializeIdType. std::find(labels.begin(), labels.end(), std::to_string(label.AsUint())) != labels.end()) { spdlog::debug("Found vertex with gid {} for index creation", vertex_parts[1]); indexed_vertices.emplace_back(key.ToString(), it->value().ToString(), diff --git a/src/storage/v2/disk/storage.hpp b/src/storage/v2/disk/storage.hpp index 8e7fbed34..74cc907ab 100644 --- a/src/storage/v2/disk/storage.hpp +++ b/src/storage/v2/disk/storage.hpp @@ -209,6 +209,16 @@ class DiskStorage final : public Storage { std::optional GetTransactionId() const override; + /// Deserializes vertex from the string key and stores it into the vertices_ and lru_vertices_. + /// Properties are deserialized from the value. + /// The method should be called only when the vertex is not in the cache. + std::optional DeserializeVertex(const rocksdb::Slice &key, const rocksdb::Slice &value); + + /// Deserializes edge from the string key and stores it into the edges_ cache. + /// Properties are deserialized from the value. + /// The method should be called only when the edge is not in the cache. + std::optional DeserializeEdge(const rocksdb::Slice &key, const rocksdb::Slice &value); + private: /// TODO(andi): Consolidate this vertex creation methods and find from in-memory version where are they used. /// Used for deserialization of vertices and edges from KV store. @@ -229,53 +239,6 @@ class DiskStorage final : public Storage { Result CreateEdge(VertexAccessor *from, VertexAccessor *to, EdgeTypeId edge_type, storage::Gid gid, uint64_t edge_commit_ts); - // (De)serialization utility methods - - /// Serialize types defined with STORAGE_DEFINE_ID_TYPE - std::string SerializeIdType(const auto &id) const; - - /// Deserialize types defined with STORAGE_DEFINE_ID_TYPE - static auto DeserializeIdType(const std::string &str); - - /// Serialize timestamp to string - static std::string SerializeTimestamp(uint64_t ts); - - /// Serialize labels to string - static std::string SerializeLabels(const std::vector &labels); - - /// Uses the PropertyStore buffer to serialize properties to string. - static std::string SerializeProperties(PropertyStore &properties); - - /// Serialize vertex to string as a key in KV store - /// label1, label2 | GID | commit_timestamp - std::string SerializeVertex(const Result> &labels, Gid gid) const; - - /// Serialize vertex to string as a key in KV store - /// label1, label2 | GID | commit_timestamp - std::string SerializeVertex(const Vertex &vertex) const; - - /// Serialize edge as two KV entries - /// vertex_gid_1 | vertex_gid_2 | direction | edge_type | GID | commit_timestamp - std::pair SerializeEdge(EdgeAccessor *edge_acc) const; - - /// Serialize edge as two KV entries - /// vertex_gid_1 | vertex_gid_2 | direction | edge_type | GID | commit_timestamp - /// @tparam src_vertex_gid, dest_vertex_gid: Gid of the source and destination vertices - /// @tparam edge: Edge to be serialized - /// @tparam edge_type_id: EdgeTypeId of the edge - std::pair SerializeEdge(Gid src_vertex_gid, Gid dest_vertex_gid, EdgeTypeId edge_type_id, - const Edge *edge) const; - - /// Deserializes vertex from the string key and stores it into the vertices_ and lru_vertices_. - /// Properties are deserialized from the value. - /// The method should be called only when the vertex is not in the cache. - std::optional DeserializeVertex(const rocksdb::Slice &key, const rocksdb::Slice &value); - - /// Deserializes edge from the string key and stores it into the edges_ cache. - /// Properties are deserialized from the value. - /// The method should be called only when the edge is not in the cache. - std::optional DeserializeEdge(const rocksdb::Slice &key, const rocksdb::Slice &value); - /// Flushes vertices and edges to the disk with the commit timestamp. /// At the time of calling, the commit_timestamp_ must already exist. /// After this method, the vertex and edge caches are cleared. diff --git a/src/utils/rocksdb.hpp b/src/utils/rocksdb.hpp index 711e532f9..e10e6fb3a 100644 --- a/src/utils/rocksdb.hpp +++ b/src/utils/rocksdb.hpp @@ -12,12 +12,107 @@ #pragma once #include +#include #include #include +#include "storage/v2/edge_accessor.hpp" +#include "storage/v2/id_types.hpp" +#include "storage/v2/property_store.hpp" +#include "storage/v2/vertex.hpp" +#include "storage/v2/vertex_accessor.hpp" + namespace memgraph::utils { +constexpr const char *outEdgeDirection = "0"; +constexpr const char *inEdgeDirection = "1"; + +inline std::string SerializeIdType(const auto &id) { return std::to_string(id.AsUint()); } + +inline auto DeserializeIdType(const std::string &str) { return storage::Gid::FromUint(std::stoull(str)); } + +inline std::string SerializeTimestamp(const uint64_t ts) { return std::to_string(ts); } + +inline std::string SerializeLabels(const std::vector &labels) { + if (labels.empty()) { + return ""; + } + std::string result = std::to_string(labels[0].AsUint()); + std::string ser_labels = std::accumulate( + std::next(labels.begin()), labels.end(), result, + [](const std::string &join, const auto &label_id) { return join + "," + std::to_string(label_id.AsUint()); }); + return ser_labels; +} + +inline std::string SerializeProperties(storage::PropertyStore &properties) { return properties.StringBuffer(); } + +/// Serialize vertex to string as a key in KV store +/// label1, label2 | GID | commit_timestamp +inline std::string SerializeVertex(const storage::Result> &labels, storage::Gid gid) { + std::string result = labels.HasError() || (*labels).empty() ? "" : utils::SerializeLabels(*labels) + "|"; + result += utils::SerializeIdType(gid); + return result; +} + +/// Serialize vertex to string as a key in KV store +/// label1, label2 | GID | commit_timestamp +inline std::string SerializeVertex(const storage::Vertex &vertex) { + std::string result = utils::SerializeLabels(vertex.labels) + "|"; + result += utils::SerializeIdType(vertex.gid); + return result; +} + +/// Serialize edge as two KV entries +/// vertex_gid_1 | vertex_gid_2 | direction | edge_type | GID | commit_timestamp +inline std::pair SerializeEdge(storage::EdgeAccessor *edge_acc) { + // Serialized objects + auto from_gid = utils::SerializeIdType(edge_acc->FromVertex().Gid()); + auto to_gid = utils::SerializeIdType(edge_acc->ToVertex().Gid()); + auto edge_type = utils::SerializeIdType(edge_acc->EdgeType()); + auto edge_gid = utils::SerializeIdType(edge_acc->Gid()); + // source->destination key + std::string src_dest_key = from_gid + "|"; + src_dest_key += to_gid + "|"; + src_dest_key += outEdgeDirection; + src_dest_key += "|" + edge_type + "|"; + src_dest_key += edge_gid; + // destination->source key + std::string dest_src_key = to_gid + "|"; + dest_src_key += from_gid + "|"; + dest_src_key += inEdgeDirection; + dest_src_key += "|" + edge_type + "|"; + dest_src_key += edge_gid; + return {src_dest_key, dest_src_key}; +} + +/// Serialize edge as two KV entries +/// vertex_gid_1 | vertex_gid_2 | direction | edge_type | GID | commit_timestamp +/// @tparam src_vertex_gid, dest_vertex_gid: Gid of the source and destination vertices +/// @tparam edge: Edge to be serialized +/// @tparam edge_type_id: EdgeTypeId of the edge +inline std::pair SerializeEdge(storage::Gid src_vertex_gid, storage::Gid dest_vertex_gid, + storage::EdgeTypeId edge_type_id, const storage::Edge *edge) { + // Serialized objects + auto from_gid = utils::SerializeIdType(src_vertex_gid); + auto to_gid = utils::SerializeIdType(dest_vertex_gid); + auto edge_type = utils::SerializeIdType(edge_type_id); + auto edge_gid = utils::SerializeIdType(edge->gid); + // source->destination key + std::string src_dest_key = from_gid + "|"; + src_dest_key += to_gid + "|"; + src_dest_key += outEdgeDirection; + src_dest_key += "|" + edge_type + "|"; + src_dest_key += edge_gid; + // destination->source key + std::string dest_src_key = to_gid + "|"; + dest_src_key += from_gid + "|"; + dest_src_key += inEdgeDirection; + dest_src_key += "|" + edge_type + "|"; + dest_src_key += edge_gid; + return {src_dest_key, dest_src_key}; +} + /// TODO: (andi): This can potentially be a problem on big-endian machines. inline void PutFixed64(std::string *dst, uint64_t value) { dst->append(const_cast(reinterpret_cast(&value)), sizeof(value)); diff --git a/tests/unit/storage_rocks.cpp b/tests/unit/storage_rocks.cpp index 01e847b08..57a98f028 100644 --- a/tests/unit/storage_rocks.cpp +++ b/tests/unit/storage_rocks.cpp @@ -11,7 +11,9 @@ #include #include +#include #include +#include #include #include "query/common.hpp" @@ -24,300 +26,92 @@ #include "storage/v2/storage.hpp" #include "storage/v2/vertex_accessor.hpp" #include "storage/v2/view.hpp" +#include "utils/rocksdb.hpp" -// class RocksDBStorageTest : public ::testing::TestWithParam { -// public: -// ~RocksDBStorageTest() { db.Clear(); } +class RocksDBStorageTest : public ::testing::TestWithParam { + public: + RocksDBStorageTest() { storage = std::unique_ptr(new memgraph::storage::DiskStorage()); } -// protected: -// memgraph::storage::rocks::RocksDBStorage db; -// memgraph::storage::Storage storage; -// }; + ~RocksDBStorageTest() override {} -// TEST_F(RocksDBStorageTest, SerializeVertexGID) { -// // empty vertices, only gid is serialized -// auto storage_dba = storage.Access(memgraph::storage::IsolationLevel::READ_UNCOMMITTED); -// memgraph::query::DbAccessor dba(&storage_dba); -// std::unordered_set gids; -// for (uint64_t i = 0; i < 5; ++i) { -// gids.insert(i); -// auto impl = dba.InsertVertex(); -// impl.SetGid(memgraph::storage::Gid::FromUint(i)); -// db.StoreVertex(impl); -// } -// // load vertices from disk -// auto loaded_vertices = db.Vertices(dba); -// ASSERT_EQ(loaded_vertices.size(), 5); -// for (const auto &vertex_acc : loaded_vertices) { -// ASSERT_TRUE(gids.contains(vertex_acc.Gid().AsUint())); -// } -// } + protected: + std::unique_ptr storage; +}; -// TEST_F(RocksDBStorageTest, SerializeVertexGIDLabels) { -// // serialize vertex's gid with its single label -// auto storage_dba = storage.Access(memgraph::storage::IsolationLevel::READ_UNCOMMITTED); -// memgraph::query::DbAccessor dba(&storage_dba); -// // save vertices on disk -// std::unordered_set gids; -// std::vector label_ids{dba.NameToLabel("Player"), dba.NameToLabel("Person"), -// dba.NameToLabel("Ball")}; -// for (int i = 0; i < 5; ++i) { -// gids.insert(i); -// auto impl = dba.InsertVertex(); -// impl.SetGid(memgraph::storage::Gid::FromUint(i)); -// impl.AddLabel(label_ids[i % 3]); -// db.StoreVertex(impl); -// } -// // load vertices from disk -// auto loaded_vertices = db.Vertices(dba); -// ASSERT_EQ(loaded_vertices.size(), 5); -// for (const auto &vertex_acc : loaded_vertices) { -// ASSERT_TRUE(gids.contains(vertex_acc.Gid().AsUint())); -// auto labels = vertex_acc.Labels(memgraph::storage::View::OLD); -// ASSERT_EQ(labels->size(), 1); -// ASSERT_TRUE(std::all_of(labels->begin(), labels->end(), [&label_ids](const auto &label_id) { -// return std::find(label_ids.begin(), label_ids.end(), label_id) != label_ids.end(); -// })); -// } -// } +/// Serialize vertex which contains only GID. +TEST_F(RocksDBStorageTest, SerializeVertexGID) { + auto acc = storage->Access(); + auto vertex = acc->CreateVertex(); + auto gid = vertex.Gid(); + ASSERT_EQ(memgraph::utils::SerializeVertex(*vertex.vertex_), "|" + memgraph::utils::SerializeIdType(gid)); +} -// TEST_F(RocksDBStorageTest, SerializeVertexGIDMutlipleLabels) { -// // serialize vertex's gid with multiple labels it contains -// auto storage_dba = storage.Access(memgraph::storage::IsolationLevel::READ_UNCOMMITTED); -// memgraph::query::DbAccessor dba(&storage_dba); -// // save vertices on disk -// std::unordered_set gids; -// std::vector label_ids{dba.NameToLabel("Player"), dba.NameToLabel("Person"), -// dba.NameToLabel("Ball")}; -// for (int i = 0; i < 5; ++i) { -// gids.insert(i); -// auto impl = dba.InsertVertex(); -// impl.SetGid(memgraph::storage::Gid::FromUint(i)); -// impl.AddLabel(label_ids[i % 3]); -// impl.AddLabel(label_ids[(i + 1) % 3]); -// db.StoreVertex(impl); -// } -// // load vertices from disk -// auto loaded_vertices = db.Vertices(dba); -// ASSERT_EQ(loaded_vertices.size(), 5); -// for (const auto &vertex_acc : loaded_vertices) { -// ASSERT_TRUE(gids.contains(vertex_acc.Gid().AsUint())); -// auto labels = vertex_acc.Labels(memgraph::storage::View::OLD); -// ASSERT_EQ(labels->size(), 2); -// ASSERT_TRUE(std::all_of(labels->begin(), labels->end(), [&label_ids](const auto &label_id) { -// return std::find(label_ids.begin(), label_ids.end(), label_id) != label_ids.end(); -// })); -// } -// } +/// Serialize vertex with gid and its single label. +TEST_F(RocksDBStorageTest, SerializeVertexGIDLabels) { + auto acc = storage->Access(); + auto vertex = acc->CreateVertex(); + auto ser_player_label = acc->NameToLabel("Player"); + auto player_result = vertex.AddLabel(ser_player_label); + auto gid = vertex.Gid(); + ASSERT_EQ(memgraph::utils::SerializeVertex(*vertex.vertex_), + std::to_string(ser_player_label.AsInt()) + "|" + memgraph::utils::SerializeIdType(gid)); +} -// TEST_F(RocksDBStorageTest, GetVerticesByLabel) { -// // search vertices by label -// auto storage_dba = storage.Access(memgraph::storage::IsolationLevel::READ_UNCOMMITTED); -// memgraph::query::DbAccessor dba(&storage_dba); -// // prepare labels -// std::vector label_ids{dba.NameToLabel("Player"), dba.NameToLabel("Player"), -// dba.NameToLabel("Ball")}; -// // insert vertices -// for (int i = 0; i < 5; ++i) { -// auto impl = dba.InsertVertex(); -// impl.AddLabel(label_ids[i % 3]); -// db.StoreVertex(impl); -// } -// // load vertices from disk -// auto player_vertices = db.Vertices(dba, dba.NameToLabel("Player")); -// auto ball_vertices = db.Vertices(dba, dba.NameToLabel("Ball")); -// ASSERT_EQ(player_vertices.size(), 4); -// ASSERT_EQ(ball_vertices.size(), 1); -// } +/// Serialize vertex with gid and its multiple labels. +TEST_F(RocksDBStorageTest, SerializeVertexGIDMultipleLabels) { + auto acc = storage->Access(); + auto vertex = acc->CreateVertex(); + auto ser_player_label = acc->NameToLabel("Player"); + auto ser_person_label = acc->NameToLabel("Person"); + auto ser_ball_label = acc->NameToLabel("Ball"); + // NOLINTNEXTLINE + auto player_res = vertex.AddLabel(ser_player_label); + auto person_res = vertex.AddLabel(ser_person_label); + auto ball_res = vertex.AddLabel(ser_ball_label); + auto gid = vertex.Gid(); + ASSERT_EQ(memgraph::utils::SerializeVertex(*vertex.vertex_), + std::to_string(ser_player_label.AsInt()) + "," + std::to_string(ser_person_label.AsInt()) + "," + + std::to_string(ser_ball_label.AsInt()) + "|" + memgraph::utils::SerializeIdType(gid)); +} -// TEST_F(RocksDBStorageTest, GetVerticesByProperty) { -// // search vertices by property value -// auto storage_dba = storage.Access(memgraph::storage::IsolationLevel::READ_UNCOMMITTED); -// memgraph::query::DbAccessor dba(&storage_dba); -// // prepare ssd properties -// std::map ssd_properties_1; -// ssd_properties_1.emplace(dba.NameToProperty("price"), memgraph::storage::PropertyValue(225.84)); -// std::map ssd_properties_2; -// ssd_properties_2.emplace(dba.NameToProperty("price"), memgraph::storage::PropertyValue(226.84)); -// // prepare hdd properties -// std::map hdd_properties_1; -// hdd_properties_1.emplace(dba.NameToProperty("price"), memgraph::storage::PropertyValue(125.84)); -// std::vector properties{ssd_properties_1, ssd_properties_2, hdd_properties_1, hdd_properties_1}; -// // insert vertices -// for (int i = 0; i < 4; ++i) { -// auto impl = dba.InsertVertex(); -// memgraph::query::MultiPropsInitChecked(&impl, properties[i]); -// db.StoreVertex(impl); -// } -// // load vertices from disk -// auto ssd_vertices_1 = db.Vertices(dba, dba.NameToProperty("price"), memgraph::storage::PropertyValue(225.84)); -// auto hdd_vertices = db.Vertices(dba, dba.NameToProperty("price"), memgraph::storage::PropertyValue(125.84)); -// auto hdd_vertices_non_existing = -// db.Vertices(dba, dba.NameToProperty("price"), memgraph::storage::PropertyValue(125.81)); -// ASSERT_EQ(ssd_vertices_1.size(), 1); -// ASSERT_EQ(hdd_vertices.size(), 2); -// ASSERT_EQ(hdd_vertices_non_existing.size(), 0); -// } +/// Serialize edge. +TEST_F(RocksDBStorageTest, SerializeEdge) { + auto acc = storage->Access(); + auto vertex1 = acc->CreateVertex(); + auto vertex2 = acc->CreateVertex(); + auto edge = acc->CreateEdge(&vertex1, &vertex2, acc->NameToEdgeType("KNOWS")); + auto gid = edge->Gid(); + auto ser_result = memgraph::utils::SerializeEdge(vertex1.Gid(), vertex2.Gid(), edge->EdgeType(), edge->edge_.ptr); + ASSERT_EQ(ser_result.first, + memgraph::utils::SerializeIdType(vertex1.Gid()) + "|" + memgraph::utils::SerializeIdType(vertex2.Gid()) + + "|0|" + std::to_string(edge->EdgeType().AsInt()) + "|" + memgraph::utils::SerializeIdType(gid)); + ASSERT_EQ(ser_result.second, + memgraph::utils::SerializeIdType(vertex2.Gid()) + "|" + memgraph::utils::SerializeIdType(vertex1.Gid()) + + "|1|" + std::to_string(edge->EdgeType().AsInt()) + "|" + memgraph::utils::SerializeIdType(gid)); +} -// TEST_F(RocksDBStorageTest, DeleteVertex) { -// // auto storage_dba = storage.Access(memgraph::storage::IsolationLevel::READ_UNCOMMITTE); -// auto storage_dba = storage.Access(); -// memgraph::query::DbAccessor dba(&storage_dba); -// std::map properties; -// // samo 1 property stane -// properties.emplace(dba.NameToProperty("sum"), memgraph::storage::PropertyValue("2TB")); -// properties.emplace(dba.NameToProperty("same_type"), memgraph::storage::PropertyValue(true)); -// // properties.emplace(dba.NameToProperty("cluster_price"), memgraph::storage::PropertyValue(2000.42)); -// // create vertex -// auto impl = dba.InsertVertex(); -// impl.AddLabel(dba.NameToLabel("Player")); -// memgraph::query::MultiPropsInitChecked(&impl, properties); -// db.StoreVertex(impl); -// // find vertex should work now -// ASSERT_TRUE(db.FindVertex(std::to_string(impl.Gid().AsUint()), dba).has_value()); -// db.FindVertex(std::to_string(impl.Gid().AsUint()), dba); -// // RocksDB doesn't physically delete entry so deletion will pass two times -// ASSERT_TRUE(db.DeleteVertex(impl).has_value()); -// ASSERT_TRUE(db.DeleteVertex(impl).has_value()); -// // second time you shouldn't be able to find the vertex -// ASSERT_FALSE(db.FindVertex(std::to_string(impl.Gid().AsUint()), dba).has_value()); -// } +TEST_F(RocksDBStorageTest, DeserializeVertex) { + // NOTE: This test would fail in the case of snaphsot isolation because of the way in which RocksDB + // serializes commit timestamp. + auto serialized_vertex = "1|1"; + auto acc = storage->Access(memgraph::storage::IsolationLevel::READ_UNCOMMITTED); + auto acc_ptr = static_cast(acc.get()); + auto vertex = acc_ptr->DeserializeVertex(serialized_vertex, "garbage"); + ASSERT_EQ(vertex->Gid().AsInt(), 1); + ASSERT_EQ(*vertex->HasLabel(memgraph::storage::LabelId::FromUint(1), memgraph::storage::View::OLD), true); +} -// TEST_F(RocksDBStorageTest, SerializeVertexGIDProperties) { -// // serializes vertex's gid, multiple labels and properties -// auto storage_dba = storage.Access(memgraph::storage::IsolationLevel::READ_UNCOMMITTED); -// memgraph::query::DbAccessor dba(&storage_dba); -// // prepare labels -// std::vector label_ids{dba.NameToLabel("Player"), dba.NameToLabel("Person"), -// dba.NameToLabel("Ball")}; -// // prepare properties -// std::map properties; -// properties.emplace(dba.NameToProperty("name"), memgraph::storage::PropertyValue("disk")); -// properties.emplace(dba.NameToProperty("memory"), memgraph::storage::PropertyValue("1TB")); -// properties.emplace(dba.NameToProperty("price"), memgraph::storage::PropertyValue(1000.21)); -// properties.emplace(dba.NameToProperty("price2"), memgraph::storage::PropertyValue(1000.212)); -// // gids -// std::unordered_set gids; -// for (int i = 0; i < 5; ++i) { -// gids.insert(i); -// auto impl = dba.InsertVertex(); -// impl.SetGid(memgraph::storage::Gid::FromUint(i)); -// impl.AddLabel(label_ids[i % 3]); -// impl.AddLabel(label_ids[(i + 1) % 3]); -// memgraph::query::MultiPropsInitChecked(&impl, properties); -// db.StoreVertex(impl); -// } -// // load vertices from disk -// auto loaded_vertices = db.Vertices(dba); -// ASSERT_EQ(loaded_vertices.size(), 5); -// for (const auto &vertex_acc : loaded_vertices) { -// ASSERT_TRUE(gids.contains(vertex_acc.Gid().AsUint())); -// // labels -// auto labels = vertex_acc.Labels(memgraph::storage::View::OLD); -// ASSERT_EQ(labels->size(), 2); -// ASSERT_TRUE(std::all_of(labels->begin(), labels->end(), [&label_ids](const auto &label_id) { -// return std::find(label_ids.begin(), label_ids.end(), label_id) != label_ids.end(); -// })); -// // check properties -// auto props = vertex_acc.Properties(memgraph::storage::View::OLD); -// ASSERT_FALSE(props.HasError()); -// auto prop_name = vertex_acc.GetProperty(memgraph::storage::View::OLD, dba.NameToProperty("name")); -// auto prop_memory = vertex_acc.GetProperty(memgraph::storage::View::OLD, dba.NameToProperty("memory")); -// auto prop_price = vertex_acc.GetProperty(memgraph::storage::View::OLD, dba.NameToProperty("price")); -// auto prop_unexisting = vertex_acc.GetProperty(memgraph::storage::View::OLD, dba.NameToProperty("random")); -// ASSERT_TRUE(prop_name->IsString()); -// ASSERT_EQ(prop_name->ValueString(), "disk"); -// ASSERT_TRUE(prop_memory->IsString()); -// ASSERT_EQ(prop_memory->ValueString(), "1TB"); -// ASSERT_TRUE(prop_price->IsDouble()); -// ASSERT_DOUBLE_EQ(prop_price->ValueDouble(), 1000.21); -// ASSERT_TRUE(prop_unexisting->IsNull()); -// } -// } - -// TEST_F(RocksDBStorageTest, SerializeEdge) { -// // create two vertices and edge between them -// // search by one of the vertices, return edge -// // check deserialization for both vertices and edge -// auto storage_dba = storage.Access(memgraph::storage::IsolationLevel::READ_UNCOMMITTED); -// memgraph::query::DbAccessor dba(&storage_dba); -// std::vector label_ids{dba.NameToLabel("Player"), dba.NameToLabel("Referee")}; -// std::map properties_1; -// properties_1.emplace(dba.NameToProperty("price"), memgraph::storage::PropertyValue(221.84)); -// std::map properties_2; -// properties_2.emplace(dba.NameToProperty("price"), memgraph::storage::PropertyValue(222.84)); -// std::vector properties{properties_1, properties_2}; -// for (int i = 0; i < 2; ++i) { -// auto impl = dba.InsertVertex(); -// impl.AddLabel(label_ids[i]); -// memgraph::query::MultiPropsInitChecked(&impl, properties[i]); -// db.StoreVertex(impl); -// } -// // prepare edge properties -// std::map edge_properties; -// edge_properties.emplace(dba.NameToProperty("sum"), memgraph::storage::PropertyValue("2TB")); -// edge_properties.emplace(dba.NameToProperty("same_type"), memgraph::storage::PropertyValue(true)); -// edge_properties.emplace(dba.NameToProperty("cluster_price"), memgraph::storage::PropertyValue(2000.42)); -// // Before inserting edge, find two vertices -// // find source vertex by the property -// auto src_vertices = db.Vertices(dba, dba.NameToProperty("price"), memgraph::storage::PropertyValue(221.84)); -// ASSERT_EQ(src_vertices.size(), 1); -// auto src_vertex = src_vertices[0]; -// // find destination vertex by the property -// auto dest_vertices = db.Vertices(dba, dba.NameToProperty("price"), memgraph::storage::PropertyValue(222.84)); -// ASSERT_EQ(dest_vertices.size(), 1); -// auto dest_vertex = dest_vertices[0]; -// // insert the edge -// uint64_t edge_gid = 2; -// auto edge_type_id = "CONNECTION"; -// auto impl_edge = dba.InsertEdge(&src_vertex, &dest_vertex, dba.NameToEdgeType(edge_type_id)); -// ASSERT_FALSE(impl_edge.HasError()); -// (*impl_edge).SetGid(memgraph::storage::Gid::FromUint(edge_gid)); -// memgraph::query::MultiPropsInitChecked(&*impl_edge, edge_properties); -// db.StoreEdge(*impl_edge); -// // Test out edges of the source vertex -// auto src_out_edges = db.OutEdges(src_vertex, dba); -// ASSERT_EQ(src_out_edges.size(), 1); -// auto src_out_edge = src_out_edges[0]; -// // test from edge accessor -// auto from_out_edge_acc = src_out_edge.From(); -// ASSERT_EQ(from_out_edge_acc.Gid(), src_vertex.Gid()); -// ASSERT_EQ(from_out_edge_acc.Labels(memgraph::storage::View::OLD)->size(), 1); -// ASSERT_EQ(from_out_edge_acc.Labels(memgraph::storage::View::OLD)->at(0), label_ids[0]); -// ASSERT_EQ(*from_out_edge_acc.Properties(memgraph::storage::View::OLD), properties_1); -// // test to edge accessor -// auto to_out_edge_acc = src_out_edge.To(); -// ASSERT_EQ(to_out_edge_acc.Gid(), dest_vertex.Gid()); -// ASSERT_EQ(to_out_edge_acc.Labels(memgraph::storage::View::OLD)->size(), 1); -// ASSERT_EQ(to_out_edge_acc.Labels(memgraph::storage::View::OLD)->at(0), label_ids[1]); -// ASSERT_EQ(*to_out_edge_acc.Properties(memgraph::storage::View::OLD), properties_2); -// // test edge accessor -// ASSERT_EQ(src_out_edge.Gid().AsUint(), edge_gid); -// ASSERT_EQ(src_out_edge.EdgeType(), dba.NameToEdgeType(edge_type_id)); -// ASSERT_EQ(*src_out_edge.Properties(memgraph::storage::View::OLD), edge_properties); -// // Test in edge of the destination vertex -// auto dest_in_edges = db.InEdges(dest_vertex, dba); -// ASSERT_EQ(dest_in_edges.size(), 1); -// auto dest_in_edge = dest_in_edges[0]; -// // test from edge accessor -// auto from_in_edge_acc = dest_in_edge.From(); -// ASSERT_EQ(from_in_edge_acc.Gid(), from_out_edge_acc.Gid()); -// ASSERT_EQ(from_in_edge_acc.Labels(memgraph::storage::View::OLD)->size(), 1); -// ASSERT_EQ(from_in_edge_acc.Labels(memgraph::storage::View::OLD)->at(0), -// from_out_edge_acc.Labels(memgraph::storage::View::OLD)->at(0)); -// ASSERT_EQ(*from_in_edge_acc.Properties(memgraph::storage::View::OLD), -// *from_out_edge_acc.Properties(memgraph::storage::View::OLD)); -// // test in edge accessors -// auto to_in_edge_acc = dest_in_edge.To(); -// ASSERT_EQ(to_in_edge_acc.Gid(), to_out_edge_acc.Gid()); -// ASSERT_EQ(to_in_edge_acc.Labels(memgraph::storage::View::OLD)->size(), 1); -// ASSERT_EQ(to_in_edge_acc.Labels(memgraph::storage::View::OLD)->at(0), -// to_out_edge_acc.Labels(memgraph::storage::View::OLD)->at(0)); -// ASSERT_EQ(*to_in_edge_acc.Properties(memgraph::storage::View::OLD), -// *to_out_edge_acc.Properties(memgraph::storage::View::OLD)); -// // test edge accessors -// ASSERT_EQ(dest_in_edge.Gid(), src_out_edge.Gid()); -// ASSERT_EQ(dest_in_edge.EdgeType(), src_out_edge.EdgeType()); -// ASSERT_EQ(*dest_in_edge.Properties(memgraph::storage::View::OLD), -// *src_out_edge.Properties(memgraph::storage::View::OLD)); -// } +TEST_F(RocksDBStorageTest, DeserializeEdge) { + // NOTE: This test would fail in the case of snaphsot isolation because of the way in which RocksDB + // serializes commit timestamp. + auto acc = storage->Access(memgraph::storage::IsolationLevel::READ_UNCOMMITTED); + auto vertex1 = acc->CreateVertex(); + auto vertex2 = acc->CreateVertex(); + auto serialized_edge = fmt::format("{}|{}|0|1|2", vertex1.Gid().AsInt(), vertex2.Gid().AsInt()); + auto acc_ptr = static_cast(acc.get()); + auto edge = acc_ptr->DeserializeEdge(serialized_edge, "garbage"); + ASSERT_EQ(edge->Gid().AsInt(), 2); + ASSERT_EQ(edge->EdgeType().AsInt(), 1); + ASSERT_EQ(edge->from_vertex_->gid.AsInt(), vertex1.Gid().AsInt()); + ASSERT_EQ(edge->to_vertex_->gid.AsInt(), vertex2.Gid().AsInt()); +}