diff --git a/include/barrier/barrier.hpp b/include/barrier/barrier.hpp index 179599328..c19ef6751 100644 --- a/include/barrier/barrier.hpp +++ b/include/barrier/barrier.hpp @@ -10,8 +10,8 @@ // for querys. namespace barrier { -// Every class which must be visible to outside the barrier should have there -// barrier class defined here. +// Every class from database which must be visible to outside the barrier should +// have there barrier class defined here. // ************ Here should be forward declarations of Sized barrier classes // ACCESSORS @@ -298,7 +298,8 @@ public: Count count(); }; -// TODO: Find reasons of such great size ant try to decrease it. +// NOTE: This large size is because of SkipList accessor which is embeded into +// iterator. The accessor has 64 fields of pointers which is in total 512 B. class VertexAccessIterator : public Sized<560, 8>, public iter::Composable<const VertexAccessor, VertexAccessIterator> @@ -318,7 +319,8 @@ public: Count count(); }; -// TODO: Find reasons of such great size ant try to decrease it. +// NOTE: This large size is because of SkipList accessor which is embeded into +// iterator. The accessor has 64 fields of pointers which is in total 512 B. class EdgeAccessIterator : public Sized<560, 8>, public iter::Composable<const EdgeAccessor, EdgeAccessIterator> @@ -476,7 +478,6 @@ public: void write(const EdgeAccessor &edge); void write(const VertexStoredProperty &prop); void write(const EdgeStoredProperty &prop); - void write_null(); void write(const Null &v); void write(const Bool &prop); void write(const Float &prop); diff --git a/include/barrier/common.hpp b/include/barrier/common.hpp index 4a4764ec7..376353e22 100644 --- a/include/barrier/common.hpp +++ b/include/barrier/common.hpp @@ -6,7 +6,7 @@ #include <utility> #include <vector> -// THis shoul be the only place to include code from memgraph other than +// This shoul be the only place to include code from memgraph other than // barrier.cpp #include "mvcc/id.hpp" #include "storage/indexes/index_definition.hpp" @@ -28,30 +28,35 @@ namespace barrier x(ArrayInt64) x(ArrayFloat) x(ArrayDouble) x(ArrayBool) x(ArrayString) // **************************** HELPER FUNCTIONS **************************** // +// CASTS FROM& -> TO& template <class TO, class FROM> TO &ref_as(FROM &ref) { return (*reinterpret_cast<TO *>(&ref)); } +// CASTS FROM const& -> TO const& template <class TO, class FROM> TO const &ref_as(FROM const &ref) { return (*reinterpret_cast<TO const *>(&ref)); } +// CASTS FROM* -> TO* template <class TO, class FROM> TO *ptr_as(FROM *ref) { return (reinterpret_cast<TO *>(ref)); } +// CASTS FROM const* -> TO const* template <class TO, class FROM> TO const *ptr_as(FROM const *ref) { return (reinterpret_cast<TO const *>(ref)); } +// CASTS FROM&& -> TO&& template <class TO, class FROM> TO &&value_as(FROM &&ref) { @@ -61,6 +66,7 @@ TO &&value_as(FROM &&ref) return (reinterpret_cast<TO &&>(std::move(ref))); } +// CASTS FROM const&& -> TO const&& template <class TO, class FROM> const TO &&value_as(const FROM &&ref) { @@ -71,7 +77,8 @@ const TO &&value_as(const FROM &&ref) } // Barrier classes which will be used only through reference/pointer should -// inherit this class. +// inherit this class. Outside of barrier derived classes will be used only +// through reference/pointer. class Unsized { public: @@ -95,7 +102,7 @@ class Sized { protected: // This will ensure that this/derived class can't be instantiated. - // This way side outside the barrier can't "accidentaly" create this/derived + // Something outside the barrier can't "accidentaly" create this/derived // type because that would be erroneous. Sized() = delete; @@ -123,28 +130,9 @@ protected: "Border class aligment mismatch"); } -public: - typename std::aligned_storage<size_B, alignment_B>::type &_data_ref() - { - return data; - } - - typename std::aligned_storage<size_B, alignment_B>::type const & - _data_ref_const() const - { - return data; - } - private: // Here is the aligned storage which imitates size and aligment of object of // original class from memgraph. typename std::aligned_storage<size_B, alignment_B>::type data; }; - -// HELPER FUNCTIONS -template <class R> -bool option_fill(Option<R> &o) -{ - return o.is_present() && o.get().fill(); -} } diff --git a/include/barrier/trans.hpp b/include/barrier/trans.hpp index 89f85ea55..641b99526 100644 --- a/include/barrier/trans.hpp +++ b/include/barrier/trans.hpp @@ -160,6 +160,10 @@ namespace barrier // Blueprint for valid transformation of references: // TRANSFORM_REF(, ::); // template <class T> TRANSFORM_REF_TEMPLATED(<T>,::<T>); +// TODO: Strongest assurance that evertyhing is correct is for all of following +// transformation to use DUP for defining theres transformation. This would mean +// that names of classes exported in barrier and names from real class in +// database are equal. // ***************** TRANSFORMS of reference and pointers DUP(Label, TRANSFORM_REF); diff --git a/include/data_structures/concurrent/common.hpp b/include/data_structures/concurrent/common.hpp index 35b1e3408..c0c69eaa8 100644 --- a/include/data_structures/concurrent/common.hpp +++ b/include/data_structures/concurrent/common.hpp @@ -5,6 +5,8 @@ using std::pair; +// Item stored in skiplist. Used by ConcurrentMap and ConcurrentMultiMap to +// store key and value but to make ordering on keys. template <typename K, typename T> class Item : public TotalOrdering<Item<K, T>>, public TotalOrdering<K, Item<K, T>>, @@ -45,6 +47,8 @@ public: } }; +// Common base for accessor of all derived containers(ConcurrentMap, +// ConcurrentSet, ...) from SkipList. template <typename T> class AccessorBase { diff --git a/include/data_structures/concurrent/concurrent_list.hpp b/include/data_structures/concurrent/concurrent_list.hpp index c5a1af6f0..52aa6b74e 100644 --- a/include/data_structures/concurrent/concurrent_list.hpp +++ b/include/data_structures/concurrent/concurrent_list.hpp @@ -5,7 +5,8 @@ #include <utility> #include "utils/crtp.hpp" -// TODO: reimplement this +// TODO: reimplement this. It's correct but somewhat inefecient and it could be +// done better. template <class T> class ConcurrentList { @@ -24,7 +25,7 @@ private: template <class V> static bool cas(std::atomic<V> &atomic, V expected, V desired) - { // Could be relaxed must be atleast Release. + { // Could be relaxed but must be at least Release. return atomic.compare_exchange_strong(expected, desired, std::memory_order_seq_cst); } @@ -35,18 +36,28 @@ private: return atomic.exchange(desired, std::memory_order_seq_cst); } + // Basic element in a ConcurrentList class Node { public: Node(const T &data) : data(data) {} Node(T &&data) : data(std::move(data)) {} + // Carried data T data; + + // Next element in list or nullptr if end. std::atomic<Node *> next{nullptr}; + + // Next removed element in list or nullptr if end. std::atomic<Node *> next_rem{nullptr}; + + // True if node has logicaly been removed from list. std::atomic<bool> removed{false}; }; + // Base for Mutable and Immutable iterators. Also serves as accessor to the + // list uses for safe garbage disposall. template <class It> class IteratorBase : public Crtp<It> { @@ -58,7 +69,9 @@ private: IteratorBase(ConcurrentList *list) : list(list) { assert(list != nullptr); + // Increment number of iterators accessing list. list->count++; + // Start from the begining of list. reset(); } @@ -80,14 +93,19 @@ private: } auto head_rem = load(list->removed); + + // Next IF checks if this thread is responisble for disposall of + // collected garbage. // Fetch could be relaxed // There exist possibility that no one will delete garbage at this - // time. - if (list->count.fetch_sub(1) == 1 && head_rem != nullptr && - cas<Node *>( - list->removed, head_rem, - nullptr)) { // I am the last one and there is garbage to be - // removed. + // time but it will be deleted at some other time. + if (list->count.fetch_sub(1) == 1 && // I am the last one accessing + head_rem != nullptr && // There is some garbage + cas<Node *>(list->removed, head_rem, + nullptr) // No new garbage was added. + ) { + // Delete all removed node following chain of next_rem starting + // from head_rem. auto now = head_rem; do { auto next = load(now->next_rem); @@ -120,7 +138,9 @@ private: do { prev = curr; curr = load(curr->next); - } while (valid() && is_removed()); + } while (valid() && is_removed()); // Loop ends if end of list is + // found or if not removed + // element is found. return this->derived(); } It &operator++(int) { return operator++(); } @@ -136,7 +156,7 @@ private: { prev = nullptr; curr = load(list->head); - while (valid() && is_removed()) { + if (valid() && is_removed()) { operator++(); } } @@ -150,9 +170,12 @@ private: // leak is less dangerous. auto node = new Node(data); Node *next = nullptr; + // Insert at begining of list. Retrys on failure. do { next = load(list->head); + // First connect to next. store(node->next, next); + // Then try to set as head. } while (!cas(list->head, next, node)); } @@ -165,11 +188,17 @@ private: bool remove() { assert(valid()); + // Try to logically remove it. if (cas(curr->removed, false, true)) { // I removed it!!! + // Try to disconnect it from list. if (!disconnect()) { + // Disconnection failed because Node relative location in + // list changed. Whe firstly must find it again and then try + // to disconnect it again. find_and_disconnect(); } + // Add to list of to be garbage collected. store(curr->next_rem, swap(list->removed, curr)); return true; } @@ -184,6 +213,8 @@ private: friend bool operator!=(const It &a, const It &b) { return !(a == b); } private: + // Fids current element starting from the begining of the list Retrys + // until it succesffuly disconnects it. void find_and_disconnect() { Node *bef = nullptr; @@ -191,28 +222,34 @@ private: auto next = load(curr->next); while (now != nullptr) { if (now == curr) { - prev = bef; + // Found it. + prev = bef; // Set the correct previous node in list. if (disconnect()) { + // succesffuly disconnected it. return; } + // Let's try again from the begining. bef = nullptr; now = load(list->head); } else if (now == next) { // Comparison with next is // optimization for early return. return; } else { + // Now isn't the one whe are looking for lets try next one. bef = now; now = load(now->next); } } } + // Trys to disconnect currrent element from bool disconnect() { auto next = load(curr->next); if (prev != nullptr) { store(prev->next, next); if (load(prev->removed)) { + // previous isn't previous any more. return false; } } else if (!cas(list->head, curr, next)) { @@ -278,14 +315,10 @@ public: Iterator begin() { return Iterator(this); } - // ConstIterator begin() { return ConstIterator(this); } - ConstIterator cbegin() { return ConstIterator(this); } Iterator end() { return Iterator(); } - // ConstIterator end() { return ConstIterator(); } - ConstIterator cend() { return ConstIterator(); } std::size_t size() { return count.load(std::memory_order_consume); } diff --git a/include/data_structures/concurrent/concurrent_map.hpp b/include/data_structures/concurrent/concurrent_map.hpp index b96906cf3..8f7fb904c 100644 --- a/include/data_structures/concurrent/concurrent_map.hpp +++ b/include/data_structures/concurrent/concurrent_map.hpp @@ -5,6 +5,9 @@ using std::pair; +// Multi thread safe map based on skiplist. +// K - type of key. +// T - type of data. template <typename K, typename T> class ConcurrentMap { @@ -57,11 +60,13 @@ public: list_it find(const K &key) { return accessor.find(key); } + // Returns iterator to item or first larger if it doesn't exist. list_it_con find_or_larger(const T &item) const { return accessor.find_or_larger(item); } + // Returns iterator to item or first larger if it doesn't exist. list_it find_or_larger(const T &item) { return accessor.find_or_larger(item); diff --git a/include/data_structures/concurrent/concurrent_multimap.hpp b/include/data_structures/concurrent/concurrent_multimap.hpp index 1ae53ce75..e860438cd 100644 --- a/include/data_structures/concurrent/concurrent_multimap.hpp +++ b/include/data_structures/concurrent/concurrent_multimap.hpp @@ -5,6 +5,9 @@ using std::pair; +// Multi thread safe multi map based on skiplist. +// K - type of key. +// T - type of data. template <typename K, typename T> class ConcurrentMultiMap { @@ -53,11 +56,13 @@ public: list_it find(const K &key) { return accessor.find(key); } + // Returns iterator to item or first larger if it doesn't exist. list_it_con find_or_larger(const T &item) const { return accessor.find_or_larger(item); } + // Returns iterator to item or first larger if it doesn't exist. list_it find_or_larger(const T &item) { return accessor.find_or_larger(item); diff --git a/include/data_structures/concurrent/concurrent_multiset.hpp b/include/data_structures/concurrent/concurrent_multiset.hpp index 858620b1c..980293280 100644 --- a/include/data_structures/concurrent/concurrent_multiset.hpp +++ b/include/data_structures/concurrent/concurrent_multiset.hpp @@ -2,6 +2,8 @@ #include "data_structures/concurrent/skiplist.hpp" +// Multi thread safe multiset based on skiplist. +// T - type of data. template <class T> class ConcurrentMultiSet { @@ -36,11 +38,13 @@ public: list_it find(const T &item) { return accessor.find(item); } + // Returns iterator to item or first larger if it doesn't exist. list_it_con find_or_larger(const T &item) const { return accessor.find_or_larger(item); } + // Returns iterator to item or first larger if it doesn't exist. list_it find_or_larger(const T &item) { return accessor.find_or_larger(item); diff --git a/include/data_structures/concurrent/concurrent_set.hpp b/include/data_structures/concurrent/concurrent_set.hpp index e0d749bee..dc5bb69cc 100644 --- a/include/data_structures/concurrent/concurrent_set.hpp +++ b/include/data_structures/concurrent/concurrent_set.hpp @@ -3,6 +3,8 @@ #include "data_structures/concurrent/common.hpp" #include "data_structures/concurrent/skiplist.hpp" +// Multi thread safe set based on skiplist. +// T - type of data. template <class T> class ConcurrentSet { @@ -37,18 +39,21 @@ public: list_it find(const T &item) { return accessor.find(item); } + // Returns iterator to item or first larger if it doesn't exist. template <class K> list_it_con find_or_larger(const K &item) const { return accessor.find_or_larger(item); } + // Returns iterator to item or first larger if it doesn't exist. template <class K> list_it find_or_larger(const K &item) { return accessor.find_or_larger(item); } + // Returns iterator to item or first larger if it doesn't exist. template <class K> list_it_con cfind_or_larger(const K &item) { diff --git a/include/data_structures/concurrent/skiplist.hpp b/include/data_structures/concurrent/skiplist.hpp index f2450f8c9..03254fa08 100644 --- a/include/data_structures/concurrent/skiplist.hpp +++ b/include/data_structures/concurrent/skiplist.hpp @@ -334,6 +334,8 @@ public: MultiIterator(SkipList *skiplist, const K &data) : data(data), skiplist(skiplist) { + // WHe must find the first element with K key. + // All of logic in this loop was taken from insert method. while (true) { auto level = find_path(skiplist, H - 1, data, preds, succs); if (level == -1) { @@ -368,32 +370,36 @@ public: return succs[0]->value(); } - // WRONG THIS CAN POSSIBLY NOT BE TRUE IF SOMEONE JUST AFTER THIS REMOVE - // ELEMENT AFTER THIS ONE. - // bool has_next() - // { - // assert(succs[0] != nullptr); - // return succs[0].forward(0) != nullptr; - // } - bool has_value() { return succs[0] != nullptr; } MultiIterator &operator++() { assert(succs[0] != nullptr); - // This whole method can be optimized if it's valid to expect height + // NOTE: This whole method can be optimized if it's valid to expect + // height // of 1 on same key elements. + + // First update preds and succs. for (int i = succs[0]->height - 1; i >= 0; i--) { preds[i] = succs[i]; succs[i] = preds[i]->forward(i); } + + // If there exists current value then check if it is equal to our + // data. if (succs[0] != nullptr) { if (succs[0]->value() != data) { + // Current data isn't equal to our data that means that this + // is the end of list of same values. succs[0] = nullptr; } else { + // Current value is same as our data but whe must check that + // it is valid data and if not whe must wait for it to + // become valid. while (succs[0] != succs[succs[0]->height - 1] || !succs[0]->flags.is_fully_linked()) { - usleep(250); + usleep(250); // Wait to become linked + // Reget succs. for (int i = succs[0]->height - 1; i >= 0; i--) { succs[i] = preds[i]->forward(i); } @@ -427,6 +433,8 @@ public: bool remove() { assert(succs[0] != nullptr); + // Calls skiplist remove method. + return skiplist->template remove<K>( data, preds, succs, SkipList<T>::template MultiIterator<K>::update_path); @@ -436,14 +444,18 @@ public: static int update_path(SkipList *skiplist, int start, const K &item, Node *preds[], Node *succs[]) { + // NOTE: This could be done more efficent than serching for item + // element again. Whe just need to use infromation already present + // in preds and succ because whe know that this method is used + // exclusively by passing it into skiplist remove method from + // MultiIterator remove method. + // One optimization here would be to wait for is_fully_linked to be // true. That way that doesnt have to be done in constructor and // ++ operator. int level_found = succs[0]->height - 1; assert(succs[0] == succs[level_found]); - // for (int i = level_found; i >= 0; i--) { - // // Someone has done something - // if (preds[i]->forward(i) != succs[i]) { + for (auto it = MultiIterator<K>(skiplist, item); it.has_value(); it++) { if (it.succs[0] == succs[0]) { // Found it @@ -453,12 +465,7 @@ public: } } // Someone removed it - // assert(succs[0]->flags.is_marked()); return -1; - // } - // } - // // Everything is fine - // return level_found; } const K &data; @@ -751,10 +758,12 @@ private: return valid; } + // Inserts non unique data into list. + // NOTE: Uses modified logic from insert method. Iterator insert_non_unique(T &&data, Node *preds[], Node *succs[]) { while (true) { - // TODO: before here was data.first + auto level = find_path(this, H - 1, data, preds, succs); auto height = 1; @@ -849,7 +858,7 @@ private: } // Insert unique data - // TODO: This is almost all duplicate code from insert + // NOTE: This is almost all duplicate code from insert. template <class K, class... Args> std::pair<Iterator, bool> emplace(Node *preds[], Node *succs[], K &key, Args &&... args) diff --git a/include/data_structures/map/rh_common.hpp b/include/data_structures/map/rh_common.hpp index c3a016943..5df918c9b 100644 --- a/include/data_structures/map/rh_common.hpp +++ b/include/data_structures/map/rh_common.hpp @@ -20,11 +20,7 @@ protected: public: Combined() : data(0) {} - Combined(D *data, size_t off) - { - // assert((((size_t)data) & 0x7) == 0 && off < 8); - this->data = ((size_t)data) | off; - } + Combined(D *data, size_t off) { this->data = ((size_t)data) | off; } bool valid() const { return data != 0; } @@ -72,6 +68,7 @@ protected: size_t data; }; + // Base for all iterators. It can start from any point in map. template <class It> class IteratorBase : public Crtp<It> { @@ -97,7 +94,11 @@ protected: } const RhBase *map; + + // How many times did whe advance. size_t advanced; + + // Current position in array size_t index; public: @@ -123,12 +124,15 @@ protected: do { advanced++; if (advanced >= map->capacity) { + // Whe have advanced more than the capacity of map is so whe + // are done. map = nullptr; advanced = index = ~((size_t)0); break; } index = (index + 1) & mask; - } while (!map->array[index].valid()); + } while (!map->array[index].valid()); // Check if there is element + // at current position. return this->derived(); } @@ -221,6 +225,7 @@ public: ConstIterator cend() const { return ConstIterator(); } protected: + // Copys RAW BYTE data from other RhBase. void copy_from(const RhBase &other) { capacity = other.capacity; @@ -235,6 +240,7 @@ protected: } } + // Takes data from other RhBase. void take_from(RhBase &&other) { capacity = other.capacity; @@ -245,16 +251,17 @@ protected: other.capacity = 0; } - void init_array(size_t size) + // Initiazes array with given capacity. + void init_array(size_t capacity) { - size_t bytes = sizeof(Combined) * size; + size_t bytes = sizeof(Combined) * capacity; array = (Combined *)malloc(bytes); std::memset(array, 0, bytes); - capacity = size; + this->capacity = capacity; } // True if before array has some values. - // Before array has to be released also. + // Before array must be released in the caller. bool increase_size() { if (capacity == 0) { @@ -276,6 +283,7 @@ protected: } public: + // Cleares all data. void clear() { free(array); @@ -297,7 +305,7 @@ protected: return hash(std::hash<K>()(key)) & mask; } - // This is rather expensive but offers good distribution. + // NOTE: This is rather expensive but offers good distribution. size_t hash(size_t x) const { x = (x ^ (x >> 30)) * UINT64_C(0xbf58476d1ce4e5b9); diff --git a/include/data_structures/map/rh_hashmap.hpp b/include/data_structures/map/rh_hashmap.hpp index 4bc8df942..277cc53f7 100644 --- a/include/data_structures/map/rh_hashmap.hpp +++ b/include/data_structures/map/rh_hashmap.hpp @@ -1,7 +1,7 @@ #include <functional> -#include "utils/crtp.hpp" #include "data_structures/map/rh_common.hpp" +#include "utils/crtp.hpp" #include "utils/option_ptr.hpp" // HashMap with RobinHood collision resolution policy. @@ -47,17 +47,20 @@ public: size_t now = index(key, mask); size_t off = 0; size_t border = 8 <= capacity ? 8 : capacity; + while (off < border) { Combined other = array[now]; if (other.valid()) { auto other_off = other.off(); if (other_off == off && key == other.ptr()->get_key()) { + // Found data. return OptionPtr<D>(other.ptr()); } else if (other_off < off) { // Other is rich break; } // Else other has equal or greater offset, so he is poor. } else { + // Empty slot means that there is no searched data. break; } @@ -76,16 +79,20 @@ public: size_t now = index(key, mask); size_t off = 0; size_t border = 8 <= capacity ? 8 : capacity; + while (off < border) { Combined other = array[now]; if (other.valid()) { auto other_off = other.off(); if (other_off == off && key == other.ptr()->get_key()) { + // Element already exists. return false; } else if (other_off < off) { // Other is rich + // Set data. array[now] = Combined(data, off); + // Move other data to the higher indexes, while (other.increment_off()) { now = (now + 1) & mask; auto tmp = array[now]; @@ -97,9 +104,11 @@ public: } } data = other.ptr(); - break; // Cant insert removed element + break; // Cant insert removed element because it would + // be to far from his real place. } // Else other has equal or greater offset, so he is poor. } else { + // Data can be placed in this empty slot. array[now] = Combined(data, off); count++; return true; @@ -110,6 +119,8 @@ public: } } + // There isn't enough space for element pointed by data so whe must + // increase array. increase_size(); return insert(data); } @@ -121,6 +132,7 @@ public: size_t now = index(key, mask); size_t off = 0; size_t border = 8 <= capacity ? 8 : capacity; + while (off < border) { Combined other = array[now]; if (other.valid()) { @@ -130,6 +142,7 @@ public: key == other_ptr->get_key()) { // Found it auto before = now; + // Whe must move other elements one slot lower. do { // This is alright even for off=0 on found element // because it wont be seen. @@ -139,7 +152,10 @@ public: before = now; now = (now + 1) & mask; other = array[now]; - } while (other.valid() && other.off() > 0); + } while (other.valid() && + other.off() > 0); // Exit if whe encounter empty + // slot or data which is exactly + // in slot which it want's to be. array[before] = Combined(); count--; @@ -149,6 +165,7 @@ public: break; } // Else other has equal or greater offset, so he is poor. } else { + // If the element to be removed existed in map it would be here. break; } diff --git a/include/data_structures/map/rh_hashmultimap.hpp b/include/data_structures/map/rh_hashmultimap.hpp index 23daa328b..ce1b0aad0 100644 --- a/include/data_structures/map/rh_hashmultimap.hpp +++ b/include/data_structures/map/rh_hashmultimap.hpp @@ -15,6 +15,27 @@ // D must have method K& get_key() // K must be comparable with ==. // HashMap behaves as if it isn't owner of entrys. +// +// Main idea of this MultiMap is a tweak of logic in RobinHood. +// RobinHood offset from prefered slot is equal to the number of slots between +// [current slot and prefered slot>. +// While in this flavour of "multi RobinHood" offset from prefered slot is equal +// to the number of different keyed elements between his current slot and +// prefered slot. +// In the following examples slots will have keys as caracters. So something +// like this: |a| will mean that in this slot there is data with key 'a'. +// like this: | | will mean empty slot. +// like this: |...| will mean arbitary number of slots. +// like this: |b:a| will mean that a want's to be in slot but b is in't. +// +// Examples: +// |...|a:a|...| => off(a) = 0 +// |...|a:a|a|...|a|...| => off(a) = 0 +// |...|b:a|a|...| => off(a) = 1 +// |...|b:a|b|...|b|a|...| => off(a) = 1 +// |...|c:a|b|a|...| => off(a) = 2 +// |...|c:a|c|...|c|b|...|b||a|...|a|...| => off(a) = 2 +// ... template <class K, class D, size_t init_size_pow2 = 2> class RhHashMultiMap : public RhBase<K, D, init_size_pow2> { @@ -124,14 +145,18 @@ public: bool multi = false; if (other_off == off && other.ptr()->get_key() == key) { // Found the same + // Must skip same keyd values to insert new value at the + // end. do { now = (now + 1) & mask; other = array[now]; if (!other.valid()) { + // Found empty slot in which data ca be added. set(now, data, off); return; } } while (other.equal(key, off)); + // There is no empty slot after same keyed values. multi = true; } else if (other_off > off || other_poor(other, mask, start, @@ -142,6 +167,8 @@ public: continue; } + // Data will be insrted at current slot and all other data + // will be displaced for one slot. array[now] = Combined(data, off); auto start_insert = now; while (is_off_adjusted(other, mask, start_insert, now, @@ -152,6 +179,7 @@ public: array[now] = other; other = tmp; if (!other.valid()) { + // Found empty slot which means i can finish now. count++; return; } @@ -159,12 +187,14 @@ public: data = other.ptr(); break; // Cant insert removed element } else { + // Found empty slot for data. set(now, data, off); return; } } } + // There is't enough space for data. increase_size(); add(data); } @@ -179,14 +209,18 @@ public: size_t off = 0; size_t border = 8 <= capacity ? 8 : capacity; Combined other = array[now]; + while (other.valid() && off < border) { const size_t other_off = other.off(); if (other_off == off && key == other.ptr()->get_key()) { + // Found same key data. auto founded = capacity; size_t started = now; bool multi = false; + // Must find slot with searched data. do { if (other.ptr() == data) { + // founded it. founded = now; } now = (now + 1) & mask; @@ -196,11 +230,14 @@ public: break; } } while (other.equal(key, off) && (multi = true)); - // multi = true is correct + if (founded == capacity) { + // Didn't found the data. return false; } + // Data will be removed by moving other data by one slot + // before. auto bef = before_index(now, mask); array[founded] = array[bef]; @@ -223,6 +260,8 @@ public: break; } else { // Else other has equal or greater off, so he is poor. + // Must skip values of same keys but different key than + // data. if (UNLIKELY(skip(now, other, other_off, mask))) { break; } @@ -317,54 +356,3 @@ private: (end < start && p <= start && p > end); } }; - -// Unnecessary -// // Removes element. Returns removed element if it existed. It doesn't -// // specify which element from same key group will be removed. -// OptionPtr<D> remove(const K &key_in) -// { -// -// if (count > 0) { -// auto key = std::ref(key_in); -// size_t mask = this->mask(); -// size_t now = index(key, mask); -// size_t off = 0; -// size_t checked = 0; -// size_t border = 8 <= capacity ? 8 : capacity; -// Combined other = array[now]; -// while (other.valid() && off < border) { -// auto other_off = other.off(); -// bool multi = false; -// if (other_off == off && key == other.ptr()->get_key()) { -// do { -// now = (now + 1) & mask; -// other = array[now]; -// if (!other.valid()) { -// break; -// } -// other_off = other.off(); -// } while (other_off == off && -// other.ptr()->get_key() == key && -// (multi = true)); // multi = true is correct -// -// auto bef = before_index(now, mask); -// auto ret = OptionPtr<D>(array[bef].ptr()); -// -// move_before(now, bef, other, mask, multi); -// return ret; -// -// } else if (other_off < off) { // Other is rich -// break; -// -// } else { // Else other has equal or greater off, so he is -// poor. -// if (UNLIKELY(skip(now, other, other_off, mask))) { -// break; -// } -// off++; -// } -// } -// } -// -// return OptionPtr<D>(); -// } diff --git a/include/database/db.hpp b/include/database/db.hpp index 2ddd20a3d..a9b18d4c7 100644 --- a/include/database/db.hpp +++ b/include/database/db.hpp @@ -20,9 +20,17 @@ class Db public: using sptr = std::shared_ptr<Db>; + // import_snapshot will in constructor import latest snapshot into the db. + // NOTE: explicit is here to prevent compiler from evaluating const char * + // into a bool. explicit Db(bool import_snapshot = true); + + // import_snapshot will in constructor import latest snapshot into the db. Db(const char *name, bool import_snapshot = true); + + // import_snapshot will in constructor import latest snapshot into the db. Db(const std::string &name, bool import_snapshot = true); + Db(const Db &db) = delete; private: diff --git a/include/database/db_accessor.hpp b/include/database/db_accessor.hpp index 931e357ad..f24acdd49 100644 --- a/include/database/db_accessor.hpp +++ b/include/database/db_accessor.hpp @@ -53,7 +53,9 @@ public: DbAccessor(Db &db, tx::Transaction &t); //*******************VERTEX METHODS + // Returns iterator of VertexAccessor for all vertices. // TODO: Implement class specaily for this return + // NOTE: This implementation must be here to be able to infere return type. auto vertex_access() { return iter::make_map( @@ -63,12 +65,16 @@ public: }); } + // Optionaly return vertex with given internal Id. Option<const VertexAccessor> vertex_find(const Id &id); // Creates new Vertex and returns filled VertexAccessor. VertexAccessor vertex_insert(); // ******************* EDGE METHODS + // Returns iterator of EdgeAccessor for all edges. + // TODO: Implement class specaily for this return + // NOTE: This implementation must be here to be able to infere return type. auto edge_access() { return iter::make_map( @@ -78,6 +84,7 @@ public: }); } + // Optionally return Edge with given internal Id. Option<const EdgeAccessor> edge_find(const Id &id); // Creates new Edge and returns filled EdgeAccessor. @@ -89,15 +96,17 @@ public: VertexAccessor const &to); // ******************* LABEL METHODS - + // Finds or crated label with given name. const Label &label_find_or_create(const char *name); + // True if label with name exists. bool label_contains(const char *name); // ******************** TYPE METHODS - + // Finds or creates edge_type with given name. const EdgeType &type_find_or_create(const char *name); + // True if edge_type with given name exists. bool type_contains(const char *name); // ******************** PROPERTY METHODS @@ -135,6 +144,8 @@ public: // True if commit was successful, or false if transaction was aborted. bool commit(); + + // Aborts transaction. void abort(); private: @@ -146,11 +157,3 @@ private: DbTransaction db_transaction; }; - -// ********************** CONVENIENT FUNCTIONS - -template <class R> -bool option_fill(Option<R> &o) -{ - return o.is_present() && o.get().fill(); -} diff --git a/include/database/db_transaction.hpp b/include/database/db_transaction.hpp index b6fabf86b..355cf94eb 100644 --- a/include/database/db_transaction.hpp +++ b/include/database/db_transaction.hpp @@ -28,14 +28,20 @@ public: // Cleans edge part of database. MUST be called by one cleaner thread at // one time. + // TODO: Should be exctracted to separate class which can enforce one thread + // at atime. void clean_edge_section(); // Cleans vertex part of database. MUST be called by one cleaner thread at // one time.. + // TODO: Should be exctracted to separate class which can enforce one thread + // at atime. void clean_vertex_section(); // Updates indexes of Vertex/Edges in index_updates. True if indexes are // updated successfully. False means that transaction failed. + // TODO: Should be moved to Indexes class where it will this DbTransaction + // as an argument. bool update_indexes(); // Will update indexes for given element TG::record_t. Actual update happens diff --git a/include/dbms/cleaner.hpp b/include/dbms/cleaner.hpp index 17930160c..47303339e 100644 --- a/include/dbms/cleaner.hpp +++ b/include/dbms/cleaner.hpp @@ -10,9 +10,10 @@ class Cleaning public: // How much sec is a cleaning_cycle in which cleaner will clean at most - // once. + // once. Starts cleaner thread. Cleaning(ConcurrentMap<std::string, Db> &dbs, size_t cleaning_cycle); + // Destroys this object after this thread joins cleaning thread. ~Cleaning(); private: @@ -22,5 +23,6 @@ private: std::vector<std::unique_ptr<Thread>> cleaners; + // Should i continue cleaning. std::atomic<bool> cleaning = {true}; }; diff --git a/include/dbms/dbms.hpp b/include/dbms/dbms.hpp index f34aa32e2..9612d1aad 100644 --- a/include/dbms/dbms.hpp +++ b/include/dbms/dbms.hpp @@ -30,7 +30,9 @@ private: // currently active database std::atomic<Db *> active_db; + // Cleaning thread. Cleaning cleaning = {dbs, CONFIG_INTEGER(config::CLEANING_CYCLE_SEC)}; + // Snapshoting thread. Snapshoter snapshoter = {dbs, CONFIG_INTEGER(config::SNAPSHOT_CYCLE_SEC)}; }; diff --git a/include/import/base_import.hpp b/include/import/base_import.hpp index 03fa1f1ce..793664ed9 100644 --- a/include/import/base_import.hpp +++ b/include/import/base_import.hpp @@ -98,7 +98,8 @@ public: return true; } - // Extracts parts while stripping data of array chars and qutation marks. + // Extracts parts of str while stripping parts of array chars and qutation + // marks. Parts are separated with delimiter. void extract(char *str, const char delimiter, vector<char *> &sub_str) { int head = 0; @@ -148,12 +149,9 @@ public: } sub_str.push_back(&str[head]); - // - // for (auto s : sub_str) { - // cout << "#" << s; - // } } + // Optionaly return vertex with given import local id if it exists. Option<VertexAccessor> const &get_vertex(size_t id) { if (vertices.size() > id) { @@ -168,6 +166,8 @@ public: DbAccessor &db; Logger logger; + // Varius marks and delimiters. They can be freely changed here and + // everything will work. char parts_mark = ','; char parts_array_mark = ','; char type_mark = ':'; @@ -176,6 +176,6 @@ public: char closed_bracket = ']'; protected: - // All created vertices which have import local id + // All created vertices which have import local id. vector<Option<VertexAccessor>> vertices; }; diff --git a/include/import/csv_import.hpp b/include/import/csv_import.hpp index 0865778ea..9f1283e45 100644 --- a/include/import/csv_import.hpp +++ b/include/import/csv_import.hpp @@ -46,7 +46,19 @@ bool equal_str(const char *a, const char *b) { return strcasecmp(a, b) == 0; } // CSV importer for importing multiple files regarding same graph. // CSV format of file should be following: +// header +// line of data +// line of data +// ... // +// Where header should be composed of parts splited by parts_mark. Number of +// parts should be same as number of parts in every line of data. Parts should +// be of format name:type where name is alfanumeric identifyer of data in thath +// column and type should be one of: id, from, to, label, type, bool, int, long, +// float, double, string, bool[], int[], long[], float[], double[], string[]. +// If name is missing the column data wont be saved into the elements. +// if the type is missing the column will be interperted as type string. If +// neither name nor type are present column will be skipped. class CSVImporter : public BaseImporter { @@ -70,6 +82,8 @@ public: private: // Loads data from file and returns number of loaded name. + // TG - TypeGroup + // F - function which will create element from filled element skelleton. template <class TG, class F> size_t import(std::fstream &file, F f, bool vertex) { @@ -104,10 +118,6 @@ private: size_t line_no = 1; ElementSkeleton es(db); while (std::getline(file, line)) { - // if (line_no % 1000 == 0) { - // cout << line_no << endl; - // } - // cout << line << endl; sub_str.clear(); es.clear(); @@ -196,6 +206,7 @@ private: if (tmp_vec.size() > 2) { logger.error("To much sub parts in header part"); return make_option<unique_ptr<Filler>>(); + } else if (tmp_vec.size() < 2) { if (tmp_vec.size() == 1) { logger.warn("Column: {} doesn't have specified type so string " @@ -203,16 +214,19 @@ private: tmp_vec[0]); name = tmp_vec[0]; type = _string; + } else { logger.warn("Empty colum definition, skiping column."); std::unique_ptr<Filler> f(new SkipFiller()); return make_option(std::move(f)); } + } else { name = tmp_vec[0]; type = tmp_vec[1]; } + // Create adequat filler if (equal_str(type, "id")) { std::unique_ptr<Filler> f( name[0] == '\0' ? new IdFiller<TG>() @@ -245,8 +259,6 @@ private: // *********************** PROPERTIES } else if (equal_str(type, "bool")) { - // return make_filler_property<BoolFiller>(vertex, name, - // Flags::Bool); std::unique_ptr<Filler> f( new BoolFiller<TG>(property_key<TG>(name, Flags::Bool))); return make_option(std::move(f)); diff --git a/include/import/element_skeleton.hpp b/include/import/element_skeleton.hpp index 3b93b0875..8543732f8 100644 --- a/include/import/element_skeleton.hpp +++ b/include/import/element_skeleton.hpp @@ -6,7 +6,7 @@ #include "storage/vertex_accessor.hpp" // Holder for element data which he can then insert as a vertex or edge into the -// database depending on the available data. +// database depending on the available data and called add_* method. class ElementSkeleton { @@ -99,15 +99,6 @@ public: Option<size_t> element_id() { return el_id; } private: - // template <class A> - // void add_propreties(A &ra) - // { - // for (auto prop : properties) { - // assert(prop.prop.is_present()); - // ra.set(prop.key, prop.prop.take()); - // } - // } - DbAccessor &db; Option<size_t> el_id; diff --git a/include/import/fillings/array.hpp b/include/import/fillings/array.hpp index 773578fd3..62c6da838 100644 --- a/include/import/fillings/array.hpp +++ b/include/import/fillings/array.hpp @@ -5,6 +5,10 @@ #include "import/fillings/filler.hpp" #include "utils/array_store.hpp" +// Parses Array of elements type T. +// TG - Type group +// T - type of element in array. +// A - property type in database for holding arrays. template <class TG, class T, class A> class ArrayFiller : public Filler { diff --git a/include/import/fillings/bool.hpp b/include/import/fillings/bool.hpp index 7d7e3e733..7c6640faf 100644 --- a/include/import/fillings/bool.hpp +++ b/include/import/fillings/bool.hpp @@ -6,6 +6,8 @@ #include "storage/model/properties/flags.hpp" #include "storage/model/properties/property_family.hpp" +// Parses boolean. +// TG - Type group template <class TG> class BoolFiller : public Filler { diff --git a/include/import/fillings/double.hpp b/include/import/fillings/double.hpp index 348d8da45..9d7cff45c 100644 --- a/include/import/fillings/double.hpp +++ b/include/import/fillings/double.hpp @@ -6,6 +6,8 @@ #include "storage/model/properties/flags.hpp" #include "storage/model/properties/property_family.hpp" +// Parses double. +// TG - Type group template <class TG> class DoubleFiller : public Filler { diff --git a/include/import/fillings/filler.hpp b/include/import/fillings/filler.hpp index 82f0b4dfa..27e331653 100644 --- a/include/import/fillings/filler.hpp +++ b/include/import/fillings/filler.hpp @@ -3,6 +3,8 @@ #include "import/element_skeleton.hpp" #include "utils/option.hpp" +// Common class for varius classes which accept one part from data line in +// import, parses it and adds it into element skelleton. class Filler { public: diff --git a/include/import/fillings/float.hpp b/include/import/fillings/float.hpp index 85a434c53..17d749ff8 100644 --- a/include/import/fillings/float.hpp +++ b/include/import/fillings/float.hpp @@ -6,6 +6,8 @@ #include "storage/model/properties/flags.hpp" #include "storage/model/properties/property_family.hpp" +// Parses float. +// TG - Type group template <class TG> class FloatFiller : public Filler { diff --git a/include/import/fillings/from.hpp b/include/import/fillings/from.hpp index c5569338a..435c6fc59 100644 --- a/include/import/fillings/from.hpp +++ b/include/import/fillings/from.hpp @@ -6,6 +6,7 @@ #include "storage/model/properties/flags.hpp" #include "storage/model/properties/property_family.hpp" +// Parses from id of vertex for edge. class FromFiller : public Filler { diff --git a/include/import/fillings/id.hpp b/include/import/fillings/id.hpp index f9e18d160..9808089c3 100644 --- a/include/import/fillings/id.hpp +++ b/include/import/fillings/id.hpp @@ -2,6 +2,8 @@ #include "import/fillings/filler.hpp" +// Parses import local Id. +// TG - Type group template <class TG> class IdFiller : public Filler { diff --git a/include/import/fillings/int32.hpp b/include/import/fillings/int32.hpp index 2203dd1f2..5816089b8 100644 --- a/include/import/fillings/int32.hpp +++ b/include/import/fillings/int32.hpp @@ -6,6 +6,8 @@ #include "storage/model/properties/flags.hpp" #include "storage/model/properties/property_family.hpp" +// Parses int32. +// TG - Type group template <class TG> class Int32Filler : public Filler { diff --git a/include/import/fillings/int64.hpp b/include/import/fillings/int64.hpp index fe31dbde4..437de3b8f 100644 --- a/include/import/fillings/int64.hpp +++ b/include/import/fillings/int64.hpp @@ -6,6 +6,8 @@ #include "storage/model/properties/flags.hpp" #include "storage/model/properties/property_family.hpp" +// Parses int64. +// TG - Type group template <class TG> class Int64Filler : public Filler { diff --git a/include/import/fillings/label.hpp b/include/import/fillings/label.hpp index 2f5753012..fe85c32f5 100644 --- a/include/import/fillings/label.hpp +++ b/include/import/fillings/label.hpp @@ -3,6 +3,7 @@ #include "database/db_accessor.hpp" #include "import/fillings/filler.hpp" +// Parses array of labels. class LabelFiller : public Filler { diff --git a/include/import/fillings/skip.hpp b/include/import/fillings/skip.hpp index 7b1952833..53e21fdc0 100644 --- a/include/import/fillings/skip.hpp +++ b/include/import/fillings/skip.hpp @@ -6,6 +6,7 @@ #include "storage/model/properties/flags.hpp" #include "storage/model/properties/property_family.hpp" +// Skips column. class SkipFiller : public Filler { diff --git a/include/import/fillings/string.hpp b/include/import/fillings/string.hpp index 38621f5a7..4e9014db9 100644 --- a/include/import/fillings/string.hpp +++ b/include/import/fillings/string.hpp @@ -6,6 +6,8 @@ #include "storage/model/properties/flags.hpp" #include "storage/model/properties/property_family.hpp" +// Parses string. +// TG - Type group template <class TG> class StringFiller : public Filler { diff --git a/include/import/fillings/to.hpp b/include/import/fillings/to.hpp index c07db3484..44249e521 100644 --- a/include/import/fillings/to.hpp +++ b/include/import/fillings/to.hpp @@ -6,6 +6,7 @@ #include "storage/model/properties/flags.hpp" #include "storage/model/properties/property_family.hpp" +// Parses to import local id of vertex for edge. class ToFiller : public Filler { diff --git a/include/import/fillings/type.hpp b/include/import/fillings/type.hpp index 577ecfbb9..5556e9dd4 100644 --- a/include/import/fillings/type.hpp +++ b/include/import/fillings/type.hpp @@ -3,6 +3,7 @@ #include "database/db_accessor.hpp" #include "import/fillings/filler.hpp" +// Parses type of edge. class TypeFiller : public Filler { diff --git a/include/query_engine/hardcode/queries.hpp b/include/query_engine/hardcode/queries.hpp index eccb605b2..53b4dbdbe 100644 --- a/include/query_engine/hardcode/queries.hpp +++ b/include/query_engine/hardcode/queries.hpp @@ -336,6 +336,21 @@ auto load_queries(Db &db) .has_property(prop_name, args[0]) .clone_to(n) // Savepoint .replace(r); // Load savepoint + // Above statments + .to().for_all([&](auto m) {}) will unrool into: + // for(auto edge:type.index.for_range(t)){ + // auto from_vertex=edge.from(); + // if(from_vertex.fill()){ + // auto &prop=from_vertex.at(prop_name); + // if(prop==args[0]){ + // auto to_vertex=edge.to(); + // if(to_vertex.fill()){ + // // Here you have all data. + // // n == from_vertex + // // m == to_vertex + // } + // } + // } + // } auto it_vertex = t.vertex_access() .fill() @@ -343,6 +358,24 @@ auto load_queries(Db &db) .clone_to(n) // Savepoint .out() .type(type); + // Above statments + .to().for_all([&](auto m) {}) will unrool into: + // for(auto from_vertex:t.vertex_access(t)){ + // if(from_vertex.fill()){ + // auto &prop=from_vertex.at(prop_name); + // if(prop==args[0]){ + // for(auto edge:from_vertex.out()){ + // if(edge.edge_type() == type){ + // auto to_vertex=edge.to(); + // if(to_vertex.fill()){ + // // Here you have all data. + // // n == from_vertex + // // m == to_vertex + // } + // } + // } + // } + // } + // } if (it_type.count() > it_vertex.count()) { // Going through vertices wiil probably be faster diff --git a/include/serialization/serialization.hpp b/include/serialization/serialization.hpp index cf77a4b04..db71cd651 100644 --- a/include/serialization/serialization.hpp +++ b/include/serialization/serialization.hpp @@ -13,14 +13,17 @@ namespace serialization template <class W> void serialize_vertex(VertexAccessor const &v, W &writer) { + // Serialize vertex id writer.start_vertex(v.id()); + // Serialize labels auto const &labels = v.labels(); writer.label_count(labels.size()); for (auto &label : labels) { writer.label(label.get().str()); } + // Serialize propertys auto const &propertys = v.properties(); writer.property_count(propertys.size()); for (auto &prop : propertys) { @@ -35,10 +38,13 @@ void serialize_vertex(VertexAccessor const &v, W &writer) template <class W> void serialize_edge(EdgeAccessor const &e, W &writer) { + // Serialize to and from vertices ids. writer.start_edge(e.from().id(), e.to().id()); + // Serialize type writer.edge_type(e.edge_type().str()); + // Serialize propertys auto const &propertys = e.properties(); writer.property_count(propertys.size()); for (auto &prop : propertys) { @@ -57,12 +63,14 @@ std::pair<Id, VertexAccessor> deserialize_vertex(DbAccessor &db, D &reader) auto v = db.vertex_insert(); auto old_id = reader.vertex_start(); + // Deserialize labels std::string s; for (auto i = reader.label_count(); i > 0; i--) { auto &label_key = db.label_find_or_create(reader.label().c_str()); v.add_label(label_key); } + // Deserialize propertys for (auto i = reader.property_count(); i > 0; i--) { auto &family = db.vertex_property_family_get(reader.property_name().c_str()); @@ -81,14 +89,17 @@ template <class D, class S> EdgeAccessor deserialize_edge(DbAccessor &db, D &reader, S &store) { auto ids = reader.edge_start(); + // Deserialize from and to ids of vertices. VertexAccessor &from = store.at(ids.first); VertexAccessor &to = store.at(ids.second); auto e = db.edge_insert(from, to); + // Deserialize type auto &edge_type_key = db.type_find_or_create(reader.edge_type().c_str()); e.edge_type(edge_type_key); + // Deserialize properties for (auto i = reader.property_count(); i > 0; i--) { auto &family = db.edge_property_family_get(reader.property_name().c_str()); diff --git a/include/snapshot/snapshot_decoder.hpp b/include/snapshot/snapshot_decoder.hpp index 8164f5dc4..b6b5b6007 100644 --- a/include/snapshot/snapshot_decoder.hpp +++ b/include/snapshot/snapshot_decoder.hpp @@ -12,6 +12,9 @@ // Decodes stored snapshot. // Caller must respect loading order to be same as stored order with // SnapshotEncoder. +// Main idea of knowing when something starts and ends is at certain points try +// to deserialize string and compare it with logically expected string seted by +// the SnapshotEncoder. class SnapshotDecoder : public GraphDecoder { public: @@ -68,6 +71,8 @@ public: T property() { if (decoder.is_list()) { + // Whe are deserializing an array. + auto size = decoder.list_header(); if (decoder.is_bool()) { ArrayStore<bool> store; @@ -100,6 +105,8 @@ public: return T::handle(std::move(store)); } } else { + // Whe are deserializing a primitive. + if (decoder.is_bool()) { return T::handle(decoder.read_bool()); diff --git a/include/snapshot/snapshot_encoder.hpp b/include/snapshot/snapshot_encoder.hpp index 92fded87c..af2dc8624 100644 --- a/include/snapshot/snapshot_encoder.hpp +++ b/include/snapshot/snapshot_encoder.hpp @@ -11,8 +11,7 @@ #include "utils/stream_wrapper.hpp" // Represents creation of a snapshot. Contains all necessary informations -// for -// write. Caller is responisble to structure his calls as following: +// for write. Caller is responisble to structure his calls as following: // * property_name_init // * label_name_init // * edge_type_name_init diff --git a/include/snapshot/snapshot_engine.hpp b/include/snapshot/snapshot_engine.hpp index 20d25e475..41a6cba23 100644 --- a/include/snapshot/snapshot_engine.hpp +++ b/include/snapshot/snapshot_engine.hpp @@ -53,6 +53,7 @@ private: // Will return different name on every call. std::string snapshot_file(std::time_t const &now, const char *type); + // Returns name of snapshot commit file. std::string snapshot_commit_file(); // Path to directory of database. Ensures that all necessary directorys diff --git a/include/storage/edge.hpp b/include/storage/edge.hpp index 4e87c4d7d..ca0cddc45 100644 --- a/include/storage/edge.hpp +++ b/include/storage/edge.hpp @@ -2,7 +2,6 @@ #include "mvcc/record.hpp" #include "storage/model/edge_model.hpp" -// #include "storage/model/properties/traversers/jsonwriter.hpp" class Edge : public mvcc::Record<Edge> { diff --git a/include/storage/edge_type/edge_type.hpp b/include/storage/edge_type/edge_type.hpp index ca2a7bf01..8838c42d8 100644 --- a/include/storage/edge_type/edge_type.hpp +++ b/include/storage/edge_type/edge_type.hpp @@ -39,6 +39,7 @@ public: CharStr char_str() { return CharStr(&id[0]); } + // Index of esges which have this type. type_index_t &index() const; private: diff --git a/include/storage/garbage/garbage.hpp b/include/storage/garbage/garbage.hpp index a83c1afcd..9ac935385 100644 --- a/include/storage/garbage/garbage.hpp +++ b/include/storage/garbage/garbage.hpp @@ -18,6 +18,7 @@ class Garbage public: Garbage(tx::Engine &e) : engine(e) {} + // Will safely dispose of data. void dispose(tx::Snapshot<Id> &&snapshot, DeleteSensitive *data); // Cleaner thread should call this method every some time. Removes data diff --git a/include/storage/indexes/index_record.hpp b/include/storage/indexes/index_record.hpp index d970d9bda..4c54c376a 100644 --- a/include/storage/indexes/index_record.hpp +++ b/include/storage/indexes/index_record.hpp @@ -49,8 +49,10 @@ public: // Will change ordering of record to descending. void set_descending(); + // true if record is nullptr. bool empty() const; + // True if this index record i valid for given Transaction. bool is_valid(tx::Transaction &t) const; // True if it can be removed. @@ -62,7 +64,7 @@ public: const K key; private: - bool descending = false; + bool descending = false; // TODO: this can be passed as template argument. typename TG::record_t *const record{nullptr}; typename TG::vlist_t *const vlist{nullptr}; }; diff --git a/include/storage/indexes/index_record_collection.hpp b/include/storage/indexes/index_record_collection.hpp deleted file mode 100644 index ff1c071b6..000000000 --- a/include/storage/indexes/index_record_collection.hpp +++ /dev/null @@ -1,40 +0,0 @@ -// #pragma once -// -// TODO: DEPRICATED -// -// #include <memory> -// -// #include "data_structures/concurrent/concurrent_set.hpp" -// #include "storage/indexes/index_record.hpp" -// -// template <class T> -// class IndexRecordCollection -// { -// public: -// using index_record_t = IndexRecord<T>; -// using index_record_collection_t = ConcurrentSet<index_record_t>; -// -// IndexRecordCollection() -// : records(std::make_unique<index_record_collection_t>()) -// { -// } -// -// void add(index_record_t &&record) -// { -// auto accessor = records->access(); -// accessor.insert(std::forward<index_record_t>(record)); -// } -// -// auto access() -// { -// return records->access(); -// } -// -// // TODO: iterator and proxy -// -// private: -// std::unique_ptr<index_record_collection_t> records; -// }; -// -// using VertexIndexRecordCollection = IndexRecordCollection<Vertex>; -// using EdgeIndexRecordCollection = IndexRecordCollection<Edge>; diff --git a/include/storage/indexes/index_update.hpp b/include/storage/indexes/index_update.hpp index dad44023f..899eff11f 100644 --- a/include/storage/indexes/index_update.hpp +++ b/include/storage/indexes/index_update.hpp @@ -4,12 +4,14 @@ #include "storage/type_group_edge.hpp" #include "storage/type_group_vertex.hpp" +// Record for updating indexes of edge struct IndexUpdateEdge { EdgeRecord *vlist; Edge *record; }; +// Record for updatin indexes of vertex struct IndexUpdateVertex { VertexRecord *vlist; diff --git a/include/storage/indexes/indexes.hpp b/include/storage/indexes/indexes.hpp index b4b32ee70..69a344f5b 100644 --- a/include/storage/indexes/indexes.hpp +++ b/include/storage/indexes/indexes.hpp @@ -23,7 +23,7 @@ public: // currently caller has to get index through object that contains // the index - + // TODO: redesign // // this was a nice try @@ -252,15 +252,20 @@ private: auto oindex = holder.get_write(t.trans); if (oindex.is_present()) { + // Inexed to which whe must insert is present. This wouldn't be the + // case if someone removed it. auto index = oindex.get(); // Iterate over all elements and add them into index. Fail if // some insert failed. bool res = iter.all([&](auto elem) { + // Try to insert record. if (!index->insert(elem.first.create_index_record( std::move(elem.second)))) { - // Index is probably unique. + + // Index wasn't successfully filled so whe should remove it + // an safely dispose of it. auto owned_maybe = holder.remove_index(index); if (owned_maybe.is_present()) { db.garbage.dispose(db.tx_engine.snapshot(), @@ -272,6 +277,8 @@ private: return true; }); if (res) { + // Index has been updated accordingly and whe can activate it + // for read. index->activate(); return true; } @@ -286,6 +293,8 @@ private: { auto owned_maybe = ih.remove_index(); if (owned_maybe.is_present()) { + // Index was successfully removed so whe are responsible for + // dispoising it safely. db.garbage.dispose(db.tx_engine.snapshot(), owned_maybe.get().release()); return true; diff --git a/include/storage/indexes/sort_order.hpp b/include/storage/indexes/sort_order.hpp deleted file mode 100644 index b04e942a0..000000000 --- a/include/storage/indexes/sort_order.hpp +++ /dev/null @@ -1,21 +0,0 @@ -#pragma once - -// TODO: DEPRICATED - -template <class T> -struct Ascending -{ - constexpr bool operator()(const T &lhs, const T &rhs) const - { - return lhs < rhs; - } -}; - -template <class T> -struct Descending -{ - constexpr bool operator()(const T &lhs, const T &rhs) const - { - return lhs > rhs; - } -}; diff --git a/include/storage/label/label.hpp b/include/storage/label/label.hpp index 3273f5978..f3bdfb63f 100644 --- a/include/storage/label/label.hpp +++ b/include/storage/label/label.hpp @@ -42,6 +42,7 @@ public: CharStr char_str() const { return CharStr(name.c_str()); } + // Index of vertices with current label. label_index_t &index() const; private: diff --git a/include/storage/model/edge_map.hpp b/include/storage/model/edge_map.hpp index d1d9b0035..521e13e8b 100644 --- a/include/storage/model/edge_map.hpp +++ b/include/storage/model/edge_map.hpp @@ -2,7 +2,6 @@ #include "data_structures/map/rh_hashmultimap.hpp" #include "storage/edge_record.hpp" -// #include "storage/vertex_record.hpp" class EdgeMap { diff --git a/include/storage/model/properties/properties.hpp b/include/storage/model/properties/properties.hpp index 54ea1034a..06de11c94 100644 --- a/include/storage/model/properties/properties.hpp +++ b/include/storage/model/properties/properties.hpp @@ -15,6 +15,9 @@ template <class TG, class T> using type_key_t = typename PropertyFamily<TG>::PropertyType::template PropertyTypeKey<T>; +// Collcetion of stored properties. +// NOTE: Currently underlying strucutre is a vector which is fine for smaller +// number of properties. template <class TG> class Properties { @@ -41,10 +44,12 @@ public: template <class T> OptionPtr<const T> at(type_key_t<T> &key) const { - auto f_key = key.family_key(); for (auto &prop : props) { - if (prop.key == f_key) { - return OptionPtr<const T>(&(prop.template as<T>())); + if (prop.key == key) { + if (prop.template is<T>()) { + return OptionPtr<const T>(&(prop.template as<T>())); + } + break; } } diff --git a/include/storage/model/properties/property_family.hpp b/include/storage/model/properties/property_family.hpp index 72e298cbf..7017c9ea7 100644 --- a/include/storage/model/properties/property_family.hpp +++ b/include/storage/model/properties/property_family.hpp @@ -104,13 +104,19 @@ public: // Ordered on POINTERS to PropertyType. // When compared with PropertyFamilyKey behaves as PropertyFamilyKey. template <class T> - class PropertyTypeKey : public TotalOrdering<PropertyTypeKey<T>> + class PropertyTypeKey + : public TotalOrdering<PropertyTypeKey<T>>, + public TotalOrdering<PropertyFamilyKey, PropertyTypeKey<T>>, + public TotalOrdering<PropertyTypeKey<T>, PropertyFamilyKey> { friend class PropertyType; PropertyTypeKey(const PropertyType &type) : type(type) {} public: - PropertyFamilyKey family_key() { return PropertyFamilyKey(type); } + PropertyFamilyKey family_key() const + { + return PropertyFamilyKey(type); + } Type const &prop_type() const { return type.type; } @@ -126,6 +132,30 @@ public: return &(lhs.type) < &(rhs.type); } + friend bool operator==(const PropertyFamilyKey &lhs, + const PropertyTypeKey &rhs) + { + return lhs == rhs.family_key(); + } + + friend bool operator<(const PropertyFamilyKey &lhs, + const PropertyTypeKey &rhs) + { + return lhs < rhs.family_key(); + } + + friend bool operator==(const PropertyTypeKey &lhs, + const PropertyFamilyKey &rhs) + { + return lhs.family_key() == rhs; + } + + friend bool operator<(const PropertyTypeKey &lhs, + const PropertyFamilyKey &rhs) + { + return lhs.family_key() < rhs; + } + private: const PropertyType &type; }; @@ -190,12 +220,15 @@ public: friend bool operator==(const PropertyFamily &lhs, const PropertyFamily &rhs); + // Place for index of TG::elements which have property from this family. IndexHolder<TG, std::nullptr_t> index; private: const std::string name_v; + // This is exclusivly for getNull method. PropertyType *null_type{nullptr}; + // TODO: Because types wont be removed this could be done with more efficent // data structure. ConcurrentMap<Type, std::unique_ptr<PropertyType>> types; diff --git a/include/storage/model/properties/property_holder.hpp b/include/storage/model/properties/property_holder.hpp index 28686f62f..1b6c0fd31 100644 --- a/include/storage/model/properties/property_holder.hpp +++ b/include/storage/model/properties/property_holder.hpp @@ -68,22 +68,22 @@ // Generates field in a union with a given type and name #define GENERATE_UNION_FIELD(type_name, union_name) type_name union_name -// Generates signatures GENERATE_define_generator(type_name, union_name ); +// Generates signatures define_generator(type_name, union_name ); // for every pair type,property #define GENERATE_FOR_ALL_PROPERTYS(define_generator) \ - GENERATE_##define_generator(Null, null_v); \ - GENERATE_##define_generator(Bool, bool_v); \ - GENERATE_##define_generator(Int32, int32_v); \ - GENERATE_##define_generator(Int64, int64_v); \ - GENERATE_##define_generator(Float, float_V); \ - GENERATE_##define_generator(Double, double_v); \ - GENERATE_##define_generator(String, string_v); \ - GENERATE_##define_generator(ArrayBool, array_bool); \ - GENERATE_##define_generator(ArrayInt32, array_int32); \ - GENERATE_##define_generator(ArrayInt64, array_int64); \ - GENERATE_##define_generator(ArrayFloat, array_float); \ - GENERATE_##define_generator(ArrayDouble, array_double); \ - GENERATE_##define_generator(ArrayString, array_string); + define_generator(Null, null_v); \ + define_generator(Bool, bool_v); \ + define_generator(Int32, int32_v); \ + define_generator(Int64, int64_v); \ + define_generator(Float, float_V); \ + define_generator(Double, double_v); \ + define_generator(String, string_v); \ + define_generator(ArrayBool, array_bool); \ + define_generator(ArrayInt32, array_int32); \ + define_generator(ArrayInt64, array_int64); \ + define_generator(ArrayFloat, array_float); \ + define_generator(ArrayDouble, array_double); \ + define_generator(ArrayString, array_string); // Holds property and has some means of determining its type. // T must have method get_type() const which returns Type. @@ -97,12 +97,13 @@ class PropertyHolder public: PropertyHolder() = delete; - GENERATE_FOR_ALL_PROPERTYS(CONSTRUCTOR_FOR_DATA); + GENERATE_FOR_ALL_PROPERTYS(GENERATE_CONSTRUCTOR_FOR_DATA); PropertyHolder(PropertyHolder const &other) : key(other.key) { switch (other.key.get_type().flags()) { - GENERATE_FOR_ALL_PROPERTYS(CASE_CLAUSE_FOR_CONSTRUCTOR_COPY); + GENERATE_FOR_ALL_PROPERTYS( + GENERATE_CASE_CLAUSE_FOR_CONSTRUCTOR_COPY); default: assert(false); } @@ -112,7 +113,8 @@ public: PropertyHolder(PropertyHolder &&other) : key(other.key) { switch (other.key.get_type().flags()) { - GENERATE_FOR_ALL_PROPERTYS(CASE_CLAUSE_FOR_CONSTRUCTOR_MOVE); + GENERATE_FOR_ALL_PROPERTYS( + GENERATE_CASE_CLAUSE_FOR_CONSTRUCTOR_MOVE); default: assert(false); } @@ -123,7 +125,8 @@ public: { assert(other.key.get_type() == key.get_type()); switch (key.get_type().flags()) { - GENERATE_FOR_ALL_PROPERTYS(CASE_CLAUSE_FOR_CONSTRUCTOR_MOVE); + GENERATE_FOR_ALL_PROPERTYS( + GENERATE_CASE_CLAUSE_FOR_CONSTRUCTOR_MOVE); default: assert(false); } @@ -133,7 +136,7 @@ public: ~PropertyHolder() { switch (key.get_type().flags()) { - GENERATE_FOR_ALL_PROPERTYS(CASE_CLAUSE_FOR_DESTRUCTOR); + GENERATE_FOR_ALL_PROPERTYS(GENERATE_CASE_CLAUSE_FOR_DESTRUCTOR); default: assert(false); } @@ -161,7 +164,7 @@ public: void accept(Handler &h) const { switch (key.get_type().flags()) { - GENERATE_FOR_ALL_PROPERTYS(CASE_CLAUSE_FOR_HANDLER); + GENERATE_FOR_ALL_PROPERTYS(GENERATE_CASE_CLAUSE_FOR_HANDLER); default: assert(false); } @@ -172,7 +175,8 @@ public: void accept_primitive(Handler &h) const { switch (key.get_type().flags()) { - GENERATE_FOR_ALL_PROPERTYS(CASE_CLAUSE_FOR_HANDLER_PRIMITIVE); + GENERATE_FOR_ALL_PROPERTYS( + GENERATE_CASE_CLAUSE_FOR_HANDLER_PRIMITIVE); default: assert(false); } @@ -181,7 +185,7 @@ public: std::ostream &print(std::ostream &stream) const { switch (key.get_type().flags()) { - GENERATE_FOR_ALL_PROPERTYS(CASE_CLAUSE_FOR_PRINT); + GENERATE_FOR_ALL_PROPERTYS(GENERATE_CASE_CLAUSE_FOR_PRINT); default: assert(false); } @@ -197,7 +201,7 @@ public: { if (key == other.key) { switch (key.get_type().flags()) { - GENERATE_FOR_ALL_PROPERTYS(CASE_CLAUSE_FOR_COMPARISON); + GENERATE_FOR_ALL_PROPERTYS(GENERATE_CASE_CLAUSE_FOR_COMPARISON); default: assert(false); } @@ -211,7 +215,7 @@ public: { if (key.get_type() == other.key.get_type()) { switch (key.get_type().flags()) { - GENERATE_FOR_ALL_PROPERTYS(CASE_CLAUSE_FOR_COMPARISON); + GENERATE_FOR_ALL_PROPERTYS(GENERATE_CASE_CLAUSE_FOR_COMPARISON); default: assert(false); } @@ -269,7 +273,7 @@ private: // Stored data. union { - GENERATE_FOR_ALL_PROPERTYS(UNION_FIELD); + GENERATE_FOR_ALL_PROPERTYS(GENERATE_UNION_FIELD); }; }; diff --git a/include/storage/model/properties/stored_property.hpp b/include/storage/model/properties/stored_property.hpp index d23b19876..e5bf39a9d 100644 --- a/include/storage/model/properties/stored_property.hpp +++ b/include/storage/model/properties/stored_property.hpp @@ -8,7 +8,8 @@ template <class TG> using property_key = typename PropertyFamily<TG>::PropertyType::PropertyFamilyKey; -// Property Class designated for creation outside the database. +// Property Class designated for creation inside the database. Meant for +// storage. template <class TG> class StoredProperty : public PropertyHolder<property_key<TG>> { @@ -16,7 +17,8 @@ class StoredProperty : public PropertyHolder<property_key<TG>> const static class PropertyFamily<TG> null_family; public: - // Needed for properties to return reference on stored property when they + // NOTE: Needed for properties to return reference on stored property when + // they // don't cointain searched property. const static class StoredProperty<TG> null; diff --git a/include/storage/vertex_accessor.hpp b/include/storage/vertex_accessor.hpp index e9d6b216e..a42f5c8b3 100644 --- a/include/storage/vertex_accessor.hpp +++ b/include/storage/vertex_accessor.hpp @@ -29,7 +29,7 @@ public: // True if vertex isn't connected to any other vertex. bool isolated() const; - // False if it's label with it already. + // False if it already has labe. bool add_label(const Label &label); // False if it doesn't have label. diff --git a/include/utils/border.hpp b/include/utils/border.hpp index 16c9555bb..18def0fe2 100644 --- a/include/utils/border.hpp +++ b/include/utils/border.hpp @@ -60,26 +60,6 @@ public: return a > b || a == b; } - // // true if no border or this > key or this >= key depends on border type. - // bool operator>(const T &other) const - // { - // return !key.is_present() || key.get() > other || - // (type == Including && key.get() == other); - // } - // - // // true if this border is inclusive and key is present and key == other. - // bool operator==(const T &other) const - // { - // return type == Including && key.is_present() && key.get() == other; - // } - // - // // true if no border or this < key or this <= key depends on border type. - // bool operator<(const T &other) const - // { - // return !key.is_present() || key.get() < other || - // (type == Including && key.get() == other); - // } - Option<T> key; BorderType type; }; diff --git a/include/utils/iterator/composable.hpp b/include/utils/iterator/composable.hpp index e19576e44..b5a23e6b8 100644 --- a/include/utils/iterator/composable.hpp +++ b/include/utils/iterator/composable.hpp @@ -98,6 +98,12 @@ public: }); } + // Calls update on values and returns resoult. + auto update() + { + return map([](auto ar) { return ar.update(); }); + } + // Filters with property under given key template <class KEY> auto has_property(KEY &key) @@ -112,13 +118,6 @@ public: return filter([&](auto &va) { return va.at(key) == prop; }); } - // Copy-s all pasing value to t before they are returned. - // auto clone_to(Option<T> &t) - // { - // return iter::make_inspect<decltype(std::move(*this))>( - // std::move(*this), [&](auto &v) { t = Option<T>(v); }); - // } - // Copy-s pasing value to t before they are returned. auto clone_to(Option<const T> &t) { diff --git a/include/utils/iterator/count.hpp b/include/utils/iterator/count.hpp index 24cd17615..60c5c65eb 100644 --- a/include/utils/iterator/count.hpp +++ b/include/utils/iterator/count.hpp @@ -5,6 +5,7 @@ // Represents number of to be returned elements from iterator. Where acutal // number is probably somwhere in [min,max]. +// NOTE: Experimental class Count : public TotalOrdering<Count> { diff --git a/include/utils/iterator/flat_map.hpp b/include/utils/iterator/flat_map.hpp index 655d4b226..ca5ab98ce 100644 --- a/include/utils/iterator/flat_map.hpp +++ b/include/utils/iterator/flat_map.hpp @@ -14,6 +14,7 @@ namespace iter // I - iterator type // J - iterator type returned from OP // OP - type of mapper function +// TODO: Split into flat operation and map operation. template <class T, class I, class J, class OP> class FlatMap : public IteratorBase<T>, public Composable<T, FlatMap<T, I, J, OP>> diff --git a/include/utils/iterator/limited_map.hpp b/include/utils/iterator/limited_map.hpp index db2fb7333..ccf64aec4 100644 --- a/include/utils/iterator/limited_map.hpp +++ b/include/utils/iterator/limited_map.hpp @@ -7,7 +7,7 @@ namespace iter { // Class which maps values returned by I iterator into value of type T with OP -// function and ends when op return empty optional. +// function and ends when op returns empty optional. // T - type of return value // I - iterator type // OP - type of mapper function. OP: V -> Option<T> diff --git a/include/utils/option.hpp b/include/utils/option.hpp index e6612b2a9..a1a1eb47a 100644 --- a/include/utils/option.hpp +++ b/include/utils/option.hpp @@ -5,8 +5,7 @@ #include <ext/aligned_buffer.h> #include <utility> -// Optional object storage - +// Optional object storage. It maybe has and maybe dosent have objet of type T. template <class T> class Option { @@ -86,6 +85,7 @@ public: return *this; } + // True if object i present. bool is_present() const { return initialized; } T &get() noexcept @@ -103,6 +103,7 @@ public: } } + // Returns ref to object if present else other. T const &get_or(T const &other) const { if (is_present()) { @@ -203,3 +204,10 @@ auto make_option_const(const T &&data) { return Option<const T>(std::move(data)); } + +// HELPER FUNCTIONS +template <class R> +bool option_fill(Option<R> &o) +{ + return o.is_present() && o.get().fill(); +} diff --git a/include/utils/option_ptr.hpp b/include/utils/option_ptr.hpp index a57502788..d50aa443f 100644 --- a/include/utils/option_ptr.hpp +++ b/include/utils/option_ptr.hpp @@ -1,5 +1,6 @@ #pragma once +// Like option just for pointers. More efficent than option. template <class T> class OptionPtr { diff --git a/include/utils/void.hpp b/include/utils/void.hpp index b151e66a1..2615ea7bf 100644 --- a/include/utils/void.hpp +++ b/include/utils/void.hpp @@ -2,6 +2,7 @@ #include "utils/total_ordering.hpp" +// Type which represents nothing. class Void : public TotalOrdering<Void> { public: diff --git a/src/barrier/barrier.cpp b/src/barrier/barrier.cpp index 2c2ab27bc..3daa60d60 100644 --- a/src/barrier/barrier.cpp +++ b/src/barrier/barrier.cpp @@ -6,15 +6,18 @@ // Implementations should follow the form: // border_return_type border_class::method_name(arguments){ // return -// CALL(method_name(trans(arguments)))/HALF_CALL(method_name(trans(arguments))); +// CALL(method_name(trans(arguments))) +// or +// HALF_CALL(method_name(trans(arguments))); // } // **************************** HELPER DEFINES *******************************// -// returns transformed pointer +// returns transformed this pointer #define THIS (trans(this)) -// Performs call x on transformed border class. +// In border class performs call x on original class. #define HALF_CALL(x) (THIS->x) -// Performs call x on transformed border class and returns transformed output. +// In border class performs call x on original class and produces transformed +// output. #define CALL(x) trans(HALF_CALL(x)) // Creates destructor for border type x which is original type y. @@ -58,24 +61,6 @@ namespace barrier { -// ************************* EdgePropertyType -// #define FOR_ALL_PROPS_delete_EdgePropertyType(x) \ -// template <> \ -// EdgePropertyType<x>::~EdgePropertyType() \ -// { \ -// HALF_CALL(~PropertyTypeKey()); \ -// } -// INSTANTIATE_FOR_PROPERTY(FOR_ALL_PROPS_delete_EdgePropertyType) - -// ************************* VertexPropertyType -// #define FOR_ALL_PROPS_delete_VertexPropertyType(x) \ -// template <> \ -// VertexPropertyType<x>::~VertexPropertyType() \ -// { \ -// HALF_CALL(~PropertyTypeKey()); \ -// } -// // INSTANTIATE_FOR_PROPERTY(FOR_ALL/_PROPS_delete_VertexPropertyType) - // ***************** Label VertexIndex<std::nullptr_t> &Label::index() const { return CALL(index()); } @@ -542,12 +527,6 @@ void RecordStream<Stream>::write(const EdgeStoredProperty &prop) HALF_CALL(write(trans(prop))); } -// template <class Stream> -// void RecordStream<Stream>::write_null() -// { -// HALF_CALL(write_null()); -// } - template <class Stream> void RecordStream<Stream>::write(const Null &v) { @@ -699,31 +678,31 @@ template class RecordStream<io::Socket>; // **************************** ERROR EXAMPLES ****************************** // // **************************** COMPILE TIME /* -error: +### error: ../libmemgraph.a(barrier.cpp.o): In function `Option<barrier::VertexAccessor const> Option<VertexAccessor const>::map<barrier::VertexAccessor const>()': /home/ktf/Workspace/memgraph/include/utils/option.hpp:111: undefined reference to `barrier::VertexAccessor::VertexAccessor<VertexAccessor const>(VertexAccessor const&&)' -description: +# description: Constructor VertexAccessor<::VertexAccessor const>(::VertexAccessor const&&) isn't written. -error: +### error: ../libmemgraph.a(barrier.cpp.o): In function `barrier::EdgeAccessor::from() const': /home/ktf/Workspace/memgraph/src/barrier/barrier.cpp:501: undefined reference to `barrier::VertexAccessor::VertexAccessor<barrier::VertexAccessor const>(barrier::VertexAccessor const&&)' -description: +# description: Move constructor VertexAccessor<VertexAccessor const>(VertexAccessor const&&) isn't defined. -error: +### error: /home/ktf/Workspace/memgraph/src/barrier/barrier.cpp:282:12: error: call to 'trans' is ambiguous return CALL(at(trans(key))); @@ -769,7 +748,7 @@ from macro 'TRANSFORM_REF' x const &trans(y const &l) { return ref_as<x const>(l); } \ ... -description: +# description: There is no valid transformation for types on which trans is called. */ diff --git a/src/database/db_accessor.cpp b/src/database/db_accessor.cpp index 4097373fc..8aff9c566 100644 --- a/src/database/db_accessor.cpp +++ b/src/database/db_accessor.cpp @@ -13,7 +13,6 @@ DbAccessor::DbAccessor(Db &db, tx::Transaction &t) } // VERTEX METHODS -// auto DbAccessor::vertex_access() Option<const VertexAccessor> DbAccessor::vertex_find(const Id &id) { @@ -36,8 +35,11 @@ EdgeAccessor DbAccessor::edge_insert(VertexAccessor &from, VertexAccessor &to) { auto edge_accessor = db_transaction.db.graph.edges.insert( db_transaction, from.vlist, to.vlist); + + // Connect edge with from,to vertices. from->data.out.add(edge_accessor.vlist); to->data.in.add(edge_accessor.vlist); + return edge_accessor; } @@ -46,8 +48,11 @@ EdgeAccessor DbAccessor::edge_insert(VertexAccessor const &from, { auto edge_accessor = db_transaction.db.graph.edges.insert( db_transaction, from.vlist, to.vlist); + + // Connect edge with updated from,to vertices. from.update()->data.out.add(edge_accessor.vlist); to.update()->data.in.add(edge_accessor.vlist); + return edge_accessor; } @@ -107,6 +112,7 @@ bool DbAccessor::commit() db_transaction.trans.commit(); return true; } else { + // Index update wasn't successfull so whe are aborting transaction. db_transaction.trans.abort(); return false; } diff --git a/src/dbms/cleaner.cpp b/src/dbms/cleaner.cpp index 7330a8dfb..697af1cd9 100644 --- a/src/dbms/cleaner.cpp +++ b/src/dbms/cleaner.cpp @@ -12,6 +12,7 @@ Cleaning::Cleaning(ConcurrentMap<std::string, Db> &dbs, size_t cleaning_cycle) : dbms(dbs), cleaning_cycle(cleaning_cycle) { + // Start the cleaning thread cleaners.push_back( std::make_unique<Thread>([&, cleaning_cycle = cleaning_cycle ]() { Logger logger = logging::log->logger("Cleaner"); @@ -22,8 +23,11 @@ Cleaning::Cleaning(ConcurrentMap<std::string, Db> &dbs, size_t cleaning_cycle) while (cleaning.load(std::memory_order_acquire)) { std::time_t now = std::time(nullptr); + // Maybe it's cleaning time. if (now >= last_clean + cleaning_cycle) { logger.info("Started cleaning cyle"); + + // Clean all databases for (auto &db : dbs.access()) { logger.info("Cleaning database \"{}\"", db.first); DbTransaction t(db.second); @@ -43,11 +47,15 @@ Cleaning::Cleaning(ConcurrentMap<std::string, Db> &dbs, size_t cleaning_cycle) db.first); logger.error("{}", e.what()); } + // NOTE: Whe should commit even if error occured. t.trans.commit(); } last_clean = now; logger.info("Finished cleaning cyle"); + } else { + + // Cleaning isn't scheduled for now so i should sleep. std::this_thread::sleep_for(std::chrono::seconds(1)); } } @@ -56,8 +64,10 @@ Cleaning::Cleaning(ConcurrentMap<std::string, Db> &dbs, size_t cleaning_cycle) Cleaning::~Cleaning() { + // Stop cleaning cleaning.store(false, std::memory_order_release); for (auto &t : cleaners) { + // Join with cleaners t.get()->join(); } } diff --git a/src/dbms/dbms.cpp b/src/dbms/dbms.cpp index a33518fc1..4d4af07a5 100644 --- a/src/dbms/dbms.cpp +++ b/src/dbms/dbms.cpp @@ -5,6 +5,7 @@ Db &Dbms::active() { Db *active = active_db.load(std::memory_order_acquire); if (UNLIKELY(active == nullptr)) { + // There is no active database. return create_default(); } else { return *active; @@ -19,6 +20,8 @@ Db &Dbms::active(const std::string &name) // create db if it doesn't exist auto it = acc.find(name); if (it == acc.end()) { + + // It doesn't exist. Snapshoter &snap = snapshoter; it = acc.emplace(name, std::forward_as_tuple(name), std::forward_as_tuple(name)) diff --git a/src/snapshot/snapshot_engine.cpp b/src/snapshot/snapshot_engine.cpp index 70d2fcd8e..bba6c7b42 100644 --- a/src/snapshot/snapshot_engine.cpp +++ b/src/snapshot/snapshot_engine.cpp @@ -21,8 +21,11 @@ bool SnapshotEngine::make_snapshot() std::lock_guard<std::mutex> lock(guard); std::time_t now = std::time(nullptr); if (make_snapshot(now, "full")) { + // Sanpsthot was created so whe should check if some older snapshots + // should be deleted. clean_snapshots(); return true; + } else { return false; } @@ -31,6 +34,8 @@ bool SnapshotEngine::make_snapshot() void SnapshotEngine::clean_snapshots() { logger.info("Started cleaning commit_file"); + // Whe first count the number of snapshots that whe know about in commit + // file. std::vector<std::string> lines; { std::ifstream commit_file(snapshot_commit_file()); @@ -43,14 +48,19 @@ void SnapshotEngine::clean_snapshots() int n = lines.size() - max_retained_snapshots; if (n > 0) { + // Whe have to much snapshots so whe should delete some. std::ofstream commit_file(snapshot_commit_file(), std::fstream::trunc); + // First whw will rewrite commit file to contain only + // max_retained_snapshots newest snapshots. for (auto i = n; i < lines.size(); i++) { commit_file << lines[i] << std::endl; } auto res = sys::flush_file_to_disk(commit_file); if (res == 0) { + // Commit file was succesfully changed so whe can now delete + // snapshots which whe evicted from commit file. commit_file.close(); logger.info("Removed {} snapshot from commit_file", n); @@ -93,12 +103,16 @@ bool SnapshotEngine::make_snapshot(std::time_t now, const char *type) auto old_trans = tx::TransactionRead(db.tx_engine); // Overenginered for incremental // snapshot. Can be removed. + + // Everything is ready for creation of snapshot. snapshot(t, snap, old_trans); auto res = sys::flush_file_to_disk(snapshot_file); if (res == 0) { + // Snapshot was succesfully written to disk. t.trans.commit(); success = true; + } else { logger.error("Error {} occured while flushing snapshot file", res); t.trans.abort(); @@ -112,6 +126,8 @@ bool SnapshotEngine::make_snapshot(std::time_t now, const char *type) } if (success) { + // Snapshot was succesfully created but for it to be reachable for + // import whe must add it to the end of commit file. std::ofstream commit_file(snapshot_commit_file(), std::fstream::app); commit_file << snapshot_file_name << std::endl; @@ -120,6 +136,8 @@ bool SnapshotEngine::make_snapshot(std::time_t now, const char *type) if (res == 0) { commit_file.close(); snapshoted_no_v.fetch_add(1); + // Snapshot was succesfully commited. + } else { logger.error("Error {} occured while flushing commit file", res); } @@ -139,6 +157,7 @@ bool SnapshotEngine::import() std::ifstream commit_file(snapshot_commit_file()); + // Whe first load all known snpashot file names from commit file. std::vector<std::string> snapshots; std::string line; while (std::getline(commit_file, line)) { @@ -166,8 +185,7 @@ bool SnapshotEngine::import() } else { logger.info("Unuccesfully tryed to import snapshot " - "\"{}\" because indexes where unuccesfully " - "with updating", + "\"{}\"", snapshots.back()); } @@ -179,6 +197,7 @@ bool SnapshotEngine::import() } snapshots.pop_back(); + // Whe will try to import older snapashot if such one exist. } } catch (const std::exception &e) { @@ -289,11 +308,13 @@ void SnapshotEngine::add_indexes(std::vector<IndexDefinition> &v) std::string SnapshotEngine::snapshot_file(std::time_t const &now, const char *type) { + // Current nano time less than second. auto now_nano = std::chrono::time_point_cast<std::chrono::nanoseconds>( std::chrono::high_resolution_clock::now()) .time_since_epoch() .count() % (1000 * 1000 * 1000); + return snapshot_db_dir() + "/" + std::to_string(now) + "_" + std::to_string(now_nano) + "_" + type; } @@ -308,9 +329,11 @@ std::string SnapshotEngine::snapshot_db_dir() if (!sys::ensure_directory_exists(snapshot_folder)) { logger.error("Error while creating directory \"{}\"", snapshot_folder); } + auto db_path = snapshot_folder + "/" + db.name(); if (!sys::ensure_directory_exists(db_path)) { logger.error("Error while creating directory \"{}\"", db_path); } + return db_path; } diff --git a/src/snapshot/snapshoter.cpp b/src/snapshot/snapshoter.cpp index 2305b8bfa..a088ff2d8 100644 --- a/src/snapshot/snapshoter.cpp +++ b/src/snapshot/snapshoter.cpp @@ -12,6 +12,7 @@ Snapshoter::Snapshoter(ConcurrentMap<std::string, Db> &dbs, size_t snapshot_cycle) : snapshot_cycle(snapshot_cycle), dbms(dbs) { + // Start snapshoter thread. thread = std::make_unique<Thread>([&]() { logger = logging::log->logger("Snapshoter"); logger.info("Started with snapshoot cycle of {} sec", @@ -46,7 +47,9 @@ void Snapshoter::run() make_snapshots(); last_snapshot = now; + } else { + // It isn't time for snapshot so i should wait. std::this_thread::sleep_for(std::chrono::seconds(1)); } } diff --git a/src/storage/edge_accessor.cpp b/src/storage/edge_accessor.cpp index f83b18b76..d4d167422 100644 --- a/src/storage/edge_accessor.cpp +++ b/src/storage/edge_accessor.cpp @@ -7,6 +7,7 @@ void EdgeAccessor::remove() const { RecordAccessor::remove(); + auto from_v = from(); bool f_from = from_v.fill(); assert(f_from); @@ -15,6 +16,7 @@ void EdgeAccessor::remove() const bool f_to = to_v.fill(); assert(f_to); + // Detach edge from vertices. from_v.update().record->data.out.remove(vlist); to_v.update().record->data.in.remove(vlist); } diff --git a/src/storage/garbage/garbage.cpp b/src/storage/garbage/garbage.cpp index b08f8330a..06f245163 100644 --- a/src/storage/garbage/garbage.cpp +++ b/src/storage/garbage/garbage.cpp @@ -10,6 +10,8 @@ void Garbage::clean() { for (auto it = gar.begin(); it != gar.end(); it++) { if (it->first.all_finished(engine) && it.remove()) { + // All transactions who could have seen data are finished and this + // thread successfull removed item from list. it->second->~DeleteSensitive(); } } diff --git a/src/storage/indexes/impl/nonunique_unordered_index.cpp b/src/storage/indexes/impl/nonunique_unordered_index.cpp index ca05249e9..b38a3d3c9 100644 --- a/src/storage/indexes/impl/nonunique_unordered_index.cpp +++ b/src/storage/indexes/impl/nonunique_unordered_index.cpp @@ -51,10 +51,14 @@ auto NonUniqueUnorderedIndex<T, K>::for_range_exact(DbAccessor &t_v, it = list.cbegin(), end = list.cend(), from = from_v, to = to_v, t = t_v ]() mutable->auto { + // NonUniqueUnorderedIndex is stupid so it must iterate through all + // index records to determine which are iniside borders. while (it != end) { const IndexRecord<T, K> &r = *it; if (from < r.key && to > r.key && r.is_valid(t.db_transaction.trans)) { + // record r is inside borders and is valid for current + // transaction. const typename T::accessor_t acc = r.access(t.db_transaction); it++; diff --git a/src/storage/indexes/impl/unique_ordered_index.cpp b/src/storage/indexes/impl/unique_ordered_index.cpp index ba5a98dd0..2ad6bba52 100644 --- a/src/storage/indexes/impl/unique_ordered_index.cpp +++ b/src/storage/indexes/impl/unique_ordered_index.cpp @@ -57,9 +57,13 @@ auto UniqueOrderedIndex<T, K>::for_range_exact(DbAccessor &t_v, // Sorted order must be checked if (this->type().order == Ascending && from_v.key.is_present()) { begin = acc.cfind_or_larger(from_v); + } else if (this->type().order == Descending && to_v.key.is_present()) { + // Order is descending so whe have to start from the end border and + // iterate to the from border. begin = acc.cfind_or_larger(to_v); end = from_v; + } else { assert(this->type().order != None); } @@ -71,9 +75,15 @@ auto UniqueOrderedIndex<T, K>::for_range_exact(DbAccessor &t_v, it = std::move(begin), b_end = std::move(end), t = t_v, hold_acc = std::move(acc) ]() mutable->auto { + // UniqueOrderedIndex is smart so he has to iterate only through + // records which are inside borders. He knows that he will start + // with items larger than from_v but he needs to check if it has + // reached end border. while (b_end >= it->key) { const IndexRecord<T, K> &r = *it; if (r.is_valid(t.db_transaction.trans)) { + // record r is inside borders and is valid for current + // transaction. const typename T::accessor_t acc = r.access(t.db_transaction); it++; diff --git a/src/storage/indexes/index_holder.cpp b/src/storage/indexes/index_holder.cpp index de3e0510e..aa8a74fd3 100644 --- a/src/storage/indexes/index_holder.cpp +++ b/src/storage/indexes/index_holder.cpp @@ -12,6 +12,7 @@ bool IndexHolder<TG, K>::set_index(std::unique_ptr<IndexBase<TG, K>> inx) if (index.compare_exchange_strong(npr<TG, K>, inx.get())) { inx.release(); return true; + } else { return false; } @@ -23,6 +24,7 @@ OptionPtr<IndexBase<TG, K>> IndexHolder<TG, K>::get_read() const auto loaded = index.load(std::memory_order_acquire); if (loaded == nullptr || !loaded->can_read()) { return OptionPtr<IndexBase<TG, K>>(); + } else { return make_option_ptr(loaded); } @@ -35,6 +37,7 @@ IndexHolder<TG, K>::get_write(const tx::Transaction &t) const auto loaded = index.load(std::memory_order_acquire); if (loaded == nullptr || !loaded->is_obliged_to_insert(t)) { return OptionPtr<IndexBase<TG, K>>(); + } else { return make_option_ptr(loaded); } @@ -46,6 +49,7 @@ IndexHolder<TG, K>::remove_index(IndexBase<TG, K> *expected) { if (index.compare_exchange_strong(expected, nullptr)) { return make_option(std::unique_ptr<IndexBase<TG, K>>(expected)); + } else { return make_option(std::unique_ptr<IndexBase<TG, K>>()); } @@ -57,6 +61,7 @@ Option<std::unique_ptr<IndexBase<TG, K>>> IndexHolder<TG, K>::remove_index() auto removed = index.exchange(nullptr); if (removed == nullptr) { return make_option<std::unique_ptr<IndexBase<TG, K>>>(); + } else { return make_option(std::unique_ptr<IndexBase<TG, K>>(removed)); } diff --git a/src/storage/indexes/indexes.cpp b/src/storage/indexes/indexes.cpp index 72d17abd0..64a0ef7f6 100644 --- a/src/storage/indexes/indexes.cpp +++ b/src/storage/indexes/indexes.cpp @@ -11,7 +11,7 @@ bool Indexes::add_index(IndexDefinition id) std::function<bool(DbTransaction &)> finish = [](auto &t) { return false; }; // Creates transaction and during it's creation adds index into it's - // place. Also created finish closure which will add necessary elements + // place. Also creates finish closure which will add necessary elements // into index. DbTransaction t(db, db.tx_engine.begin([&](auto &t) mutable { size_t code = id.loc.location_code(); diff --git a/src/storage/model/properties/properties.cpp b/src/storage/model/properties/properties.cpp index 5cda61ce6..f17e61a2b 100644 --- a/src/storage/model/properties/properties.cpp +++ b/src/storage/model/properties/properties.cpp @@ -35,8 +35,7 @@ void Properties<TG>::set(StoredProperty<TG> &&value) for (auto &prop : props) { if (prop.key == value.key) { // It is necessary to change key because the types from before and - // now - // could be different. + // now could be different. StoredProperty<TG> &sp = const_cast<StoredProperty<TG> &>(prop); sp = std::move(value); return; diff --git a/src/storage/vertex_accessor.cpp b/src/storage/vertex_accessor.cpp index 06347c24c..016bca2b4 100644 --- a/src/storage/vertex_accessor.cpp +++ b/src/storage/vertex_accessor.cpp @@ -53,16 +53,27 @@ void VertexAccessor::remove() const { RecordAccessor::remove(); + // Detach all out edges. for (auto evr : record->data.out) { auto ea = EdgeAccessor(evr, db); + + // Delete edge ea.vlist->remove(db.trans); + + // Remove edge from it's to vertex. auto to_v = ea.to(); to_v.fill(); to_v.update().record->data.in.remove(ea.vlist); } + + // Detach all in edges. for (auto evr : record->data.in) { auto ea = EdgeAccessor(evr, db); + + // Delete edge ea.vlist->remove(db.trans); + + // Remove edge from it's from vertex. auto from_v = ea.from(); from_v.fill(); from_v.update().record->data.out.remove(ea.vlist); diff --git a/tests/integration/index.cpp b/tests/integration/index.cpp index 1f9790f02..cbf9dba35 100644 --- a/tests/integration/index.cpp +++ b/tests/integration/index.cpp @@ -61,7 +61,7 @@ void add_property(Db &db, StoredProperty<TypeGroupVertex> &prop) { DbAccessor t(db); - t.vertex_access().fill().for_all([&](auto va) { va.set(prop); }); + t.vertex_access().fill().update().for_all([&](auto va) { va.set(prop); }); assert(t.commit()); } @@ -73,7 +73,7 @@ void add_vertex_property_serial_int(Db &db, PropertyFamily<TypeGroupVertex> &f) auto key = f.get(Int64::type).family_key(); size_t i = 0; - t.vertex_access().fill().for_all([&](auto va) mutable { + t.vertex_access().fill().update().for_all([&](auto va) mutable { va.set(StoredProperty<TypeGroupVertex>(Int64(i), key)); i++; }); @@ -88,7 +88,7 @@ void add_edge_property_serial_int(Db &db, PropertyFamily<TypeGroupEdge> &f) auto key = f.get(Int64::type).family_key(); size_t i = 0; - t.edge_access().fill().for_all([&](auto va) mutable { + t.edge_access().fill().update().for_all([&](auto va) mutable { va.set(StoredProperty<TypeGroupEdge>(Int64(i), key)); i++; }); @@ -186,7 +186,7 @@ int main(void) logging::init_async(); logging::log->pipe(std::make_unique<Stdout>()); - size_t cvl_n = 1000; + size_t cvl_n = 1; std::string create_vertex_label = "CREATE (n:LABEL {name: \"cleaner_test\"}) RETURN n"; @@ -223,11 +223,22 @@ int main(void) Db db("index", false); assert(db.indexes().add_index(vertex_property_nonunique_unordered)); assert(db.indexes().add_index(edge_property_nonunique_unordered)); + run(cvl_n, create_vertex_label, db); - add_edge(cvl_n, db); + auto sp = StoredProperty<TypeGroupVertex>( + Int64(0), db.graph.vertices.property_family_find_or_create("prop") + .get(Int64::type) + .family_key()); + add_property(db, sp); + assert(cvl_n == size(db, db.graph.vertices.property_family_find_or_create("prop") .index)); + + add_edge(cvl_n, db); + add_edge_property_serial_int( + db, db.graph.edges.property_family_find_or_create("prop")); + assert( cvl_n == size(db,