Add storage stat for single node

Summary:
GraphDb now has GetStorageStat method that returns live view to
object containing vertex_count, edge_count and avg_degree().
Stat is updated on each garbage collection run and represents number of
objects that are visible to any transaction.

Reviewers: teon.banek, msantl, ipaljak

Reviewed By: teon.banek

Subscribers: pullbot

Differential Revision: https://phabricator.memgraph.io/D1731
This commit is contained in:
Vinko Kasljevic 2018-11-15 08:30:01 +01:00
parent d3634e9a39
commit b77d186f58
6 changed files with 152 additions and 0 deletions

View File

@ -19,6 +19,23 @@
namespace database {
/// Struct containing basic statistics about storage.
struct Stat {
// std::atomic<long> is needed as reference to stat is passed to
// other threads. If there were no std::atomic we couldn't guarantee
// that a change to any member will be visible to other threads.
/// Vertex count is number of `VersionList<Vertex>` physically stored.
std::atomic<int64_t> vertex_count{0};
/// Vertex count is number of `VersionList<Edge>` physically stored.
std::atomic<int64_t> edge_count{0};
/// Average in/out degree of a vertex.
/// `avg_degree` is calculated as 2 * `edges_count` / `vertex_count`.
std::atomic<double> avg_degree{0};
};
/// Database configuration. Initialized from flags, but modifiable.
struct Config {
Config();
@ -101,7 +118,27 @@ class GraphDb {
/// When this is false, no new transactions should be created.
bool is_accepting_transactions() const { return is_accepting_transactions_; }
/// Get live view of storage stats. Gets updated on RefreshStat.
const Stat &GetStat() const { return stat_; }
/// Updates storage stats.
void RefreshStat() {
auto vertex_count = storage().vertices_.access().size();
auto edge_count = storage().edges_.access().size();
stat_.vertex_count = vertex_count;
stat_.edge_count = edge_count;
if (vertex_count != 0) {
stat_.avg_degree = 2 * static_cast<double>(edge_count) / vertex_count;
} else {
stat_.avg_degree = 0;
}
}
protected:
Stat stat_;
std::atomic<bool> is_accepting_transactions_{true};
std::unique_ptr<utils::Scheduler> snapshot_creator_;

View File

@ -19,6 +19,23 @@
namespace database {
/// Struct containing basic statistics about storage.
struct Stat {
// std::atomic<int64_t> is needed as reference to stat is passed to
// other threads. If there were no std::atomic we couldn't guarantee
// that a change to any member will be visible to other threads.
/// Vertex count is number of `VersionList<Vertex>` physically stored.
std::atomic<int64_t> vertex_count{0};
/// Vertex count is number of `VersionList<Edge>` physically stored.
std::atomic<int64_t> edge_count{0};
/// Average in/out degree of a vertex.
/// `avg_degree` is calculated as 2 * `edges_count` / `vertex_count`.
std::atomic<double> avg_degree{0};
};
/// Database configuration. Initialized from flags, but modifiable.
struct Config {
Config();
@ -101,7 +118,27 @@ class GraphDb {
/// When this is false, no new transactions should be created.
bool is_accepting_transactions() const { return is_accepting_transactions_; }
/// Get live view of storage stats. Gets updated on RefreshStat.
const Stat &GetStat() const { return stat_; }
/// Updates storage stats.
void RefreshStat() {
auto vertex_count = storage().vertices_.access().size();
auto edge_count = storage().edges_.access().size();
stat_.vertex_count = vertex_count;
stat_.edge_count = edge_count;
if (vertex_count != 0) {
stat_.avg_degree = 2 * static_cast<double>(edge_count) / vertex_count;
} else {
stat_.avg_degree = 0;
}
}
protected:
Stat stat_;
std::atomic<bool> is_accepting_transactions_{true};
std::unique_ptr<utils::Scheduler> snapshot_creator_;

View File

@ -60,6 +60,8 @@ class Storage {
private:
friend class GraphDbAccessor;
// Because of GraphDb::RefreshStat
friend class GraphDb;
friend class StorageGc;
gid::Generator vertex_generator_;

View File

@ -60,6 +60,8 @@ class Storage {
private:
friend class GraphDbAccessor;
// Needed for GraphDb::RefreshStat.
friend class GraphDb;
friend class StorageGc;
gid::Generator vertex_generator_;

View File

@ -234,6 +234,9 @@ target_link_libraries(${test_prefix}static_bitset mg-single-node kvstore_dummy_l
add_unit_test(storage_address.cpp)
target_link_libraries(${test_prefix}storage_address mg-distributed kvstore_dummy_lib)
add_unit_test(storage_stat.cpp)
target_link_libraries(${test_prefix}storage_stat mg-single-node kvstore_dummy_lib)
add_unit_test(stripped.cpp)
target_link_libraries(${test_prefix}stripped mg-single-node kvstore_dummy_lib)

View File

@ -0,0 +1,71 @@
#include <glog/logging.h>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include "database/single_node/graph_db.hpp"
#include "database/single_node/graph_db_accessor.hpp"
class StatTest : public ::testing::Test {
public:
database::GraphDb db_;
};
#define COMPARE(stat, vc, ec, ad) \
EXPECT_EQ(stat.vertex_count, vc); \
EXPECT_EQ(stat.edge_count, ec); \
EXPECT_EQ(stat.avg_degree, ad);
TEST_F(StatTest, CountTest1) {
auto &stat = db_.GetStat();
COMPARE(stat, 0, 0, 0);
auto dba = db_.Access();
dba->InsertVertex();
dba->InsertVertex();
dba->InsertVertex();
COMPARE(stat, 0, 0, 0);
db_.RefreshStat();
COMPARE(stat, 3, 0, 0);
}
TEST_F(StatTest, CountTest2) {
auto &stat = db_.GetStat();
COMPARE(stat, 0, 0, 0);
auto dba = db_.Access();
auto type = dba->EdgeType("edge");
auto v1 = dba->InsertVertex();
auto v2 = dba->InsertVertex();
auto v3 = dba->InsertVertex();
auto v4 = dba->InsertVertex();
auto e1 = dba->InsertEdge(v1, v2, type);
auto e2 = dba->InsertEdge(v2, v2, type);
auto e3 = dba->InsertEdge(v3, v2, type);
auto e4 = dba->InsertEdge(v4, v2, type);
auto e5 = dba->InsertEdge(v1, v3, type);
COMPARE(stat, 0, 0, 0);
db_.RefreshStat();
COMPARE(stat, 4, 5, 2.5);
dba->Commit();
auto dba1 = db_.Access();
auto v22 = dba1->FindVertex(v2.gid(), true);
dba1->DetachRemoveVertex(v22);
db_.RefreshStat();
COMPARE(stat, 4, 5, 2.5);
dba1->Commit();
db_.CollectGarbage();
db_.RefreshStat();
COMPARE(stat, 3, 1, 2.0 / 3);
}
int main(int argc, char **argv) {
::testing::InitGoogleTest(&argc, argv);
google::InitGoogleLogging(argv[0]);
return RUN_ALL_TESTS();
}