From b93bab375ed71a6f61c805d2fa9e18538f5c0957 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dominik=20Tomic=CC=8Cevic=CC=81?= <dominik.tomicevic@gmail.com> Date: Sat, 2 Jan 2016 12:20:51 +0100 Subject: [PATCH] record accessor, indexes and cursors --- data_structures/skiplist/skiplist.hpp | 5 + data_structures/skiplist/skiplistset.hpp | 5 + mvcc/version_list.hpp | 82 +++++++++------ storage/cursor.hpp | 99 +++++++++++++++++++ storage/indexes/index.hpp | 25 +++++ storage/indexes/index_key.hpp | 24 +++++ storage/indexes/keys/non_unique_key.hpp | 16 +++ storage/indexes/keys/unique_key.hpp | 40 ++++++++ storage/indexes/sort_order.hpp | 19 ++++ storage/label_store.hpp | 24 +++++ {database => storage}/locking/lock_status.hpp | 0 {database => storage}/locking/record_lock.hpp | 0 storage/model/label.hpp | 17 ++-- storage/model/label_list.hpp | 7 +- storage/model/properties/properties.hpp | 7 +- storage/model/record.hpp | 24 ----- storage/record_accessor.hpp | 61 ++++++++++++ storage/vertex.hpp | 2 + storage/vertex_accessor.hpp | 11 +++ storage/vertices.hpp | 57 +---------- 20 files changed, 397 insertions(+), 128 deletions(-) create mode 100644 storage/cursor.hpp create mode 100644 storage/indexes/index.hpp create mode 100644 storage/indexes/index_key.hpp create mode 100644 storage/indexes/keys/non_unique_key.hpp create mode 100644 storage/indexes/keys/unique_key.hpp create mode 100644 storage/indexes/sort_order.hpp create mode 100644 storage/label_store.hpp rename {database => storage}/locking/lock_status.hpp (100%) rename {database => storage}/locking/record_lock.hpp (100%) delete mode 100644 storage/model/record.hpp create mode 100644 storage/record_accessor.hpp create mode 100644 storage/vertex_accessor.hpp diff --git a/data_structures/skiplist/skiplist.hpp b/data_structures/skiplist/skiplist.hpp index 2446ab00f..6dce15f12 100644 --- a/data_structures/skiplist/skiplist.hpp +++ b/data_structures/skiplist/skiplist.hpp @@ -402,6 +402,11 @@ public: return Accessor(this); } + Accessor access() const + { + return Accessor(this); + } + private: using guard_t = std::unique_lock<lock_t>; diff --git a/data_structures/skiplist/skiplistset.hpp b/data_structures/skiplist/skiplistset.hpp index abf3f298d..bf77607d5 100644 --- a/data_structures/skiplist/skiplistset.hpp +++ b/data_structures/skiplist/skiplistset.hpp @@ -224,6 +224,11 @@ public: return Accessor(std::move(data.access())); } + Accessor access() const + { + return Accessor(std::move(data.access())); + } + private: SkipList<Key, T> data; }; diff --git a/mvcc/version_list.hpp b/mvcc/version_list.hpp index 8971aa757..b6f44dd0d 100644 --- a/mvcc/version_list.hpp +++ b/mvcc/version_list.hpp @@ -5,7 +5,7 @@ #include "memory/lazy_gc.hpp" #include "mvcc/serialization_error.hpp" -#include "database/locking/record_lock.hpp" +#include "storage/locking/record_lock.hpp" namespace mvcc { @@ -22,16 +22,16 @@ public: { friend class VersionList<T>; - Accessor(tx::Transaction& transaction, VersionList<T>& record) - : transaction(transaction), record(record) + Accessor(tx::Transaction& transaction, VersionList<T>& vlist) + : transaction(transaction), vlist(vlist) { - record.add_ref(); + vlist.add_ref(); } public: ~Accessor() { - record.release_ref(); + vlist.release_ref(); } Accessor(const Accessor&) = default; @@ -42,27 +42,42 @@ public: T* insert() { - return record.insert(transaction); + return vlist.insert(transaction); } const T* find() const { - return record.find(transaction); + return vlist.find(transaction); } T* update() { - return record.update(transaction); + return vlist.update(transaction); + } + + T* update(T* record) + { + return vlist.update(record, transaction); } bool remove() { - return record.remove(transaction); + return vlist.remove(transaction); + } + + bool remove(T* record) + { + return vlist.remove(record, transaction); + } + + const Id& id() const + { + return vlist.id; } private: tx::Transaction& transaction; - VersionList<T>& record; + VersionList<T>& vlist; }; VersionList() = default; @@ -117,6 +132,9 @@ public: private: std::atomic<T*> head {nullptr}; RecordLock lock; + + Id id; + //static Recycler recycler; T* find(const tx::Transaction& t) @@ -139,9 +157,10 @@ private: return r; } - T* insert(tx::Transaction& t) + T* insert(tx::Transaction& t, const Id& id) { assert(head == nullptr); + this->id = id; // create a first version of the record // TODO replace 'new' with something better @@ -158,12 +177,20 @@ private: T* update(tx::Transaction& t) { assert(head != nullptr); - auto record = lock_and_validate(t); + auto record = find(t); // check if we found any visible records if(!record) return nullptr; + return update(record, t); + } + + T* update(T* record, tx::Transaction& t) + { + assert(record != nullptr); + lock_and_validate(record, t); + auto updated = new T(); updated->data = record->data; @@ -179,15 +206,23 @@ private: bool remove(tx::Transaction& t) { assert(head != nullptr); - auto record = lock_and_validate(t); + auto record = find(t); if(!record) return false; - return record->mark_deleted(t), true; + lock_and_validate(record, t); + return remove(record, t), true; } - T* lock_and_validate(T* record, tx::Transaction& t) + void remove(T* record, tx::Transaction& t) + { + assert(record != nullptr); + lock_and_validate(record, t); + record->mark_deleted(t); + } + + void lock_and_validate(T* record, tx::Transaction& t) { assert(record != nullptr); assert(record == find(t)); @@ -198,27 +233,12 @@ private: // if the record hasn't been deleted yet or the deleting transaction // has aborted, it's ok to modify it if(!record->tx.exp() || record->hints.load().exp.is_aborted()) - return record; + return; // if it committed, then we have a serialization conflict assert(record->hints.load().exp.is_committed()); throw SerializationError(); } - - T* lock_and_validate(tx::Transaction& t) - { - // find the visible record version to delete - auto record = find(t); - return record == nullptr ? nullptr : lock_and_validate(record, t); - } }; } - -// these are convenient typedefs to use in other contexes to make the code a -// bit clearer -class Vertex; -class Edge; - -using VertexRecord = mvcc::VersionList<Vertex>; -using EdgeRecord = mvcc::VersionList<Edge>; diff --git a/storage/cursor.hpp b/storage/cursor.hpp new file mode 100644 index 000000000..6960e9357 --- /dev/null +++ b/storage/cursor.hpp @@ -0,0 +1,99 @@ +#pragma once + +#include "transactions/transaction.hpp" +#include "mvcc/version_list.hpp" +#include "storage/model/properties/property.hpp" + +#include "storage/vertex.hpp" + +template <class T, class Accessor, class It, class Derived> +class RecordCursor : public Crtp<Derived> +{ +public: + RecordCursor(Accessor&& accessor, It item, tx::Transaction& t) + : accessor(accessor), item(item), t(t) + { + + } + + // |------------------------- data operations -------------------------| + + const Property* property(const std::string& key) const + { + return record->props.at(key); + } + + template <class V, class... Args> + void property(const std::string& key, Args&&... args) + { + record->props.template set<V>(key, std::forward<Args>(args)...); + } + + // |------------------------ record operations ------------------------| + + Derived& update() + { + record = item->update(t); + return this->derived(); + } + + Derived& remove() + { + item->remove(t); + return this->derived(); + } + + // |-------------------------- iterator impl --------------------------| + + Derived& operator++() + { + ++item; + } + + Derived& operator++(int) + { + return operator++(); + } + + friend constexpr bool operator==(const Derived& a, const Derived& b) + { + return a.record == b.record; + } + + friend constexpr bool operator!=(const Derived& a, const Derived& b) + { + return !(a == b); + } + + const T& operator*() const + { + assert(record != nullptr); + return *record; + } + + const T* operator->() const + { + assert(record != nullptr); + return record; + } + + operator const T&() const + { + return *record; + } + +protected: + Accessor accessor; + tx::Transaction& t; + It item; + + T* record; +}; + +template <class It, class Accessor> +class Vertex::Cursor + : public RecordCursor<Vertex, Accessor, It, Cursor<It, Accessor>> +{ +public: + +}; diff --git a/storage/indexes/index.hpp b/storage/indexes/index.hpp new file mode 100644 index 000000000..14189637f --- /dev/null +++ b/storage/indexes/index.hpp @@ -0,0 +1,25 @@ +#pragma once + +#include "data_structures/skiplist/skiplist.hpp" +#include "keys/unique_key.hpp" + +#include "storage/cursor.hpp" + +template <class Key, class Cursor, class Item, class SortOrder> +class Index +{ + using skiplist_t = SkipList<Key, Item*>; + using iterator_t = typename skiplist_t::Iterator; + using accessor_t = typename skiplist_t::Accessor; + using K = typename Key::key_t; + +public: + Cursor insert(const K& key, Item* item, tx::Transaction& t) + { + + } + + +private: + skiplist_t skiplist; +}; diff --git a/storage/indexes/index_key.hpp b/storage/indexes/index_key.hpp new file mode 100644 index 000000000..6bb7689ec --- /dev/null +++ b/storage/indexes/index_key.hpp @@ -0,0 +1,24 @@ +#pragma once + +#include "utils/total_ordering.hpp" + +#include "ordering.hpp" +#include "storage/model/properties/properties.hpp" +#include "storage/model/properties/property.hpp" + +template <class Ordering> +class UniqueIndexKey +{ +public: + namespace + +private: + Property& key; +}; + +template <class Orderingt> +class IndexKey +{ +public: + +}; diff --git a/storage/indexes/keys/non_unique_key.hpp b/storage/indexes/keys/non_unique_key.hpp new file mode 100644 index 000000000..dd64b0264 --- /dev/null +++ b/storage/indexes/keys/non_unique_key.hpp @@ -0,0 +1,16 @@ +#pragma once + +#include "unique_key.hpp" + +template <class K, class T, class SortOrder> +class NonUniqueKey +{ +public: + NonUniqueKey(const K& key, const T&) + { + + } + +private: + intptr_t x; +}; diff --git a/storage/indexes/keys/unique_key.hpp b/storage/indexes/keys/unique_key.hpp new file mode 100644 index 000000000..2917b195d --- /dev/null +++ b/storage/indexes/keys/unique_key.hpp @@ -0,0 +1,40 @@ +#pragma once + +#include "utils/total_ordering.hpp" + +#include "storage/indexes/sort_order.hpp" +#include "storage/model/properties/properties.hpp" +#include "storage/model/properties/property.hpp" + +template <class K, class SortOrder> +class UniqueKey : public TotalOrdering<UniqueKey<K, SortOrder>> +{ +public: + using type = UniqueKey<K, SortOrder>; + using key_t = K; + + UniqueKey(const K& key) : key(key) {} + + friend constexpr bool operator<(const type& lhs, const type& rhs) + { + return sort_order(lhs.key, rhs.key); + } + + friend constexpr bool operator==(const type& lhs, const type& rhs) + { + return lhs.key == rhs.key; + } + + operator const K&() const { return key; } + +private: + static constexpr SortOrder sort_order = SortOrder(); + const K& key; +}; + +template <class K> +using UniqueKeyAsc = UniqueKey<K, Ascending<K>>; + +template <class K> +using UniqueKeyDesc = UniqueKey<K, Descending<K>>; + diff --git a/storage/indexes/sort_order.hpp b/storage/indexes/sort_order.hpp new file mode 100644 index 000000000..c30ee02eb --- /dev/null +++ b/storage/indexes/sort_order.hpp @@ -0,0 +1,19 @@ +#pragma once + +template <class T> +class Ascending +{ + constexpr bool operator()(const T& lhs, const T& rhs) const + { + return lhs < rhs; + } +}; + +template <class T> +class Descending +{ + constexpr bool operator()(const T& lhs, const T& rhs) const + { + return lhs > rhs; + } +}; diff --git a/storage/label_store.hpp b/storage/label_store.hpp new file mode 100644 index 000000000..f40e37a8b --- /dev/null +++ b/storage/label_store.hpp @@ -0,0 +1,24 @@ +#pragma once + +#include "model/label.hpp" +#include "data_structures/skiplist/skiplistset.hpp" + +class LabelStore +{ +public: + + const Label& find_or_create(const std::string& name) + { + auto accessor = labels.access(); + return accessor.insert(Label(name)).first; + } + + bool contains(const std::string& name) const + { + auto accessor = labels.access(); + return accessor.find(Label(name)) != accessor.end(); + } + +private: + SkipListSet<Label> labels; +}; diff --git a/database/locking/lock_status.hpp b/storage/locking/lock_status.hpp similarity index 100% rename from database/locking/lock_status.hpp rename to storage/locking/lock_status.hpp diff --git a/database/locking/record_lock.hpp b/storage/locking/record_lock.hpp similarity index 100% rename from database/locking/record_lock.hpp rename to storage/locking/record_lock.hpp diff --git a/storage/model/label.hpp b/storage/model/label.hpp index 46ffdcb83..3a4f2b02e 100644 --- a/storage/model/label.hpp +++ b/storage/model/label.hpp @@ -7,29 +7,32 @@ class Label : public TotalOrdering<Label> { public: - Label(const std::string& id) : id(id) {} - Label(std::string&& id) : id(std::move(id)) {} + Label(const std::string& name) : name(name) {} + Label(std::string&& name) : name(std::move(name)) {} + + Label(const Label&) = default; + Label(Label&&) = default; friend bool operator<(const Label& lhs, const Label& rhs) { - return lhs.id < rhs.id; + return lhs.name < rhs.name; } friend bool operator==(const Label& lhs, const Label& rhs) { - return lhs.id == rhs.id; + return lhs.name == rhs.name; } friend std::ostream& operator<<(std::ostream& stream, const Label& label) { - return stream << label.id; + return stream << label.name; } operator const std::string&() const { - return id; + return name; } private: - std::string id; + std::string name; }; diff --git a/storage/model/label_list.hpp b/storage/model/label_list.hpp index f72dfe824..a9d123d8c 100644 --- a/storage/model/label_list.hpp +++ b/storage/model/label_list.hpp @@ -14,11 +14,6 @@ public: auto end() const { return labels.end(); } auto cend() const { return labels.end(); } - bool add(Label&& label) - { - return labels.insert(std::move(label)).second; - } - bool add(const Label& label) { return labels.insert(label).second; @@ -50,5 +45,5 @@ public: } private: - std::set<Label> labels; + std::set<const Label&> labels; }; diff --git a/storage/model/properties/properties.hpp b/storage/model/properties/properties.hpp index 2d8c328b7..d3255e5c6 100644 --- a/storage/model/properties/properties.hpp +++ b/storage/model/properties/properties.hpp @@ -9,12 +9,7 @@ class Properties using props_t = std::map<std::string, Property::sptr>; public: - props_t::iterator find(const std::string& key) - { - return props.find(key); - } - - Property* at(const std::string& key) + const Property* at(const std::string& key) const { auto it = props.find(key); return it == props.end() ? nullptr : it->second.get(); diff --git a/storage/model/record.hpp b/storage/model/record.hpp deleted file mode 100644 index 7c9c128ac..000000000 --- a/storage/model/record.hpp +++ /dev/null @@ -1,24 +0,0 @@ -#pragma once - -#include <ostream> -#include <mutex> -#include <set> - -#include "utils/crtp.hpp" - -#include "threading/sync/spinlock.hpp" - -#include "mvcc/mvcc.hpp" - -#include "properties/properties.hpp" - -template <class Derived> -class Record : public Crtp<Derived>, public mvcc::Mvcc<Derived> -{ -public: - // a record contains a key value map containing data - Properties properties; - - // each record can have one or more distinct labels. - // std::set<uint16_t> labels; -}; diff --git a/storage/record_accessor.hpp b/storage/record_accessor.hpp new file mode 100644 index 000000000..567fc5669 --- /dev/null +++ b/storage/record_accessor.hpp @@ -0,0 +1,61 @@ +#pragma once + +#include "transactions/transaction.hpp" +#include "mvcc/version_list.hpp" +#include "storage/model/properties/property.hpp" + +template <class T, class Store, class Derived> +class RecordAccessor +{ +protected: + using vlist_t = mvcc::VersionList<T>; + +public: + RecordAccessor() = default; + + RecordAccessor(T* record, vlist_t* vlist, Store* store) + : record(record), vlist(vlist), store(store) {} + + const Id& id() const + { + auto accessor = vlist->access(); + return accessor.id(); + } + + bool empty() const + { + return record == nullptr; + } + + Derived update(tx::Transaction& t) const + { + assert(!empty()); + + auto accessor = vlist->access(); + return Derived(accessor->update(t), vlist, store); + } + + bool remove(tx::Transaction& t) const + { + assert(!empty()); + + auto accessor = vlist->access(); + return accessor->remove(t); + } + + const Property* property(const std::string& key) const + { + return record->props.at(key); + } + + template <class V, class... Args> + void property(const std::string& key, Args&&... args) + { + record->props.template set<V>(key, std::forward<Args>(args)...); + } + +protected: + T* const record {nullptr}; + vlist_t* const vlist {nullptr}; + Store* const store {nullptr}; +}; diff --git a/storage/vertex.hpp b/storage/vertex.hpp index c1b3d5075..812b79060 100644 --- a/storage/vertex.hpp +++ b/storage/vertex.hpp @@ -7,6 +7,8 @@ class Vertex : public mvcc::Record<Vertex> { public: + class Accessor; + Vertex() = default; Vertex(const VertexModel& data) : data(data) {} Vertex(VertexModel&& data) : data(std::move(data)) {} diff --git a/storage/vertex_accessor.hpp b/storage/vertex_accessor.hpp new file mode 100644 index 000000000..b02c4baaa --- /dev/null +++ b/storage/vertex_accessor.hpp @@ -0,0 +1,11 @@ +#pragma once + +#include "record_accessor.hpp" +#include "vertex.hpp" +#include "vertices.hpp" + +class Vertex::Accessor : public RecordAccessor<Vertex, Vertices, Accessor> +{ +public: + using RecordAccessor::RecordAccessor; +}; diff --git a/storage/vertices.hpp b/storage/vertices.hpp index c4b862259..3df3bfc45 100644 --- a/storage/vertices.hpp +++ b/storage/vertices.hpp @@ -26,60 +26,9 @@ public: return vertex; } - VertexProxy insert(tx::Transaction& transaction) - { - // get next vertex id - auto next = counter.next(std::memory_order_acquire); - - // create new vertex record - VertexRecord vertex_record; - - // insert the new vertex record into the vertex store - auto vertices_accessor = vertices.access(); - auto result = vertices_accessor.insert_unique(next, std::move(vertex_record)); - - // create new vertex - auto inserted_vertex_record = result.first; - auto vertex_accessor = inserted_vertex_record->second.access(transaction); - auto vertex = vertex_accessor.insert(); - - VertexProxy vertex_proxy(next, vertex, this, &inserted_vertex_record->second); - return vertex_proxy; - } - - Vertex* update(tx::Transaction& transaction, const Id& id) - { - // find vertex record - auto vertices_accessor = vertices.access(); - auto vertex_record = vertices_accessor.find(id); - - if (vertex_record == vertices_accessor.end()) - return nullptr; - - // get vertex that is going to be updated - auto vertex_accessor = vertex_record->second.access(transaction); - auto vertex = vertex_accessor.update(); - - return vertex; - } - - bool remove(tx::Transaction& transaction, const Id& id) - { - // find vertex record - auto vertices_accessor = vertices.access(); - auto vertex_record = vertices_accessor.find(id); - - if (vertex_record == vertices_accessor.end()) - return false; - - // mark vertex record via the vertex accessor as deleted - // return boolean result true if vertex could be removed - // or false if vertex couldn't be removed - auto vertex_accessor = vertex_record->second.access(transaction); - return vertex_accessor.remove(); - } - private: - SkipList<uint64_t, VertexRecord> vertices; + Indexes indexes; + + SkipList<uint64_t, VersionList<Vertex>> vertices; AtomicCounter<uint64_t> counter; };