2021-10-03 18:07:04 +08:00
|
|
|
// Copyright 2021 Memgraph Ltd.
|
|
|
|
//
|
|
|
|
// Use of this software is governed by the Business Source License
|
|
|
|
// included in the file licenses/BSL.txt; by using this file, you agree to be bound by the terms of the Business Source
|
|
|
|
// License, and you may not use this file except in compliance with the Business Source License.
|
|
|
|
//
|
|
|
|
// As of the Change Date specified in that file, in accordance with
|
|
|
|
// the Business Source License, use of this software will be governed
|
|
|
|
// by the Apache License, Version 2.0, included in the file
|
|
|
|
// licenses/APL.txt.
|
|
|
|
|
2019-06-26 22:01:51 +08:00
|
|
|
#pragma once
|
|
|
|
|
2020-10-27 17:11:43 +08:00
|
|
|
#include <atomic>
|
2020-07-25 00:26:36 +08:00
|
|
|
#include <filesystem>
|
2019-06-26 22:01:51 +08:00
|
|
|
#include <optional>
|
2019-07-22 20:01:24 +08:00
|
|
|
#include <shared_mutex>
|
2021-05-10 16:10:01 +08:00
|
|
|
#include <variant>
|
2019-06-26 22:01:51 +08:00
|
|
|
|
2020-12-03 20:28:23 +08:00
|
|
|
#include "io/network/endpoint.hpp"
|
[StorageV2] Implement GC
Summary:
Here are some numbers from the benchmark:
```
(TOOLCHAIN) mtomic@poso:~/memgraph/build_release$ tests/benchmark/storage_v2_gc --num-threads 8
Config: NoGc, Time: 25.9836
Config: OnFinishGc, Time: 49.012
Config: 100msPeriodicGc, Time: 45.9856
Config: 1000msPeriodicGc, Time: 40.3094
```
```
(TOOLCHAIN) mtomic@poso:~/memgraph/build_release$ tests/benchmark/storage_v2_gc --num-threads 7
Config: NoGc, Time: 20.4256
Config: OnFinishGc, Time: 39.6669
Config: 100msPeriodicGc, Time: 30.7956
Config: 1000msPeriodicGc, Time: 35.128
```
It is not that bad if there is a core dedicated to doing garbage collection.
Reviewers: mferencevic, teon.banek
Reviewed By: mferencevic, teon.banek
Subscribers: pullbot
Differential Revision: https://phabricator.memgraph.io/D2168
2019-07-09 22:34:23 +08:00
|
|
|
#include "storage/v2/commit_log.hpp"
|
2019-09-23 20:08:48 +08:00
|
|
|
#include "storage/v2/config.hpp"
|
2019-08-20 20:59:13 +08:00
|
|
|
#include "storage/v2/constraints.hpp"
|
2020-11-25 19:08:26 +08:00
|
|
|
#include "storage/v2/durability/metadata.hpp"
|
2020-07-25 00:26:36 +08:00
|
|
|
#include "storage/v2/durability/wal.hpp"
|
2019-07-08 21:10:05 +08:00
|
|
|
#include "storage/v2/edge.hpp"
|
|
|
|
#include "storage/v2/edge_accessor.hpp"
|
2019-07-25 23:11:45 +08:00
|
|
|
#include "storage/v2/indices.hpp"
|
2021-06-14 21:47:57 +08:00
|
|
|
#include "storage/v2/isolation_level.hpp"
|
2019-07-08 21:10:05 +08:00
|
|
|
#include "storage/v2/mvcc.hpp"
|
2019-07-18 22:17:41 +08:00
|
|
|
#include "storage/v2/name_id_mapper.hpp"
|
2019-07-04 19:06:03 +08:00
|
|
|
#include "storage/v2/result.hpp"
|
2019-06-26 22:01:51 +08:00
|
|
|
#include "storage/v2/transaction.hpp"
|
|
|
|
#include "storage/v2/vertex.hpp"
|
|
|
|
#include "storage/v2/vertex_accessor.hpp"
|
2020-11-12 16:55:56 +08:00
|
|
|
#include "utils/file_locker.hpp"
|
2021-03-04 19:20:11 +08:00
|
|
|
#include "utils/on_scope_exit.hpp"
|
2019-07-22 20:01:24 +08:00
|
|
|
#include "utils/rw_lock.hpp"
|
[StorageV2] Implement GC
Summary:
Here are some numbers from the benchmark:
```
(TOOLCHAIN) mtomic@poso:~/memgraph/build_release$ tests/benchmark/storage_v2_gc --num-threads 8
Config: NoGc, Time: 25.9836
Config: OnFinishGc, Time: 49.012
Config: 100msPeriodicGc, Time: 45.9856
Config: 1000msPeriodicGc, Time: 40.3094
```
```
(TOOLCHAIN) mtomic@poso:~/memgraph/build_release$ tests/benchmark/storage_v2_gc --num-threads 7
Config: NoGc, Time: 20.4256
Config: OnFinishGc, Time: 39.6669
Config: 100msPeriodicGc, Time: 30.7956
Config: 1000msPeriodicGc, Time: 35.128
```
It is not that bad if there is a core dedicated to doing garbage collection.
Reviewers: mferencevic, teon.banek
Reviewed By: mferencevic, teon.banek
Subscribers: pullbot
Differential Revision: https://phabricator.memgraph.io/D2168
2019-07-09 22:34:23 +08:00
|
|
|
#include "utils/scheduler.hpp"
|
|
|
|
#include "utils/skip_list.hpp"
|
2019-07-22 23:05:00 +08:00
|
|
|
#include "utils/synchronized.hpp"
|
2020-12-01 23:51:25 +08:00
|
|
|
#include "utils/uuid.hpp"
|
2019-06-26 22:01:51 +08:00
|
|
|
|
2021-03-10 17:36:38 +08:00
|
|
|
/// REPLICATION ///
|
2020-10-27 17:11:43 +08:00
|
|
|
#include "rpc/server.hpp"
|
2020-12-03 20:28:23 +08:00
|
|
|
#include "storage/v2/replication/config.hpp"
|
2020-12-01 23:51:25 +08:00
|
|
|
#include "storage/v2/replication/enums.hpp"
|
2020-10-27 17:11:43 +08:00
|
|
|
#include "storage/v2/replication/rpc.hpp"
|
|
|
|
#include "storage/v2/replication/serialization.hpp"
|
|
|
|
|
2019-06-26 22:01:51 +08:00
|
|
|
namespace storage {
|
|
|
|
|
|
|
|
// The storage is based on this paper:
|
|
|
|
// https://db.in.tum.de/~muehlbau/papers/mvcc.pdf
|
|
|
|
// The paper implements a fully serializable storage, in our implementation we
|
|
|
|
// only implement snapshot isolation for transactions.
|
|
|
|
|
2019-07-29 20:26:31 +08:00
|
|
|
/// Iterable for iterating through all vertices of a Storage.
|
|
|
|
///
|
|
|
|
/// An instance of this will be usually be wrapped inside VerticesIterable for
|
|
|
|
/// generic, public use.
|
|
|
|
class AllVerticesIterable final {
|
2019-07-22 22:05:44 +08:00
|
|
|
utils::SkipList<Vertex>::Accessor vertices_accessor_;
|
|
|
|
Transaction *transaction_;
|
|
|
|
View view_;
|
2019-07-25 23:11:45 +08:00
|
|
|
Indices *indices_;
|
2020-01-30 22:56:11 +08:00
|
|
|
Constraints *constraints_;
|
2019-09-24 22:48:36 +08:00
|
|
|
Config::Items config_;
|
2019-11-26 17:43:27 +08:00
|
|
|
std::optional<VertexAccessor> vertex_;
|
2019-07-22 22:05:44 +08:00
|
|
|
|
2019-07-29 20:26:31 +08:00
|
|
|
public:
|
2019-07-22 22:05:44 +08:00
|
|
|
class Iterator final {
|
2019-07-29 20:26:31 +08:00
|
|
|
AllVerticesIterable *self_;
|
2019-07-22 22:05:44 +08:00
|
|
|
utils::SkipList<Vertex>::Iterator it_;
|
|
|
|
|
|
|
|
public:
|
2019-07-29 20:26:31 +08:00
|
|
|
Iterator(AllVerticesIterable *self, utils::SkipList<Vertex>::Iterator it);
|
2019-07-22 22:05:44 +08:00
|
|
|
|
|
|
|
VertexAccessor operator*() const;
|
|
|
|
|
|
|
|
Iterator &operator++();
|
|
|
|
|
2021-02-18 22:32:43 +08:00
|
|
|
bool operator==(const Iterator &other) const { return self_ == other.self_ && it_ == other.it_; }
|
2019-07-22 22:05:44 +08:00
|
|
|
|
|
|
|
bool operator!=(const Iterator &other) const { return !(*this == other); }
|
|
|
|
};
|
|
|
|
|
2021-02-18 22:32:43 +08:00
|
|
|
AllVerticesIterable(utils::SkipList<Vertex>::Accessor vertices_accessor, Transaction *transaction, View view,
|
|
|
|
Indices *indices, Constraints *constraints, Config::Items config)
|
2019-07-22 22:05:44 +08:00
|
|
|
: vertices_accessor_(std::move(vertices_accessor)),
|
|
|
|
transaction_(transaction),
|
2019-07-25 23:11:45 +08:00
|
|
|
view_(view),
|
2019-10-21 18:22:11 +08:00
|
|
|
indices_(indices),
|
2020-01-30 22:56:11 +08:00
|
|
|
constraints_(constraints),
|
2019-10-21 18:22:11 +08:00
|
|
|
config_(config) {}
|
2019-07-22 22:05:44 +08:00
|
|
|
|
|
|
|
Iterator begin() { return Iterator(this, vertices_accessor_.begin()); }
|
|
|
|
Iterator end() { return Iterator(this, vertices_accessor_.end()); }
|
|
|
|
};
|
|
|
|
|
2019-07-29 20:26:31 +08:00
|
|
|
/// Generic access to different kinds of vertex iterations.
|
|
|
|
///
|
|
|
|
/// This class should be the primary type used by the client code to iterate
|
|
|
|
/// over vertices inside a Storage instance.
|
|
|
|
class VerticesIterable final {
|
|
|
|
enum class Type { ALL, BY_LABEL, BY_LABEL_PROPERTY };
|
|
|
|
|
|
|
|
Type type_;
|
|
|
|
union {
|
|
|
|
AllVerticesIterable all_vertices_;
|
|
|
|
LabelIndex::Iterable vertices_by_label_;
|
|
|
|
LabelPropertyIndex::Iterable vertices_by_label_property_;
|
|
|
|
};
|
|
|
|
|
|
|
|
public:
|
|
|
|
explicit VerticesIterable(AllVerticesIterable);
|
|
|
|
explicit VerticesIterable(LabelIndex::Iterable);
|
|
|
|
explicit VerticesIterable(LabelPropertyIndex::Iterable);
|
|
|
|
|
|
|
|
VerticesIterable(const VerticesIterable &) = delete;
|
|
|
|
VerticesIterable &operator=(const VerticesIterable &) = delete;
|
|
|
|
|
|
|
|
VerticesIterable(VerticesIterable &&) noexcept;
|
|
|
|
VerticesIterable &operator=(VerticesIterable &&) noexcept;
|
|
|
|
|
|
|
|
~VerticesIterable();
|
|
|
|
|
|
|
|
class Iterator final {
|
|
|
|
Type type_;
|
|
|
|
union {
|
|
|
|
AllVerticesIterable::Iterator all_it_;
|
|
|
|
LabelIndex::Iterable::Iterator by_label_it_;
|
|
|
|
LabelPropertyIndex::Iterable::Iterator by_label_property_it_;
|
|
|
|
};
|
|
|
|
|
|
|
|
void Destroy() noexcept;
|
|
|
|
|
|
|
|
public:
|
|
|
|
explicit Iterator(AllVerticesIterable::Iterator);
|
|
|
|
explicit Iterator(LabelIndex::Iterable::Iterator);
|
|
|
|
explicit Iterator(LabelPropertyIndex::Iterable::Iterator);
|
|
|
|
|
|
|
|
Iterator(const Iterator &);
|
|
|
|
Iterator &operator=(const Iterator &);
|
|
|
|
|
|
|
|
Iterator(Iterator &&) noexcept;
|
|
|
|
Iterator &operator=(Iterator &&) noexcept;
|
|
|
|
|
|
|
|
~Iterator();
|
|
|
|
|
|
|
|
VertexAccessor operator*() const;
|
|
|
|
|
|
|
|
Iterator &operator++();
|
|
|
|
|
|
|
|
bool operator==(const Iterator &other) const;
|
|
|
|
bool operator!=(const Iterator &other) const { return !(*this == other); }
|
|
|
|
};
|
|
|
|
|
|
|
|
Iterator begin();
|
|
|
|
Iterator end();
|
|
|
|
};
|
|
|
|
|
2019-09-11 19:22:11 +08:00
|
|
|
/// Structure used to return information about existing indices in the storage.
|
|
|
|
struct IndicesInfo {
|
|
|
|
std::vector<LabelId> label;
|
|
|
|
std::vector<std::pair<LabelId, PropertyId>> label_property;
|
|
|
|
};
|
|
|
|
|
|
|
|
/// Structure used to return information about existing constraints in the
|
|
|
|
/// storage.
|
|
|
|
struct ConstraintsInfo {
|
|
|
|
std::vector<std::pair<LabelId, PropertyId>> existence;
|
2020-02-13 18:52:56 +08:00
|
|
|
std::vector<std::pair<LabelId, std::set<PropertyId>>> unique;
|
2019-09-11 19:22:11 +08:00
|
|
|
};
|
|
|
|
|
2019-12-09 22:49:28 +08:00
|
|
|
/// Structure used to return information about the storage.
|
|
|
|
struct StorageInfo {
|
|
|
|
uint64_t vertex_count;
|
|
|
|
uint64_t edge_count;
|
|
|
|
double average_degree;
|
|
|
|
uint64_t memory_usage;
|
|
|
|
uint64_t disk_usage;
|
|
|
|
};
|
|
|
|
|
2020-11-19 21:26:03 +08:00
|
|
|
enum class ReplicationRole : uint8_t { MAIN, REPLICA };
|
2020-10-27 17:11:43 +08:00
|
|
|
|
2019-06-26 22:01:51 +08:00
|
|
|
class Storage final {
|
|
|
|
public:
|
2019-07-31 17:10:19 +08:00
|
|
|
/// @throw std::system_error
|
|
|
|
/// @throw std::bad_alloc
|
2019-09-13 17:18:17 +08:00
|
|
|
explicit Storage(Config config = Config());
|
[StorageV2] Implement GC
Summary:
Here are some numbers from the benchmark:
```
(TOOLCHAIN) mtomic@poso:~/memgraph/build_release$ tests/benchmark/storage_v2_gc --num-threads 8
Config: NoGc, Time: 25.9836
Config: OnFinishGc, Time: 49.012
Config: 100msPeriodicGc, Time: 45.9856
Config: 1000msPeriodicGc, Time: 40.3094
```
```
(TOOLCHAIN) mtomic@poso:~/memgraph/build_release$ tests/benchmark/storage_v2_gc --num-threads 7
Config: NoGc, Time: 20.4256
Config: OnFinishGc, Time: 39.6669
Config: 100msPeriodicGc, Time: 30.7956
Config: 1000msPeriodicGc, Time: 35.128
```
It is not that bad if there is a core dedicated to doing garbage collection.
Reviewers: mferencevic, teon.banek
Reviewed By: mferencevic, teon.banek
Subscribers: pullbot
Differential Revision: https://phabricator.memgraph.io/D2168
2019-07-09 22:34:23 +08:00
|
|
|
|
|
|
|
~Storage();
|
|
|
|
|
2019-06-26 22:01:51 +08:00
|
|
|
class Accessor final {
|
2019-07-11 19:27:50 +08:00
|
|
|
private:
|
|
|
|
friend class Storage;
|
|
|
|
|
2021-06-14 21:47:57 +08:00
|
|
|
explicit Accessor(Storage *storage, IsolationLevel isolation_level);
|
2019-06-26 22:01:51 +08:00
|
|
|
|
2019-07-11 19:27:50 +08:00
|
|
|
public:
|
2019-06-26 22:01:51 +08:00
|
|
|
Accessor(const Accessor &) = delete;
|
|
|
|
Accessor &operator=(const Accessor &) = delete;
|
2019-07-11 19:27:50 +08:00
|
|
|
Accessor &operator=(Accessor &&other) = delete;
|
2019-06-26 22:01:51 +08:00
|
|
|
|
2019-07-11 19:27:50 +08:00
|
|
|
// NOTE: After the accessor is moved, all objects derived from it (accessors
|
|
|
|
// and iterators) are *invalid*. You have to get all derived objects again.
|
2019-07-03 21:32:03 +08:00
|
|
|
Accessor(Accessor &&other) noexcept;
|
2019-06-26 22:01:51 +08:00
|
|
|
|
2019-07-03 21:32:03 +08:00
|
|
|
~Accessor();
|
|
|
|
|
2019-07-31 17:10:19 +08:00
|
|
|
/// @throw std::bad_alloc
|
2019-07-03 21:32:03 +08:00
|
|
|
VertexAccessor CreateVertex();
|
|
|
|
|
|
|
|
std::optional<VertexAccessor> FindVertex(Gid gid, View view);
|
|
|
|
|
2019-07-22 22:05:44 +08:00
|
|
|
VerticesIterable Vertices(View view) {
|
2021-02-18 22:32:43 +08:00
|
|
|
return VerticesIterable(AllVerticesIterable(storage_->vertices_.access(), &transaction_, view,
|
|
|
|
&storage_->indices_, &storage_->constraints_,
|
|
|
|
storage_->config_.items));
|
2019-07-22 22:05:44 +08:00
|
|
|
}
|
|
|
|
|
2019-07-29 20:26:31 +08:00
|
|
|
VerticesIterable Vertices(LabelId label, View view);
|
2019-07-25 23:11:45 +08:00
|
|
|
|
2019-07-29 20:26:31 +08:00
|
|
|
VerticesIterable Vertices(LabelId label, PropertyId property, View view);
|
2019-07-25 23:11:45 +08:00
|
|
|
|
2021-02-18 22:32:43 +08:00
|
|
|
VerticesIterable Vertices(LabelId label, PropertyId property, const PropertyValue &value, View view);
|
2019-07-25 23:11:45 +08:00
|
|
|
|
2021-02-18 22:32:43 +08:00
|
|
|
VerticesIterable Vertices(LabelId label, PropertyId property,
|
|
|
|
const std::optional<utils::Bound<PropertyValue>> &lower_bound,
|
|
|
|
const std::optional<utils::Bound<PropertyValue>> &upper_bound, View view);
|
2019-07-25 23:11:45 +08:00
|
|
|
|
2019-08-22 22:49:45 +08:00
|
|
|
/// Return approximate number of all vertices in the database.
|
|
|
|
/// Note that this is always an over-estimate and never an under-estimate.
|
2021-02-18 22:32:43 +08:00
|
|
|
int64_t ApproximateVertexCount() const { return storage_->vertices_.size(); }
|
2019-08-22 22:49:45 +08:00
|
|
|
|
|
|
|
/// Return approximate number of vertices with the given label.
|
|
|
|
/// Note that this is always an over-estimate and never an under-estimate.
|
|
|
|
int64_t ApproximateVertexCount(LabelId label) const {
|
|
|
|
return storage_->indices_.label_index.ApproximateVertexCount(label);
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Return approximate number of vertices with the given label and property.
|
|
|
|
/// Note that this is always an over-estimate and never an under-estimate.
|
|
|
|
int64_t ApproximateVertexCount(LabelId label, PropertyId property) const {
|
2021-02-18 22:32:43 +08:00
|
|
|
return storage_->indices_.label_property_index.ApproximateVertexCount(label, property);
|
2019-08-22 22:49:45 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Return approximate number of vertices with the given label and the given
|
|
|
|
/// value for the given property. Note that this is always an over-estimate
|
|
|
|
/// and never an under-estimate.
|
2021-02-18 22:32:43 +08:00
|
|
|
int64_t ApproximateVertexCount(LabelId label, PropertyId property, const PropertyValue &value) const {
|
|
|
|
return storage_->indices_.label_property_index.ApproximateVertexCount(label, property, value);
|
2019-08-22 22:49:45 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Return approximate number of vertices with the given label and value for
|
|
|
|
/// the given property in the range defined by provided upper and lower
|
|
|
|
/// bounds.
|
2021-02-18 22:32:43 +08:00
|
|
|
int64_t ApproximateVertexCount(LabelId label, PropertyId property,
|
|
|
|
const std::optional<utils::Bound<PropertyValue>> &lower,
|
|
|
|
const std::optional<utils::Bound<PropertyValue>> &upper) const {
|
|
|
|
return storage_->indices_.label_property_index.ApproximateVertexCount(label, property, lower, upper);
|
2019-08-22 22:49:45 +08:00
|
|
|
}
|
|
|
|
|
2021-05-10 16:10:01 +08:00
|
|
|
/// @return Accessor to the deleted vertex if a deletion took place, std::nullopt otherwise
|
2019-07-31 17:10:19 +08:00
|
|
|
/// @throw std::bad_alloc
|
2021-05-04 19:00:07 +08:00
|
|
|
Result<std::optional<VertexAccessor>> DeleteVertex(VertexAccessor *vertex);
|
2019-07-04 19:06:03 +08:00
|
|
|
|
2021-05-10 16:10:01 +08:00
|
|
|
/// @return Accessor to the deleted vertex and deleted edges if a deletion took place, std::nullopt otherwise
|
2019-07-31 17:10:19 +08:00
|
|
|
/// @throw std::bad_alloc
|
2021-05-10 16:10:01 +08:00
|
|
|
Result<std::optional<std::pair<VertexAccessor, std::vector<EdgeAccessor>>>> DetachDeleteVertex(
|
|
|
|
VertexAccessor *vertex);
|
2019-07-08 21:10:05 +08:00
|
|
|
|
2019-07-31 17:10:19 +08:00
|
|
|
/// @throw std::bad_alloc
|
2021-02-18 22:32:43 +08:00
|
|
|
Result<EdgeAccessor> CreateEdge(VertexAccessor *from, VertexAccessor *to, EdgeTypeId edge_type);
|
2019-07-08 21:10:05 +08:00
|
|
|
|
2021-05-10 16:10:01 +08:00
|
|
|
/// Accessor to the deleted edge if a deletion took place, std::nullopt otherwise
|
2019-07-31 17:10:19 +08:00
|
|
|
/// @throw std::bad_alloc
|
2021-05-10 16:10:01 +08:00
|
|
|
Result<std::optional<EdgeAccessor>> DeleteEdge(EdgeAccessor *edge);
|
2019-07-08 21:10:05 +08:00
|
|
|
|
2019-07-31 20:43:45 +08:00
|
|
|
const std::string &LabelToName(LabelId label) const;
|
|
|
|
const std::string &PropertyToName(PropertyId property) const;
|
|
|
|
const std::string &EdgeTypeToName(EdgeTypeId edge_type) const;
|
2019-07-18 22:17:41 +08:00
|
|
|
|
2019-07-31 17:10:19 +08:00
|
|
|
/// @throw std::bad_alloc if unable to insert a new mapping
|
2020-02-24 20:32:52 +08:00
|
|
|
LabelId NameToLabel(const std::string_view &name);
|
2019-07-31 17:10:19 +08:00
|
|
|
|
|
|
|
/// @throw std::bad_alloc if unable to insert a new mapping
|
2020-02-24 20:32:52 +08:00
|
|
|
PropertyId NameToProperty(const std::string_view &name);
|
2019-07-31 17:10:19 +08:00
|
|
|
|
|
|
|
/// @throw std::bad_alloc if unable to insert a new mapping
|
2020-02-24 20:32:52 +08:00
|
|
|
EdgeTypeId NameToEdgeType(const std::string_view &name);
|
2019-07-18 22:17:41 +08:00
|
|
|
|
2021-02-18 22:32:43 +08:00
|
|
|
bool LabelIndexExists(LabelId label) const { return storage_->indices_.label_index.IndexExists(label); }
|
2019-11-12 17:31:57 +08:00
|
|
|
|
|
|
|
bool LabelPropertyIndexExists(LabelId label, PropertyId property) const {
|
2021-02-18 22:32:43 +08:00
|
|
|
return storage_->indices_.label_property_index.IndexExists(label, property);
|
2019-11-12 17:31:57 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
IndicesInfo ListAllIndices() const {
|
2021-02-18 22:32:43 +08:00
|
|
|
return {storage_->indices_.label_index.ListIndices(), storage_->indices_.label_property_index.ListIndices()};
|
2019-11-12 17:31:57 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
ConstraintsInfo ListAllConstraints() const {
|
2020-01-30 22:56:11 +08:00
|
|
|
return {ListExistenceConstraints(storage_->constraints_),
|
|
|
|
storage_->constraints_.unique_constraints.ListConstraints()};
|
2019-11-12 17:31:57 +08:00
|
|
|
}
|
|
|
|
|
2019-07-03 21:32:03 +08:00
|
|
|
void AdvanceCommand();
|
|
|
|
|
2020-01-30 22:56:11 +08:00
|
|
|
/// Commit returns `ConstraintViolation` if the changes made by this
|
|
|
|
/// transaction violate an existence or unique constraint. In that case the
|
2019-09-09 21:52:09 +08:00
|
|
|
/// transaction is automatically aborted. Otherwise, void is returned.
|
2019-07-31 17:10:19 +08:00
|
|
|
/// @throw std::bad_alloc
|
2021-02-18 22:32:43 +08:00
|
|
|
utils::BasicResult<ConstraintViolation, void> Commit(std::optional<uint64_t> desired_commit_timestamp = {});
|
2019-07-03 21:32:03 +08:00
|
|
|
|
2019-07-31 17:10:19 +08:00
|
|
|
/// @throw std::bad_alloc
|
2019-07-03 21:32:03 +08:00
|
|
|
void Abort();
|
2019-06-26 22:01:51 +08:00
|
|
|
|
2021-04-23 20:19:42 +08:00
|
|
|
void FinalizeTransaction();
|
|
|
|
|
2019-06-26 22:01:51 +08:00
|
|
|
private:
|
2020-10-27 17:11:43 +08:00
|
|
|
/// @throw std::bad_alloc
|
|
|
|
VertexAccessor CreateVertex(storage::Gid gid);
|
|
|
|
|
|
|
|
/// @throw std::bad_alloc
|
2021-02-18 22:32:43 +08:00
|
|
|
Result<EdgeAccessor> CreateEdge(VertexAccessor *from, VertexAccessor *to, EdgeTypeId edge_type, storage::Gid gid);
|
2020-10-27 17:11:43 +08:00
|
|
|
|
2019-06-26 22:01:51 +08:00
|
|
|
Storage *storage_;
|
2019-09-12 18:15:07 +08:00
|
|
|
std::shared_lock<utils::RWLock> storage_guard_;
|
2019-07-11 19:27:50 +08:00
|
|
|
Transaction transaction_;
|
2021-04-23 20:19:42 +08:00
|
|
|
std::optional<uint64_t> commit_timestamp_;
|
2019-07-11 19:27:50 +08:00
|
|
|
bool is_transaction_active_;
|
2019-09-24 22:48:36 +08:00
|
|
|
Config::Items config_;
|
2019-06-26 22:01:51 +08:00
|
|
|
};
|
|
|
|
|
2021-06-14 21:47:57 +08:00
|
|
|
Accessor Access(std::optional<IsolationLevel> override_isolation_level = {}) {
|
|
|
|
return Accessor{this, override_isolation_level.value_or(isolation_level_)};
|
|
|
|
}
|
2019-06-26 22:01:51 +08:00
|
|
|
|
2019-07-31 20:43:45 +08:00
|
|
|
const std::string &LabelToName(LabelId label) const;
|
|
|
|
const std::string &PropertyToName(PropertyId property) const;
|
|
|
|
const std::string &EdgeTypeToName(EdgeTypeId edge_type) const;
|
|
|
|
|
2019-07-31 17:10:19 +08:00
|
|
|
/// @throw std::bad_alloc if unable to insert a new mapping
|
2020-02-24 20:32:52 +08:00
|
|
|
LabelId NameToLabel(const std::string_view &name);
|
2019-07-31 17:10:19 +08:00
|
|
|
|
|
|
|
/// @throw std::bad_alloc if unable to insert a new mapping
|
2020-02-24 20:32:52 +08:00
|
|
|
PropertyId NameToProperty(const std::string_view &name);
|
2019-07-31 17:10:19 +08:00
|
|
|
|
|
|
|
/// @throw std::bad_alloc if unable to insert a new mapping
|
2020-02-24 20:32:52 +08:00
|
|
|
EdgeTypeId NameToEdgeType(const std::string_view &name);
|
2019-07-31 20:43:45 +08:00
|
|
|
|
2019-09-23 21:29:50 +08:00
|
|
|
/// @throw std::bad_alloc
|
2021-02-18 22:32:43 +08:00
|
|
|
bool CreateIndex(LabelId label, std::optional<uint64_t> desired_commit_timestamp = {});
|
2019-09-23 21:29:50 +08:00
|
|
|
|
2019-07-31 17:10:19 +08:00
|
|
|
/// @throw std::bad_alloc
|
2021-02-18 22:32:43 +08:00
|
|
|
bool CreateIndex(LabelId label, PropertyId property, std::optional<uint64_t> desired_commit_timestamp = {});
|
2019-07-25 23:11:45 +08:00
|
|
|
|
2021-02-18 22:32:43 +08:00
|
|
|
bool DropIndex(LabelId label, std::optional<uint64_t> desired_commit_timestamp = {});
|
2019-09-23 21:29:50 +08:00
|
|
|
|
2021-02-18 22:32:43 +08:00
|
|
|
bool DropIndex(LabelId label, PropertyId property, std::optional<uint64_t> desired_commit_timestamp = {});
|
2019-07-25 23:11:45 +08:00
|
|
|
|
2019-11-12 17:31:57 +08:00
|
|
|
IndicesInfo ListAllIndices() const;
|
2019-09-11 19:22:11 +08:00
|
|
|
|
2020-01-30 22:56:11 +08:00
|
|
|
/// Creates an existence constraint. Returns true if the constraint was
|
|
|
|
/// successfuly added, false if it already exists and a `ConstraintViolation`
|
|
|
|
/// if there is an existing vertex violating the constraint.
|
2019-08-20 20:59:13 +08:00
|
|
|
///
|
|
|
|
/// @throw std::bad_alloc
|
|
|
|
/// @throw std::length_error
|
2020-02-13 18:52:56 +08:00
|
|
|
utils::BasicResult<ConstraintViolation, bool> CreateExistenceConstraint(
|
2021-02-18 22:32:43 +08:00
|
|
|
LabelId label, PropertyId property, std::optional<uint64_t> desired_commit_timestamp = {});
|
2019-08-20 20:59:13 +08:00
|
|
|
|
2020-01-30 22:56:11 +08:00
|
|
|
/// Removes an existence constraint. Returns true if the constraint was
|
|
|
|
/// removed, and false if it doesn't exist.
|
2021-02-18 22:32:43 +08:00
|
|
|
bool DropExistenceConstraint(LabelId label, PropertyId property,
|
|
|
|
std::optional<uint64_t> desired_commit_timestamp = {});
|
2020-01-30 22:56:11 +08:00
|
|
|
|
2020-02-13 18:52:56 +08:00
|
|
|
/// Creates a unique constraint. In the case of two vertices violating the
|
|
|
|
/// constraint, it returns `ConstraintViolation`. Otherwise returns a
|
|
|
|
/// `UniqueConstraints::CreationStatus` enum with the following possibilities:
|
|
|
|
/// * `SUCCESS` if the constraint was successfully created,
|
2020-03-02 19:20:29 +08:00
|
|
|
/// * `ALREADY_EXISTS` if the constraint already existed,
|
|
|
|
/// * `EMPTY_PROPERTIES` if the property set is empty, or
|
|
|
|
// * `PROPERTIES_SIZE_LIMIT_EXCEEDED` if the property set exceeds the
|
|
|
|
// limit of maximum number of properties.
|
2020-01-30 22:56:11 +08:00
|
|
|
///
|
|
|
|
/// @throw std::bad_alloc
|
2021-02-18 22:32:43 +08:00
|
|
|
utils::BasicResult<ConstraintViolation, UniqueConstraints::CreationStatus> CreateUniqueConstraint(
|
|
|
|
LabelId label, const std::set<PropertyId> &properties, std::optional<uint64_t> desired_commit_timestamp = {});
|
2020-01-30 22:56:11 +08:00
|
|
|
|
2020-03-02 19:20:29 +08:00
|
|
|
/// Removes a unique constraint. Returns `UniqueConstraints::DeletionStatus`
|
|
|
|
/// enum with the following possibilities:
|
|
|
|
/// * `SUCCESS` if constraint was successfully removed,
|
|
|
|
/// * `NOT_FOUND` if the specified constraint was not found,
|
|
|
|
/// * `EMPTY_PROPERTIES` if the property set is empty, or
|
|
|
|
/// * `PROPERTIES_SIZE_LIMIT_EXCEEDED` if the property set exceeds the
|
|
|
|
// limit of maximum number of properties.
|
2021-02-18 22:32:43 +08:00
|
|
|
UniqueConstraints::DeletionStatus DropUniqueConstraint(LabelId label, const std::set<PropertyId> &properties,
|
|
|
|
std::optional<uint64_t> desired_commit_timestamp = {});
|
2019-08-20 20:59:13 +08:00
|
|
|
|
2019-11-12 17:31:57 +08:00
|
|
|
ConstraintsInfo ListAllConstraints() const;
|
2019-09-11 19:22:11 +08:00
|
|
|
|
2019-12-09 22:49:28 +08:00
|
|
|
StorageInfo GetInfo() const;
|
|
|
|
|
2021-01-19 19:10:06 +08:00
|
|
|
bool LockPath();
|
|
|
|
bool UnlockPath();
|
2020-11-01 22:15:06 +08:00
|
|
|
|
2021-02-18 22:32:43 +08:00
|
|
|
bool SetReplicaRole(io::network::Endpoint endpoint, const replication::ReplicationServerConfig &config = {});
|
2020-12-03 20:28:23 +08:00
|
|
|
|
2020-12-11 17:47:19 +08:00
|
|
|
bool SetMainReplicationRole();
|
2020-11-01 22:15:06 +08:00
|
|
|
|
2020-12-03 20:28:23 +08:00
|
|
|
enum class RegisterReplicaError : uint8_t { NAME_EXISTS, CONNECTION_FAILED };
|
2020-11-01 22:15:06 +08:00
|
|
|
|
2020-12-03 20:28:23 +08:00
|
|
|
/// @pre The instance should have a MAIN role
|
|
|
|
/// @pre Timeout can only be set for SYNC replication
|
|
|
|
utils::BasicResult<RegisterReplicaError, void> RegisterReplica(
|
2021-02-18 22:32:43 +08:00
|
|
|
std::string name, io::network::Endpoint endpoint, replication::ReplicationMode replication_mode,
|
2020-12-03 20:28:23 +08:00
|
|
|
const replication::ReplicationClientConfig &config = {});
|
|
|
|
/// @pre The instance should have a MAIN role
|
|
|
|
bool UnregisterReplica(std::string_view name);
|
2020-11-19 21:26:03 +08:00
|
|
|
|
2021-02-18 22:32:43 +08:00
|
|
|
std::optional<replication::ReplicaState> GetReplicaState(std::string_view name);
|
2020-12-03 20:28:23 +08:00
|
|
|
|
|
|
|
ReplicationRole GetReplicationRole() const;
|
|
|
|
|
|
|
|
struct ReplicaInfo {
|
|
|
|
std::string name;
|
|
|
|
replication::ReplicationMode mode;
|
|
|
|
std::optional<double> timeout;
|
|
|
|
io::network::Endpoint endpoint;
|
|
|
|
replication::ReplicaState state;
|
|
|
|
};
|
|
|
|
|
|
|
|
std::vector<ReplicaInfo> ReplicasInfo();
|
2020-10-27 17:11:43 +08:00
|
|
|
|
2021-03-04 19:20:11 +08:00
|
|
|
void FreeMemory();
|
|
|
|
|
2021-06-14 21:47:57 +08:00
|
|
|
void SetIsolationLevel(IsolationLevel isolation_level);
|
|
|
|
|
2021-06-30 18:31:30 +08:00
|
|
|
enum class CreateSnapshotError : uint8_t { DisabledForReplica };
|
|
|
|
|
|
|
|
utils::BasicResult<CreateSnapshotError> CreateSnapshot();
|
|
|
|
|
2019-06-26 22:01:51 +08:00
|
|
|
private:
|
2021-06-14 21:47:57 +08:00
|
|
|
Transaction CreateTransaction(IsolationLevel isolation_level);
|
2019-09-12 18:15:07 +08:00
|
|
|
|
2021-03-04 19:20:11 +08:00
|
|
|
/// The force parameter determines the behaviour of the garbage collector.
|
|
|
|
/// If it's set to true, it will behave as a global operation, i.e. it can't
|
|
|
|
/// be part of a transaction, and no other transaction can be active at the same time.
|
|
|
|
/// This allows it to delete immediately vertices without worrying that some other
|
|
|
|
/// transaction is possibly using it. If there are active transactions when this method
|
|
|
|
/// is called with force set to true, it will fallback to the same method with the force
|
|
|
|
/// set to false.
|
|
|
|
/// If it's set to false, it will execute in parallel with other transactions, ensuring
|
|
|
|
/// that no object in use can be deleted.
|
2019-07-31 17:10:19 +08:00
|
|
|
/// @throw std::system_error
|
|
|
|
/// @throw std::bad_alloc
|
2021-03-04 19:20:11 +08:00
|
|
|
template <bool force>
|
[StorageV2] Implement GC
Summary:
Here are some numbers from the benchmark:
```
(TOOLCHAIN) mtomic@poso:~/memgraph/build_release$ tests/benchmark/storage_v2_gc --num-threads 8
Config: NoGc, Time: 25.9836
Config: OnFinishGc, Time: 49.012
Config: 100msPeriodicGc, Time: 45.9856
Config: 1000msPeriodicGc, Time: 40.3094
```
```
(TOOLCHAIN) mtomic@poso:~/memgraph/build_release$ tests/benchmark/storage_v2_gc --num-threads 7
Config: NoGc, Time: 20.4256
Config: OnFinishGc, Time: 39.6669
Config: 100msPeriodicGc, Time: 30.7956
Config: 1000msPeriodicGc, Time: 35.128
```
It is not that bad if there is a core dedicated to doing garbage collection.
Reviewers: mferencevic, teon.banek
Reviewed By: mferencevic, teon.banek
Subscribers: pullbot
Differential Revision: https://phabricator.memgraph.io/D2168
2019-07-09 22:34:23 +08:00
|
|
|
void CollectGarbage();
|
|
|
|
|
2020-07-25 00:26:36 +08:00
|
|
|
bool InitializeWalFile();
|
|
|
|
void FinalizeWalFile();
|
|
|
|
|
2021-02-18 22:32:43 +08:00
|
|
|
void AppendToWal(const Transaction &transaction, uint64_t final_commit_timestamp);
|
|
|
|
void AppendToWal(durability::StorageGlobalOperation operation, LabelId label, const std::set<PropertyId> &properties,
|
2020-07-25 00:26:36 +08:00
|
|
|
uint64_t final_commit_timestamp);
|
|
|
|
|
2021-02-18 22:32:43 +08:00
|
|
|
uint64_t CommitTimestamp(std::optional<uint64_t> desired_commit_timestamp = {});
|
2020-11-25 19:08:26 +08:00
|
|
|
|
2019-07-22 20:01:24 +08:00
|
|
|
// Main storage lock.
|
|
|
|
//
|
|
|
|
// Accessors take a shared lock when starting, so it is possible to block
|
2019-08-20 20:59:13 +08:00
|
|
|
// creation of new accessors by taking a unique lock. This is used when doing
|
|
|
|
// operations on storage that affect the global state, for example index
|
|
|
|
// creation.
|
2019-11-12 17:31:57 +08:00
|
|
|
mutable utils::RWLock main_lock_{utils::RWLock::Priority::WRITE};
|
2019-07-22 20:01:24 +08:00
|
|
|
|
2019-06-26 22:01:51 +08:00
|
|
|
// Main object storage
|
|
|
|
utils::SkipList<storage::Vertex> vertices_;
|
2019-07-08 21:10:05 +08:00
|
|
|
utils::SkipList<storage::Edge> edges_;
|
2019-06-26 22:01:51 +08:00
|
|
|
std::atomic<uint64_t> vertex_id_{0};
|
2019-07-08 21:10:05 +08:00
|
|
|
std::atomic<uint64_t> edge_id_{0};
|
2019-12-09 22:49:28 +08:00
|
|
|
// Even though the edge count is already kept in the `edges_` SkipList, the
|
|
|
|
// list is used only when properties are enabled for edges. Because of that we
|
|
|
|
// keep a separate count of edges that is always updated.
|
|
|
|
std::atomic<uint64_t> edge_count_{0};
|
2019-06-26 22:01:51 +08:00
|
|
|
|
2019-07-22 23:05:00 +08:00
|
|
|
NameIdMapper name_id_mapper_;
|
|
|
|
|
2019-08-20 20:59:13 +08:00
|
|
|
Constraints constraints_;
|
2020-01-30 22:56:11 +08:00
|
|
|
Indices indices_;
|
2019-07-25 23:11:45 +08:00
|
|
|
|
2019-06-26 22:01:51 +08:00
|
|
|
// Transaction engine
|
[StorageV2] Implement GC
Summary:
Here are some numbers from the benchmark:
```
(TOOLCHAIN) mtomic@poso:~/memgraph/build_release$ tests/benchmark/storage_v2_gc --num-threads 8
Config: NoGc, Time: 25.9836
Config: OnFinishGc, Time: 49.012
Config: 100msPeriodicGc, Time: 45.9856
Config: 1000msPeriodicGc, Time: 40.3094
```
```
(TOOLCHAIN) mtomic@poso:~/memgraph/build_release$ tests/benchmark/storage_v2_gc --num-threads 7
Config: NoGc, Time: 20.4256
Config: OnFinishGc, Time: 39.6669
Config: 100msPeriodicGc, Time: 30.7956
Config: 1000msPeriodicGc, Time: 35.128
```
It is not that bad if there is a core dedicated to doing garbage collection.
Reviewers: mferencevic, teon.banek
Reviewed By: mferencevic, teon.banek
Subscribers: pullbot
Differential Revision: https://phabricator.memgraph.io/D2168
2019-07-09 22:34:23 +08:00
|
|
|
utils::SpinLock engine_lock_;
|
2019-06-26 22:01:51 +08:00
|
|
|
uint64_t timestamp_{kTimestampInitialId};
|
|
|
|
uint64_t transaction_id_{kTransactionInitialId};
|
[StorageV2] Implement GC
Summary:
Here are some numbers from the benchmark:
```
(TOOLCHAIN) mtomic@poso:~/memgraph/build_release$ tests/benchmark/storage_v2_gc --num-threads 8
Config: NoGc, Time: 25.9836
Config: OnFinishGc, Time: 49.012
Config: 100msPeriodicGc, Time: 45.9856
Config: 1000msPeriodicGc, Time: 40.3094
```
```
(TOOLCHAIN) mtomic@poso:~/memgraph/build_release$ tests/benchmark/storage_v2_gc --num-threads 7
Config: NoGc, Time: 20.4256
Config: OnFinishGc, Time: 39.6669
Config: 100msPeriodicGc, Time: 30.7956
Config: 1000msPeriodicGc, Time: 35.128
```
It is not that bad if there is a core dedicated to doing garbage collection.
Reviewers: mferencevic, teon.banek
Reviewed By: mferencevic, teon.banek
Subscribers: pullbot
Differential Revision: https://phabricator.memgraph.io/D2168
2019-07-09 22:34:23 +08:00
|
|
|
// TODO: This isn't really a commit log, it doesn't even care if a
|
|
|
|
// transaction commited or aborted. We could probably combine this with
|
|
|
|
// `timestamp_` in a sensible unit, something like TransactionClock or
|
|
|
|
// whatever.
|
2021-02-19 18:00:10 +08:00
|
|
|
std::optional<CommitLog> commit_log_;
|
[StorageV2] Implement GC
Summary:
Here are some numbers from the benchmark:
```
(TOOLCHAIN) mtomic@poso:~/memgraph/build_release$ tests/benchmark/storage_v2_gc --num-threads 8
Config: NoGc, Time: 25.9836
Config: OnFinishGc, Time: 49.012
Config: 100msPeriodicGc, Time: 45.9856
Config: 1000msPeriodicGc, Time: 40.3094
```
```
(TOOLCHAIN) mtomic@poso:~/memgraph/build_release$ tests/benchmark/storage_v2_gc --num-threads 7
Config: NoGc, Time: 20.4256
Config: OnFinishGc, Time: 39.6669
Config: 100msPeriodicGc, Time: 30.7956
Config: 1000msPeriodicGc, Time: 35.128
```
It is not that bad if there is a core dedicated to doing garbage collection.
Reviewers: mferencevic, teon.banek
Reviewed By: mferencevic, teon.banek
Subscribers: pullbot
Differential Revision: https://phabricator.memgraph.io/D2168
2019-07-09 22:34:23 +08:00
|
|
|
|
2021-02-18 22:32:43 +08:00
|
|
|
utils::Synchronized<std::list<Transaction>, utils::SpinLock> committed_transactions_;
|
2021-06-14 21:47:57 +08:00
|
|
|
IsolationLevel isolation_level_;
|
[StorageV2] Implement GC
Summary:
Here are some numbers from the benchmark:
```
(TOOLCHAIN) mtomic@poso:~/memgraph/build_release$ tests/benchmark/storage_v2_gc --num-threads 8
Config: NoGc, Time: 25.9836
Config: OnFinishGc, Time: 49.012
Config: 100msPeriodicGc, Time: 45.9856
Config: 1000msPeriodicGc, Time: 40.3094
```
```
(TOOLCHAIN) mtomic@poso:~/memgraph/build_release$ tests/benchmark/storage_v2_gc --num-threads 7
Config: NoGc, Time: 20.4256
Config: OnFinishGc, Time: 39.6669
Config: 100msPeriodicGc, Time: 30.7956
Config: 1000msPeriodicGc, Time: 35.128
```
It is not that bad if there is a core dedicated to doing garbage collection.
Reviewers: mferencevic, teon.banek
Reviewed By: mferencevic, teon.banek
Subscribers: pullbot
Differential Revision: https://phabricator.memgraph.io/D2168
2019-07-09 22:34:23 +08:00
|
|
|
|
2019-09-13 17:18:17 +08:00
|
|
|
Config config_;
|
[StorageV2] Implement GC
Summary:
Here are some numbers from the benchmark:
```
(TOOLCHAIN) mtomic@poso:~/memgraph/build_release$ tests/benchmark/storage_v2_gc --num-threads 8
Config: NoGc, Time: 25.9836
Config: OnFinishGc, Time: 49.012
Config: 100msPeriodicGc, Time: 45.9856
Config: 1000msPeriodicGc, Time: 40.3094
```
```
(TOOLCHAIN) mtomic@poso:~/memgraph/build_release$ tests/benchmark/storage_v2_gc --num-threads 7
Config: NoGc, Time: 20.4256
Config: OnFinishGc, Time: 39.6669
Config: 100msPeriodicGc, Time: 30.7956
Config: 1000msPeriodicGc, Time: 35.128
```
It is not that bad if there is a core dedicated to doing garbage collection.
Reviewers: mferencevic, teon.banek
Reviewed By: mferencevic, teon.banek
Subscribers: pullbot
Differential Revision: https://phabricator.memgraph.io/D2168
2019-07-09 22:34:23 +08:00
|
|
|
utils::Scheduler gc_runner_;
|
|
|
|
std::mutex gc_lock_;
|
2019-07-22 23:05:00 +08:00
|
|
|
|
|
|
|
// Undo buffers that were unlinked and now are waiting to be freed.
|
2021-02-18 22:32:43 +08:00
|
|
|
utils::Synchronized<std::list<std::pair<uint64_t, std::list<Delta>>>, utils::SpinLock> garbage_undo_buffers_;
|
2019-07-22 23:05:00 +08:00
|
|
|
|
2019-07-25 23:11:45 +08:00
|
|
|
// Vertices that are logically deleted but still have to be removed from
|
|
|
|
// indices before removing them from the main storage.
|
2019-07-22 23:05:00 +08:00
|
|
|
utils::Synchronized<std::list<Gid>, utils::SpinLock> deleted_vertices_;
|
2019-07-25 23:11:45 +08:00
|
|
|
|
|
|
|
// Vertices that are logically deleted and removed from indices and now wait
|
|
|
|
// to be removed from the main storage.
|
|
|
|
std::list<std::pair<uint64_t, Gid>> garbage_vertices_;
|
|
|
|
|
|
|
|
// Edges that are logically deleted and wait to be removed from the main
|
|
|
|
// storage.
|
2019-07-22 23:05:00 +08:00
|
|
|
utils::Synchronized<std::list<Gid>, utils::SpinLock> deleted_edges_;
|
2019-10-01 19:42:27 +08:00
|
|
|
|
2020-07-25 00:26:36 +08:00
|
|
|
// Durability
|
|
|
|
std::filesystem::path snapshot_directory_;
|
|
|
|
std::filesystem::path wal_directory_;
|
|
|
|
std::filesystem::path lock_file_path_;
|
|
|
|
utils::OutputFile lock_file_handle_;
|
|
|
|
|
|
|
|
utils::Scheduler snapshot_runner_;
|
2021-06-30 18:31:30 +08:00
|
|
|
utils::SpinLock snapshot_lock_;
|
2020-07-25 00:26:36 +08:00
|
|
|
|
|
|
|
// UUID used to distinguish snapshots and to link snapshots to WALs
|
|
|
|
std::string uuid_;
|
|
|
|
// Sequence number used to keep track of the chain of WALs.
|
|
|
|
uint64_t wal_seq_num_{0};
|
|
|
|
|
2020-12-01 23:51:25 +08:00
|
|
|
// UUID to distinguish different main instance runs for replication process
|
|
|
|
// on SAME storage.
|
|
|
|
// Multiple instances can have same storage UUID and be MAIN at the same time.
|
|
|
|
// We cannot compare commit timestamps of those instances if one of them
|
|
|
|
// becomes the replica of the other so we use epoch_id_ as additional
|
|
|
|
// discriminating property.
|
|
|
|
// Example of this:
|
|
|
|
// We have 2 instances of the same storage, S1 and S2.
|
|
|
|
// S1 and S2 are MAIN and accept their own commits and write them to the WAL.
|
|
|
|
// At the moment when S1 commited a transaction with timestamp 20, and S2
|
|
|
|
// a different transaction with timestamp 15, we change S2's role to REPLICA
|
|
|
|
// and register it on S1.
|
|
|
|
// Without using the epoch_id, we don't know that S1 and S2 have completely
|
|
|
|
// different transactions, we think that the S2 is behind only by 5 commits.
|
|
|
|
std::string epoch_id_;
|
|
|
|
// History of the previous epoch ids.
|
|
|
|
// Each value consists of the epoch id along the last commit belonging to that
|
|
|
|
// epoch.
|
|
|
|
std::deque<std::pair<std::string, uint64_t>> epoch_history_;
|
|
|
|
|
2020-07-25 00:26:36 +08:00
|
|
|
std::optional<durability::WalFile> wal_file_;
|
|
|
|
uint64_t wal_unsynced_transactions_{0};
|
2020-10-27 17:11:43 +08:00
|
|
|
|
2020-11-12 16:55:56 +08:00
|
|
|
utils::FileRetainer file_retainer_;
|
|
|
|
|
2021-01-19 19:10:06 +08:00
|
|
|
// Global locker that is used for clients file locking
|
|
|
|
utils::FileRetainer::FileLocker global_locker_;
|
|
|
|
|
2020-12-01 23:51:25 +08:00
|
|
|
// Last commited timestamp
|
2020-11-25 19:08:26 +08:00
|
|
|
std::atomic<uint64_t> last_commit_timestamp_{kTimestampInitialId};
|
2020-11-01 22:15:06 +08:00
|
|
|
|
2020-12-01 23:51:25 +08:00
|
|
|
class ReplicationServer;
|
|
|
|
std::unique_ptr<ReplicationServer> replication_server_{nullptr};
|
|
|
|
|
|
|
|
class ReplicationClient;
|
|
|
|
// We create ReplicationClient using unique_ptr so we can move
|
|
|
|
// newly created client into the vector.
|
|
|
|
// We cannot move the client directly because it contains ThreadPool
|
|
|
|
// which cannot be moved. Also, the move is necessary because
|
|
|
|
// we don't want to create the client directly inside the vector
|
|
|
|
// because that would require the lock on the list putting all
|
|
|
|
// commits (they iterate list of clients) to halt.
|
2020-12-11 17:47:19 +08:00
|
|
|
// This way we can initialize client in main thread which means
|
|
|
|
// that we can immediately notify the user if the initialization
|
2020-12-01 23:51:25 +08:00
|
|
|
// failed.
|
2021-02-18 22:32:43 +08:00
|
|
|
using ReplicationClientList = utils::Synchronized<std::vector<std::unique_ptr<ReplicationClient>>, utils::SpinLock>;
|
2020-11-12 16:55:56 +08:00
|
|
|
ReplicationClientList replication_clients_;
|
2020-11-01 22:15:06 +08:00
|
|
|
|
2020-11-19 21:26:03 +08:00
|
|
|
std::atomic<ReplicationRole> replication_role_{ReplicationRole::MAIN};
|
2019-06-26 22:01:51 +08:00
|
|
|
};
|
|
|
|
|
|
|
|
} // namespace storage
|