record accessor, indexes and cursors

This commit is contained in:
Dominik Tomičević 2016-01-02 12:20:51 +01:00
parent c495872b22
commit b93bab375e
20 changed files with 397 additions and 128 deletions

View File

@ -402,6 +402,11 @@ public:
return Accessor(this);
}
Accessor access() const
{
return Accessor(this);
}
private:
using guard_t = std::unique_lock<lock_t>;

View File

@ -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;
};

View File

@ -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>;

99
storage/cursor.hpp Normal file
View File

@ -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:
};

25
storage/indexes/index.hpp Normal file
View File

@ -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;
};

View File

@ -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:
};

View File

@ -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;
};

View File

@ -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>>;

View File

@ -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;
}
};

24
storage/label_store.hpp Normal file
View File

@ -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;
};

View File

@ -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;
};

View File

@ -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;
};

View File

@ -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();

View File

@ -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;
};

View File

@ -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};
};

View File

@ -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)) {}

View File

@ -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;
};

View File

@ -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;
};