mirror of
https://github.com/matrix-construct/construct
synced 2024-11-29 10:12:39 +01:00
ircd::db: Various improvements to DB subsystem.
This commit is contained in:
parent
00b27909a0
commit
ca608402f5
9 changed files with 498 additions and 293 deletions
|
@ -63,7 +63,7 @@ extern struct log::log log;
|
||||||
|
|
||||||
//
|
//
|
||||||
// These are forward declarations to objects we may carry a pointer to.
|
// These are forward declarations to objects we may carry a pointer to.
|
||||||
// Users of ircd::db should not be dealing with these types.
|
// Users of ircd::db should not have to deal directly with these types.
|
||||||
//
|
//
|
||||||
namespace rocksdb
|
namespace rocksdb
|
||||||
{
|
{
|
||||||
|
|
|
@ -32,6 +32,11 @@ namespace db {
|
||||||
// for satisfaction. Cells from different columns sharing the same key are composed
|
// for satisfaction. Cells from different columns sharing the same key are composed
|
||||||
// into a `row` (see: row.h).
|
// into a `row` (see: row.h).
|
||||||
//
|
//
|
||||||
|
// When composed into a `row` or `object` remember that calls to cell::key() will
|
||||||
|
// all be the same index key -- not the name of the column the cell is representing
|
||||||
|
// in the row. You probably want cell::col() when iterating the row to build a
|
||||||
|
// JSON object's keys when iterating across a row.
|
||||||
|
//
|
||||||
// NOTE that this cell struct is type-agnostic. The database is capable of storing
|
// NOTE that this cell struct is type-agnostic. The database is capable of storing
|
||||||
// binary data in the key or the value for a cell. The string_view will work with
|
// binary data in the key or the value for a cell. The string_view will work with
|
||||||
// both a normal string and binary data, so this class is not a template and offers
|
// both a normal string and binary data, so this class is not a template and offers
|
||||||
|
@ -69,6 +74,9 @@ struct cell
|
||||||
explicit operator string_view() const; // empty on !valid()
|
explicit operator string_view() const; // empty on !valid()
|
||||||
explicit operator string_view(); // reload then empty on !valid()
|
explicit operator string_view(); // reload then empty on !valid()
|
||||||
|
|
||||||
|
// [SET] Perform operation on this cell only
|
||||||
|
void operator()(const op &, const string_view &val = {}, const sopts & = {});
|
||||||
|
|
||||||
// [SET] assign cell
|
// [SET] assign cell
|
||||||
cell &operator=(const string_view &);
|
cell &operator=(const string_view &);
|
||||||
|
|
||||||
|
@ -76,9 +84,10 @@ struct cell
|
||||||
bool compare_exchange(string_view &expected, const string_view &desired);
|
bool compare_exchange(string_view &expected, const string_view &desired);
|
||||||
string_view exchange(const string_view &desired);
|
string_view exchange(const string_view &desired);
|
||||||
|
|
||||||
|
// [GET] load cell only (returns valid)
|
||||||
bool load(gopts = {});
|
bool load(gopts = {});
|
||||||
|
|
||||||
cell(column, const string_view &index, std::unique_ptr<rocksdb::Iterator>);
|
cell(column, const string_view &index, std::unique_ptr<rocksdb::Iterator>, gopts = {});
|
||||||
cell(column, const string_view &index, gopts = {});
|
cell(column, const string_view &index, gopts = {});
|
||||||
cell(database &, const string_view &column, const string_view &index, gopts = {});
|
cell(database &, const string_view &column, const string_view &index, gopts = {});
|
||||||
cell();
|
cell();
|
||||||
|
@ -92,48 +101,38 @@ struct cell
|
||||||
};
|
};
|
||||||
|
|
||||||
//
|
//
|
||||||
// A delta is an element of a database transaction. The cell::delta
|
// A delta is an element of a database transaction. You can use this to change
|
||||||
// is specific to a key in a column. Use cell deltas to make an
|
// the values of cells. Use cell deltas to make an all-succeed-or-all-fail
|
||||||
// all-succeed-or-all-fail transaction across many cells in many columns.
|
// transaction across many cells in various columns at once.
|
||||||
//
|
//
|
||||||
struct cell::delta
|
struct cell::delta
|
||||||
:std::tuple<op, cell &, string_view>
|
:std::tuple<op, cell &, string_view>
|
||||||
{
|
{
|
||||||
delta(const enum op &op, cell &c, const string_view &val = {})
|
delta(cell &c, const string_view &val, const enum op &op = op::SET)
|
||||||
:std::tuple<enum op, cell &, string_view>{op, c, val}
|
:std::tuple<enum op, cell &, string_view>{op, c, val}
|
||||||
{}
|
{}
|
||||||
|
|
||||||
delta(cell &c, const string_view &val, const enum op &op = op::SET)
|
delta(const enum op &op, cell &c, const string_view &val = {})
|
||||||
:std::tuple<enum op, cell &, string_view>{op, c, val}
|
:std::tuple<enum op, cell &, string_view>{op, c, val}
|
||||||
{}
|
{}
|
||||||
};
|
};
|
||||||
|
|
||||||
// [SET] Perform operations in a sequence as a single transaction.
|
// [SET] Perform operations in a sequence as a single transaction. No template
|
||||||
void write(const cell::delta &, const sopts & = {});
|
// iterators supported yet, just a ptr range good for contiguous sequences like
|
||||||
|
// vectors and initializer_lists. To support any iterator I think we'll need to
|
||||||
|
// forward-expose a wrapping of rocksdb::WriteBatch.
|
||||||
|
void write(const cell::delta *const &begin, const cell::delta *const &end, const sopts & = {});
|
||||||
void write(const std::initializer_list<cell::delta> &, const sopts & = {});
|
void write(const std::initializer_list<cell::delta> &, const sopts & = {});
|
||||||
void write(const sopts &, const std::initializer_list<cell::delta> &);
|
void write(const sopts &, const std::initializer_list<cell::delta> &);
|
||||||
|
void write(const cell::delta &, const sopts & = {});
|
||||||
|
|
||||||
|
// Util
|
||||||
const std::string &name(const cell &);
|
const std::string &name(const cell &);
|
||||||
uint64_t sequence(const cell &);
|
uint64_t sequence(const cell &);
|
||||||
|
|
||||||
} // namespace db
|
} // namespace db
|
||||||
} // namespace ircd
|
} // namespace ircd
|
||||||
|
|
||||||
inline
|
|
||||||
uint64_t
|
|
||||||
ircd::db::sequence(const cell &c)
|
|
||||||
{
|
|
||||||
const database::snapshot &ss(c);
|
|
||||||
return sequence(ss);
|
|
||||||
}
|
|
||||||
|
|
||||||
inline
|
|
||||||
const std::string &
|
|
||||||
ircd::db::name(const cell &c)
|
|
||||||
{
|
|
||||||
return name(c.c);
|
|
||||||
}
|
|
||||||
|
|
||||||
inline std::ostream &
|
inline std::ostream &
|
||||||
ircd::db::operator<<(std::ostream &s, const cell &c)
|
ircd::db::operator<<(std::ostream &s, const cell &c)
|
||||||
{
|
{
|
||||||
|
|
|
@ -95,11 +95,12 @@ struct column
|
||||||
void operator()(const string_view &key, const view_closure &func, const gopts & = {});
|
void operator()(const string_view &key, const view_closure &func, const gopts & = {});
|
||||||
void operator()(const string_view &key, const gopts &, const view_closure &func);
|
void operator()(const string_view &key, const gopts &, const view_closure &func);
|
||||||
|
|
||||||
// [SET] Perform operations in a sequence as a single transaction.
|
// [SET] Perform operations in a sequence as a single transaction. No template iterators
|
||||||
void operator()(const delta &, const sopts & = {});
|
// supported yet, just a ContiguousContainer iteration (and derived convenience overloads)
|
||||||
|
void operator()(const delta *const &begin, const delta *const &end, const sopts & = {});
|
||||||
void operator()(const std::initializer_list<delta> &, const sopts & = {});
|
void operator()(const std::initializer_list<delta> &, const sopts & = {});
|
||||||
void operator()(const sopts &, const std::initializer_list<delta> &);
|
void operator()(const sopts &, const std::initializer_list<delta> &);
|
||||||
void operator()(const op &, const string_view &key, const string_view &val = {}, const sopts & = {});
|
void operator()(const delta &, const sopts & = {});
|
||||||
|
|
||||||
explicit column(std::shared_ptr<database::column> c);
|
explicit column(std::shared_ptr<database::column> c);
|
||||||
column(database::column &c);
|
column(database::column &c);
|
||||||
|
@ -110,17 +111,24 @@ struct column
|
||||||
//
|
//
|
||||||
// Delta is an element of a transaction. Use column::delta's to atomically
|
// Delta is an element of a transaction. Use column::delta's to atomically
|
||||||
// commit to multiple keys in the same column. Refer to delta.h for the `enum op`
|
// commit to multiple keys in the same column. Refer to delta.h for the `enum op`
|
||||||
// choices. Refer to cell::delta to transact with multiple cells across
|
// choices. Refer to cell::delta to transact with multiple cells across different
|
||||||
// different columns.
|
// columns. Refer to row::delta to transact with entire rows.
|
||||||
|
//
|
||||||
|
// Note, for now, unlike cell::delta and row::delta, the column::delta has
|
||||||
|
// no reference to the column in its tuple. This is why these deltas are executed
|
||||||
|
// through the member column::operator() and not an overload of db::write().
|
||||||
|
//
|
||||||
|
// It is unlikely you will need to work with column deltas directly because
|
||||||
|
// you may decohere one column from the others participating in a row.
|
||||||
//
|
//
|
||||||
struct column::delta
|
struct column::delta
|
||||||
:std::tuple<op, string_view, string_view>
|
:std::tuple<op, string_view, string_view>
|
||||||
{
|
{
|
||||||
delta(const enum op &op, const string_view &key, const string_view &val = {})
|
delta(const string_view &key, const string_view &val, const enum op &op = op::SET)
|
||||||
:std::tuple<enum op, string_view, string_view>{op, key, val}
|
:std::tuple<enum op, string_view, string_view>{op, key, val}
|
||||||
{}
|
{}
|
||||||
|
|
||||||
delta(const string_view &key, const string_view &val, const enum op &op = op::SET)
|
delta(const enum op &op, const string_view &key, const string_view &val = {})
|
||||||
:std::tuple<enum op, string_view, string_view>{op, key, val}
|
:std::tuple<enum op, string_view, string_view>{op, key, val}
|
||||||
{}
|
{}
|
||||||
};
|
};
|
||||||
|
@ -146,12 +154,12 @@ struct column::const_iterator
|
||||||
const_iterator(std::shared_ptr<database::column>, std::unique_ptr<rocksdb::Iterator> &&, gopts = {});
|
const_iterator(std::shared_ptr<database::column>, std::unique_ptr<rocksdb::Iterator> &&, gopts = {});
|
||||||
|
|
||||||
public:
|
public:
|
||||||
operator const database::column &() const { return *c; }
|
explicit operator const database::snapshot &() const;
|
||||||
operator const database::snapshot &() const { return opts.snapshot; }
|
explicit operator const database::column &() const;
|
||||||
explicit operator const gopts &() const { return opts; }
|
explicit operator const gopts &() const;
|
||||||
|
|
||||||
operator database::column &() { return *c; }
|
explicit operator database::snapshot &();
|
||||||
explicit operator database::snapshot &() { return opts.snapshot; }
|
explicit operator database::column &();
|
||||||
|
|
||||||
operator bool() const;
|
operator bool() const;
|
||||||
bool operator!() const;
|
bool operator!() const;
|
||||||
|
@ -209,27 +217,60 @@ void flush(column &, const bool &blocking = false);
|
||||||
} // namespace db
|
} // namespace db
|
||||||
} // namespace ircd
|
} // namespace ircd
|
||||||
|
|
||||||
inline
|
inline ircd::db::column::const_iterator::operator
|
||||||
ircd::db::column::operator database::column &()
|
database::column &()
|
||||||
{
|
{
|
||||||
return *c;
|
return *c;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline
|
inline ircd::db::column::const_iterator::operator
|
||||||
ircd::db::column::operator database &()
|
database::snapshot &()
|
||||||
{
|
{
|
||||||
return database::get(*c);
|
return opts.snapshot;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline
|
inline ircd::db::column::const_iterator::operator
|
||||||
ircd::db::column::operator const database::column &()
|
const gopts &()
|
||||||
|
const
|
||||||
|
{
|
||||||
|
return opts;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline ircd::db::column::const_iterator::operator
|
||||||
|
const database::column &()
|
||||||
const
|
const
|
||||||
{
|
{
|
||||||
return *c;
|
return *c;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline
|
inline ircd::db::column::const_iterator::operator
|
||||||
ircd::db::column::operator const database &()
|
const database::snapshot &()
|
||||||
|
const
|
||||||
|
{
|
||||||
|
return opts.snapshot;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline ircd::db::column::operator
|
||||||
|
database::column &()
|
||||||
|
{
|
||||||
|
return *c;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline ircd::db::column::operator
|
||||||
|
database &()
|
||||||
|
{
|
||||||
|
return database::get(*c);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline ircd::db::column::operator
|
||||||
|
const database::column &()
|
||||||
|
const
|
||||||
|
{
|
||||||
|
return *c;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline ircd::db::column::operator
|
||||||
|
const database &()
|
||||||
const
|
const
|
||||||
{
|
{
|
||||||
return database::get(*c);
|
return database::get(*c);
|
||||||
|
|
|
@ -64,6 +64,7 @@ struct database
|
||||||
std::shared_ptr<rocksdb::Cache> cache;
|
std::shared_ptr<rocksdb::Cache> cache;
|
||||||
std::map<string_view, std::shared_ptr<column>> columns;
|
std::map<string_view, std::shared_ptr<column>> columns;
|
||||||
custom_ptr<rocksdb::DB> d;
|
custom_ptr<rocksdb::DB> d;
|
||||||
|
unique_const_iterator<decltype(dbs)> dbs_it;
|
||||||
|
|
||||||
operator std::shared_ptr<database>() { return shared_from_this(); }
|
operator std::shared_ptr<database>() { return shared_from_this(); }
|
||||||
operator const rocksdb::DB &() const { return *d; }
|
operator const rocksdb::DB &() const { return *d; }
|
||||||
|
@ -72,12 +73,12 @@ struct database
|
||||||
const column &operator[](const string_view &) const;
|
const column &operator[](const string_view &) const;
|
||||||
column &operator[](const string_view &);
|
column &operator[](const string_view &);
|
||||||
|
|
||||||
database(const std::string &name,
|
database(std::string name,
|
||||||
const std::string &options,
|
std::string options,
|
||||||
description);
|
description);
|
||||||
|
|
||||||
database(const std::string &name,
|
database(std::string name,
|
||||||
const std::string &options = {});
|
std::string options = {});
|
||||||
|
|
||||||
database() = default;
|
database() = default;
|
||||||
database(database &&) = delete;
|
database(database &&) = delete;
|
||||||
|
@ -148,19 +149,15 @@ struct database::options::map
|
||||||
|
|
||||||
struct database::snapshot
|
struct database::snapshot
|
||||||
{
|
{
|
||||||
std::weak_ptr<database> d;
|
|
||||||
std::shared_ptr<const rocksdb::Snapshot> s;
|
std::shared_ptr<const rocksdb::Snapshot> s;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
operator const database &() const { return *d.lock(); }
|
|
||||||
operator const rocksdb::Snapshot *() const { return s.get(); }
|
operator const rocksdb::Snapshot *() const { return s.get(); }
|
||||||
|
|
||||||
operator database &() { return *d.lock(); }
|
explicit operator bool() const { return bool(s); }
|
||||||
|
|
||||||
operator bool() const { return bool(s); }
|
|
||||||
bool operator !() const { return !s; }
|
bool operator !() const { return !s; }
|
||||||
|
|
||||||
snapshot(database &);
|
explicit snapshot(database &);
|
||||||
snapshot() = default;
|
snapshot() = default;
|
||||||
~snapshot() noexcept;
|
~snapshot() noexcept;
|
||||||
};
|
};
|
||||||
|
|
|
@ -36,6 +36,10 @@ enum op
|
||||||
SINGLE_DELETE, // (batch.SingleDelete)
|
SINGLE_DELETE, // (batch.SingleDelete)
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Indicates an op uses both a key and value for its operation. Some only use
|
||||||
|
// a key name so an empty value argument in a delta is okay when false.
|
||||||
|
bool value_required(const op &op);
|
||||||
|
|
||||||
using merge_delta = std::pair<string_view, string_view>;
|
using merge_delta = std::pair<string_view, string_view>;
|
||||||
using merge_closure = std::function<std::string (const string_view &key, const merge_delta &)>;
|
using merge_closure = std::function<std::string (const string_view &key, const merge_delta &)>;
|
||||||
using update_closure = std::function<std::string (const string_view &key, merge_delta &)>;
|
using update_closure = std::function<std::string (const string_view &key, merge_delta &)>;
|
||||||
|
|
|
@ -23,42 +23,11 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
#define HAVE_IRCD_DB_OBJECT_H
|
#define HAVE_IRCD_DB_OBJECT_H
|
||||||
|
|
||||||
// handler register(username):
|
#include "value.h"
|
||||||
//
|
|
||||||
// (let database value registered = 0)
|
|
||||||
//
|
|
||||||
// context A | context B
|
|
||||||
// |
|
|
||||||
//0 enter |
|
|
||||||
//1 if(registered) | enter ; A yields on cache-miss/IO read
|
|
||||||
//2 | if(registered) ; B resumes hitting cached value A fetched
|
|
||||||
//3 | bnt return; ; B continues without yield
|
|
||||||
//4 | registered = time(nullptr); ; B assigns B's value to database
|
|
||||||
//5 b?t return; | leave ; A resumes [what does if() see?]
|
|
||||||
//6 registered = time(nullptr); | ; A overwrites B's value
|
|
||||||
//7 leave | ; ???
|
|
||||||
|
|
||||||
namespace ircd {
|
namespace ircd {
|
||||||
namespace db {
|
namespace db {
|
||||||
|
|
||||||
template<class T,
|
|
||||||
database *const &d>
|
|
||||||
struct value
|
|
||||||
{
|
|
||||||
};
|
|
||||||
|
|
||||||
template<database *const &d>
|
|
||||||
struct value<void, d>
|
|
||||||
:cell
|
|
||||||
{
|
|
||||||
value(const string_view &name,
|
|
||||||
const string_view &index)
|
|
||||||
:cell{*d, name, index}
|
|
||||||
{}
|
|
||||||
|
|
||||||
using cell::cell;
|
|
||||||
};
|
|
||||||
|
|
||||||
template<database *const &d>
|
template<database *const &d>
|
||||||
struct object
|
struct object
|
||||||
:row
|
:row
|
||||||
|
@ -68,14 +37,21 @@ struct object
|
||||||
string_view prefix;
|
string_view prefix;
|
||||||
string_view index;
|
string_view index;
|
||||||
|
|
||||||
object(const string_view &prefix, const string_view &index);
|
template<class T = string_view> value<d, T> get(const string_view &name);
|
||||||
|
|
||||||
|
object(const string_view &index, const string_view &prefix = {}, gopts = {});
|
||||||
|
object(const string_view &index, const std::initializer_list<json::index::member> &, gopts = {});
|
||||||
object() = default;
|
object() = default;
|
||||||
};
|
};
|
||||||
|
|
||||||
template<database *const &d>
|
} // namespace db
|
||||||
object<d>::object(const string_view &prefix,
|
} // namespace ircd
|
||||||
const string_view &index)
|
|
||||||
:row{[&prefix, &index]() -> row
|
template<ircd::db::database *const &d>
|
||||||
|
ircd::db::object<d>::object(const string_view &index,
|
||||||
|
const string_view &prefix,
|
||||||
|
gopts opts)
|
||||||
|
:row{[&prefix, &index, &opts]() -> row
|
||||||
{
|
{
|
||||||
// The prefix is the name of the object we want to find members in.
|
// The prefix is the name of the object we want to find members in.
|
||||||
// This function has to find columns starting with the prefix but not
|
// This function has to find columns starting with the prefix but not
|
||||||
|
@ -110,116 +86,27 @@ object<d>::object(const string_view &prefix,
|
||||||
// Clear empty names from the array before passing up to row{}
|
// Clear empty names from the array before passing up to row{}
|
||||||
const auto end(std::remove(names, names + std::distance(low, hi), string_view{}));
|
const auto end(std::remove(names, names + std::distance(low, hi), string_view{}));
|
||||||
const auto count(std::distance(names, end));
|
const auto count(std::distance(names, end));
|
||||||
return row
|
return
|
||||||
{
|
{
|
||||||
*d, index, vector_view<string_view>(names, count)
|
*d, index, vector_view<string_view>(names, count), opts
|
||||||
};
|
};
|
||||||
}()}
|
}()}
|
||||||
,index{index}
|
,index{index}
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
template<database *const &d>
|
template<ircd::db::database *const &d>
|
||||||
struct value<string_view ,d>
|
ircd::db::object<d>::object(const string_view &index,
|
||||||
:value<void, d>
|
const std::initializer_list<json::index::member> &members,
|
||||||
|
gopts opts)
|
||||||
|
:object{index, string_view{}, opts}
|
||||||
{
|
{
|
||||||
operator string_view() const
|
|
||||||
{
|
|
||||||
return string_view{static_cast<const cell &>(*this)};
|
|
||||||
}
|
|
||||||
|
|
||||||
operator string_view()
|
|
||||||
{
|
|
||||||
return string_view{static_cast<cell &>(*this)};
|
|
||||||
}
|
|
||||||
|
|
||||||
value &operator=(const string_view &val)
|
|
||||||
{
|
|
||||||
static_cast<cell &>(*this) = val;
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
value(const string_view &col,
|
|
||||||
const string_view &row)
|
|
||||||
:value<void, d>{col, row}
|
|
||||||
{}
|
|
||||||
|
|
||||||
friend std::ostream &operator<<(std::ostream &s, const value<string_view, d> &v)
|
|
||||||
{
|
|
||||||
s << string_view{v};
|
|
||||||
return s;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
template<class T,
|
|
||||||
database *const &d>
|
|
||||||
struct arithmetic_value
|
|
||||||
:value<void, d>
|
|
||||||
{
|
|
||||||
bool compare_exchange(T &expected, const T &desired)
|
|
||||||
{
|
|
||||||
const auto ep(reinterpret_cast<const char *>(&expected));
|
|
||||||
const auto dp(reinterpret_cast<const char *>(&desired));
|
|
||||||
|
|
||||||
string_view s{ep, expected? sizeof(T) : 0};
|
|
||||||
const auto ret(cell::compare_exchange(s, string_view{dp, sizeof(T)}));
|
|
||||||
expected = !s.empty()? *reinterpret_cast<const T *>(s.data()) : 0;
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
T exchange(const T &desired)
|
|
||||||
{
|
|
||||||
const auto dp(reinterpret_cast<const char *>(&desired));
|
|
||||||
const auto ret(cell::exchange(string_view{dp, desired? sizeof(T) : 0}));
|
|
||||||
return !ret.empty()? *reinterpret_cast<const T *>(ret.data()) : 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
operator T() const
|
|
||||||
{
|
|
||||||
const auto val(this->val());
|
|
||||||
return !val.empty()? *reinterpret_cast<const T *>(val.data()) : 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
operator T()
|
|
||||||
{
|
|
||||||
const auto val(this->val());
|
|
||||||
return !val.empty()? *reinterpret_cast<const T *>(val.data()) : 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
arithmetic_value &operator=(const T &val)
|
|
||||||
{
|
|
||||||
cell &cell(*this);
|
|
||||||
const auto ptr(reinterpret_cast<const char *>(&val));
|
|
||||||
cell = string_view{ptr, val? sizeof(T) : 0};
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
friend std::ostream &operator<<(std::ostream &s, const arithmetic_value &v)
|
|
||||||
{
|
|
||||||
s << T(v);
|
|
||||||
return s;
|
|
||||||
}
|
|
||||||
|
|
||||||
arithmetic_value(const string_view &col,
|
|
||||||
const string_view &row)
|
|
||||||
:value<void, d>{col, row}
|
|
||||||
{}
|
|
||||||
};
|
|
||||||
|
|
||||||
#define IRCD_ARITHMETIC_VALUE(_type_) \
|
|
||||||
template<database *const &d> \
|
|
||||||
struct value<_type_, d> \
|
|
||||||
:arithmetic_value<_type_, d> \
|
|
||||||
{ \
|
|
||||||
using arithmetic_value<_type_, d>::arithmetic_value; \
|
|
||||||
}
|
}
|
||||||
|
|
||||||
IRCD_ARITHMETIC_VALUE(uint64_t);
|
template<ircd::db::database *const &d>
|
||||||
IRCD_ARITHMETIC_VALUE(int64_t);
|
template<class T>
|
||||||
IRCD_ARITHMETIC_VALUE(uint32_t);
|
ircd::db::value<d, T>
|
||||||
IRCD_ARITHMETIC_VALUE(int32_t);
|
ircd::db::object<d>::get(const string_view &name)
|
||||||
IRCD_ARITHMETIC_VALUE(uint16_t);
|
{
|
||||||
IRCD_ARITHMETIC_VALUE(int16_t);
|
return row::operator[](name);
|
||||||
|
}
|
||||||
} // namespace db
|
|
||||||
} // namespace ircd
|
|
||||||
|
|
|
@ -47,7 +47,10 @@ enum class set
|
||||||
struct sopts
|
struct sopts
|
||||||
:optlist<set>
|
:optlist<set>
|
||||||
{
|
{
|
||||||
template<class... list> sopts(list&&... l): optlist<set>{std::forward<list>(l)...} {}
|
template<class... list>
|
||||||
|
sopts(list&&... l)
|
||||||
|
:optlist<set>{std::forward<list>(l)...}
|
||||||
|
{}
|
||||||
};
|
};
|
||||||
|
|
||||||
enum class get
|
enum class get
|
||||||
|
@ -66,7 +69,10 @@ struct gopts
|
||||||
{
|
{
|
||||||
database::snapshot snapshot;
|
database::snapshot snapshot;
|
||||||
|
|
||||||
template<class... list> gopts(list&&... l): optlist<get>{std::forward<list>(l)...} {}
|
template<class... list>
|
||||||
|
gopts(list&&... l)
|
||||||
|
:optlist<get>{std::forward<list>(l)...}
|
||||||
|
{}
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace db
|
} // namespace db
|
||||||
|
|
|
@ -26,9 +26,13 @@
|
||||||
namespace ircd {
|
namespace ircd {
|
||||||
namespace db {
|
namespace db {
|
||||||
|
|
||||||
// A row is a collection of cells from different columns which all share the same
|
// A `row` is a collection of cells from different columns which all share the same
|
||||||
// key. This is an interface for dealing with those cells in the aggregate.
|
// key. This is an interface for dealing with those cells in the aggregate.
|
||||||
//
|
//
|
||||||
|
// Note that in a `row` each `cell` comes from a different `column`, but `cell::key()`
|
||||||
|
// will all return the same index value across the whole `row`. To get the names
|
||||||
|
// of the columns themselves to build ex. the key name of a JSON key-value pair,
|
||||||
|
// use `cell::col()`, which will be different for each `cell` across the `row`.
|
||||||
struct row
|
struct row
|
||||||
{
|
{
|
||||||
struct delta;
|
struct delta;
|
||||||
|
@ -73,7 +77,7 @@ struct row
|
||||||
row(database &,
|
row(database &,
|
||||||
const string_view &key = {},
|
const string_view &key = {},
|
||||||
const vector_view<string_view> &columns = {},
|
const vector_view<string_view> &columns = {},
|
||||||
const gopts &opts = {});
|
gopts opts = {});
|
||||||
|
|
||||||
friend size_t trim(row &, const std::function<bool (cell &)> &);
|
friend size_t trim(row &, const std::function<bool (cell &)> &);
|
||||||
friend size_t trim(row &, const string_view &key); // remove invalid or not equal
|
friend size_t trim(row &, const string_view &key); // remove invalid or not equal
|
||||||
|
@ -138,6 +142,34 @@ struct row::iterator
|
||||||
friend bool operator!=(const iterator &, const iterator &);
|
friend bool operator!=(const iterator &, const iterator &);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
//
|
||||||
|
// A delta is an element of a database transaction. You can use this to make
|
||||||
|
// an all-succeed-or-all-fail commitment to multiple rows at once. It is also
|
||||||
|
// useful to make a commitment on a single row as a convenient way to compose
|
||||||
|
// all of a row's cells together.
|
||||||
|
//
|
||||||
|
struct row::delta
|
||||||
|
:std::tuple<op, row &>
|
||||||
|
{
|
||||||
|
delta(row &r, const enum op &op = op::SET)
|
||||||
|
:std::tuple<enum op, row &>{op, r}
|
||||||
|
{}
|
||||||
|
|
||||||
|
delta(const enum op &op, row &r)
|
||||||
|
:std::tuple<enum op, row &>{op, r}
|
||||||
|
{}
|
||||||
|
};
|
||||||
|
|
||||||
|
// [SET] Perform operations in a sequence as a single transaction. No template
|
||||||
|
// iterators supported yet, just a ptr range good for contiguous sequences.
|
||||||
|
void write(const row::delta *const &begin, const row::delta *const &end, const sopts & = {});
|
||||||
|
void write(const std::initializer_list<row::delta> &, const sopts & = {});
|
||||||
|
void write(const sopts &, const std::initializer_list<row::delta> &);
|
||||||
|
void write(const row::delta &, const sopts & = {});
|
||||||
|
|
||||||
|
// [SET] Delete row from DB (convenience to an op::DELETE delta)
|
||||||
|
void del(row &, const sopts & = {});
|
||||||
|
|
||||||
} // namespace db
|
} // namespace db
|
||||||
} // namespace ircd
|
} // namespace ircd
|
||||||
|
|
||||||
|
|
427
ircd/db.cc
427
ircd/db.cc
|
@ -55,6 +55,7 @@ string_view slice(const rocksdb::Slice &);
|
||||||
// Frequently used get options and set options are separate from the string/map system
|
// Frequently used get options and set options are separate from the string/map system
|
||||||
rocksdb::WriteOptions make_opts(const sopts &);
|
rocksdb::WriteOptions make_opts(const sopts &);
|
||||||
rocksdb::ReadOptions make_opts(const gopts &, const bool &iterator = false);
|
rocksdb::ReadOptions make_opts(const gopts &, const bool &iterator = false);
|
||||||
|
bool optstr_find_and_remove(std::string &optstr, const std::string &what);
|
||||||
|
|
||||||
enum class pos
|
enum class pos
|
||||||
{
|
{
|
||||||
|
@ -229,13 +230,27 @@ database::dbs
|
||||||
} // namespace db
|
} // namespace db
|
||||||
} // namespace ircd
|
} // namespace ircd
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// init
|
||||||
|
//
|
||||||
|
|
||||||
|
namespace ircd {
|
||||||
|
namespace db {
|
||||||
|
|
||||||
|
static void init_directory();
|
||||||
|
static void init_version();
|
||||||
|
|
||||||
|
} // namespace db
|
||||||
|
} // namespace ircd
|
||||||
|
|
||||||
static char ircd_db_version[64];
|
static char ircd_db_version[64];
|
||||||
const char *const ircd::db::version(ircd_db_version);
|
const char *const ircd::db::version(ircd_db_version);
|
||||||
|
|
||||||
// Renders a version string from the defines included here.
|
// Renders a version string from the defines included here.
|
||||||
__attribute__((constructor))
|
__attribute__((constructor))
|
||||||
static void
|
static void
|
||||||
version_init()
|
ircd::db::init_version()
|
||||||
{
|
{
|
||||||
snprintf(ircd_db_version, sizeof(ircd_db_version), "%d.%d.%d",
|
snprintf(ircd_db_version, sizeof(ircd_db_version), "%d.%d.%d",
|
||||||
ROCKSDB_MAJOR,
|
ROCKSDB_MAJOR,
|
||||||
|
@ -243,6 +258,33 @@ version_init()
|
||||||
ROCKSDB_PATCH);
|
ROCKSDB_PATCH);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
ircd::db::init_directory()
|
||||||
|
try
|
||||||
|
{
|
||||||
|
const auto dbdir(fs::get(fs::DB));
|
||||||
|
if(fs::mkdir(dbdir))
|
||||||
|
log.warning("Created new database directory at `%s'", dbdir);
|
||||||
|
else
|
||||||
|
log.info("Using database directory at `%s'", dbdir);
|
||||||
|
}
|
||||||
|
catch(const fs::error &e)
|
||||||
|
{
|
||||||
|
log.error("Cannot start database system: %s", e.what());
|
||||||
|
if(ircd::debugmode)
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
|
||||||
|
ircd::db::init::init()
|
||||||
|
{
|
||||||
|
init_directory();
|
||||||
|
}
|
||||||
|
|
||||||
|
ircd::db::init::~init()
|
||||||
|
noexcept
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
//
|
//
|
||||||
// database
|
// database
|
||||||
|
@ -291,22 +333,22 @@ ircd::db::shared_from(const database::column &column)
|
||||||
// database
|
// database
|
||||||
//
|
//
|
||||||
|
|
||||||
ircd::db::database::database(const std::string &name,
|
ircd::db::database::database(std::string name,
|
||||||
const std::string &optstr)
|
std::string optstr)
|
||||||
:database
|
:database
|
||||||
{
|
{
|
||||||
name, optstr, {}
|
std::move(name), std::move(optstr), {}
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
ircd::db::database::database(const std::string &name,
|
ircd::db::database::database(std::string name,
|
||||||
const std::string &optstr,
|
std::string optstr,
|
||||||
description description)
|
description description)
|
||||||
try
|
try
|
||||||
:name
|
:name
|
||||||
{
|
{
|
||||||
name
|
std::move(name)
|
||||||
}
|
}
|
||||||
,path
|
,path
|
||||||
{
|
{
|
||||||
|
@ -338,17 +380,31 @@ try
|
||||||
,d{[this, &description, &optstr]
|
,d{[this, &description, &optstr]
|
||||||
() -> custom_ptr<rocksdb::DB>
|
() -> custom_ptr<rocksdb::DB>
|
||||||
{
|
{
|
||||||
|
// RocksDB doesn't parse a read_only option, so we allow that to be added
|
||||||
|
// to open the database as read_only and then remove that from the string.
|
||||||
|
const bool read_only
|
||||||
|
{
|
||||||
|
optstr_find_and_remove(optstr, "read_only=true;"s)
|
||||||
|
};
|
||||||
|
|
||||||
|
// We also allow the user to specify fsck=true to run a repair operation on
|
||||||
|
// the db. This may be expensive to do by default every startup.
|
||||||
|
const bool fsck
|
||||||
|
{
|
||||||
|
optstr_find_and_remove(optstr, "fsck=true;"s)
|
||||||
|
};
|
||||||
|
|
||||||
|
// Generate RocksDB options from string
|
||||||
rocksdb::DBOptions opts
|
rocksdb::DBOptions opts
|
||||||
{
|
{
|
||||||
options(optstr)
|
options(optstr)
|
||||||
};
|
};
|
||||||
|
|
||||||
// Setup sundry
|
// Setup sundry
|
||||||
opts.error_if_exists = false;
|
|
||||||
opts.create_if_missing = true;
|
opts.create_if_missing = true;
|
||||||
opts.create_missing_column_families = true;
|
opts.create_missing_column_families = true;
|
||||||
opts.max_file_opening_threads = 0;
|
opts.max_file_opening_threads = 0;
|
||||||
opts.use_fsync = true;
|
//opts.use_fsync = true;
|
||||||
|
|
||||||
// Setup logging
|
// Setup logging
|
||||||
logs->SetInfoLogLevel(ircd::debugmode? rocksdb::DEBUG_LEVEL : rocksdb::WARN_LEVEL);
|
logs->SetInfoLogLevel(ircd::debugmode? rocksdb::DEBUG_LEVEL : rocksdb::WARN_LEVEL);
|
||||||
|
@ -418,53 +474,76 @@ try
|
||||||
{
|
{
|
||||||
return static_cast<const rocksdb::ColumnFamilyDescriptor &>(*pair.second);
|
return static_cast<const rocksdb::ColumnFamilyDescriptor &>(*pair.second);
|
||||||
});
|
});
|
||||||
/*
|
|
||||||
if(fs::is_dir(path))
|
if(fsck && fs::is_dir(path))
|
||||||
{
|
{
|
||||||
log.info("Checking database @ `%s' columns[%zu]", path, columns.size());
|
log.info("Checking database @ `%s' columns[%zu]",
|
||||||
throw_on_error(rocksdb::RepairDB(path, opts, columns));
|
path,
|
||||||
log.info("Database @ `%s' check complete", path, columns.size());
|
columns.size());
|
||||||
|
|
||||||
|
throw_on_error
|
||||||
|
{
|
||||||
|
rocksdb::RepairDB(path, opts, columns)
|
||||||
|
};
|
||||||
|
|
||||||
|
log.info("Database @ `%s' check complete",
|
||||||
|
path,
|
||||||
|
columns.size());
|
||||||
}
|
}
|
||||||
//if(has_opt(opts, opt::READ_ONLY))
|
|
||||||
// throw_on_error(rocksdb::DB::OpenForReadOnly(*this->opts, path, columns, &handles, &ptr));
|
|
||||||
//else
|
|
||||||
*/
|
|
||||||
// Announce attempt before usual point where exceptions are thrown
|
// Announce attempt before usual point where exceptions are thrown
|
||||||
log.debug("Opening database \"%s\" @ `%s' columns[%zu]",
|
log.debug("Opening database \"%s\" @ `%s' columns[%zu]",
|
||||||
this->name,
|
this->name,
|
||||||
path,
|
path,
|
||||||
columns.size());
|
columns.size());
|
||||||
|
|
||||||
throw_on_error
|
if(read_only)
|
||||||
{
|
throw_on_error
|
||||||
rocksdb::DB::Open(opts, path, columns, &handles, &ptr)
|
{
|
||||||
};
|
rocksdb::DB::OpenForReadOnly(opts, path, columns, &handles, &ptr)
|
||||||
|
};
|
||||||
|
else
|
||||||
|
throw_on_error
|
||||||
|
{
|
||||||
|
rocksdb::DB::Open(opts, path, columns, &handles, &ptr)
|
||||||
|
};
|
||||||
|
|
||||||
for(const auto &handle : handles)
|
for(const auto &handle : handles)
|
||||||
this->columns.at(handle->GetName())->handle.reset(handle);
|
this->columns.at(handle->GetName())->handle.reset(handle);
|
||||||
|
|
||||||
return { ptr, deleter };
|
return { ptr, deleter };
|
||||||
}()}
|
}()}
|
||||||
|
,dbs_it
|
||||||
|
{
|
||||||
|
dbs, dbs.emplace(string_view{this->name}, this).first
|
||||||
|
}
|
||||||
{
|
{
|
||||||
log.info("'%s': Opened database @ `%s' (handle: %p) columns[%zu] seq[%zu]",
|
log.info("'%s': Opened database @ `%s' (handle: %p) columns[%zu] seq[%zu]",
|
||||||
name,
|
this->name,
|
||||||
path,
|
path,
|
||||||
(const void *)this,
|
(const void *)this,
|
||||||
columns.size(),
|
columns.size(),
|
||||||
d->GetLatestSequenceNumber());
|
d->GetLatestSequenceNumber());
|
||||||
|
|
||||||
dbs.emplace(string_view{this->name}, this);
|
|
||||||
}
|
}
|
||||||
catch(const std::exception &e)
|
catch(const std::exception &e)
|
||||||
{
|
{
|
||||||
throw error("Failed to open db '%s': %s", name, e.what());
|
throw error("Failed to open db '%s': %s",
|
||||||
|
this->name,
|
||||||
|
e.what());
|
||||||
}
|
}
|
||||||
|
|
||||||
ircd::db::database::~database()
|
ircd::db::database::~database()
|
||||||
noexcept
|
noexcept
|
||||||
{
|
{
|
||||||
log.debug("'%s': closing database @ `%s'", name, path);
|
const auto background_errors
|
||||||
dbs.erase(name);
|
{
|
||||||
|
property<uint64_t>(*this, rocksdb::DB::Properties::kBackgroundErrors)
|
||||||
|
};
|
||||||
|
|
||||||
|
log.debug("'%s': closing database @ `%s' (background errors: %lu)",
|
||||||
|
name,
|
||||||
|
path,
|
||||||
|
background_errors);
|
||||||
}
|
}
|
||||||
|
|
||||||
ircd::db::database::column &
|
ircd::db::database::column &
|
||||||
|
@ -475,7 +554,9 @@ try
|
||||||
}
|
}
|
||||||
catch(const std::out_of_range &e)
|
catch(const std::out_of_range &e)
|
||||||
{
|
{
|
||||||
throw error("'%s': column '%s' is not available or specified in schema", this->name, name);
|
throw schema_error("'%s': column '%s' is not available or specified in schema",
|
||||||
|
this->name,
|
||||||
|
name);
|
||||||
}
|
}
|
||||||
|
|
||||||
const ircd::db::database::column &
|
const ircd::db::database::column &
|
||||||
|
@ -486,7 +567,9 @@ const try
|
||||||
}
|
}
|
||||||
catch(const std::out_of_range &e)
|
catch(const std::out_of_range &e)
|
||||||
{
|
{
|
||||||
throw error("'%s': column '%s' is not available or specified in schema", this->name, name);
|
throw schema_error("'%s': column '%s' is not available or specified in schema",
|
||||||
|
this->name,
|
||||||
|
name);
|
||||||
}
|
}
|
||||||
|
|
||||||
ircd::db::database &
|
ircd::db::database &
|
||||||
|
@ -522,8 +605,8 @@ ircd::db::database::comparator::Equal(const Slice &a,
|
||||||
const
|
const
|
||||||
{
|
{
|
||||||
assert(bool(user.equal));
|
assert(bool(user.equal));
|
||||||
const string_view sa{a.data(), a.size()};
|
const string_view sa{slice(a)};
|
||||||
const string_view sb{b.data(), b.size()};
|
const string_view sb{slice(b)};
|
||||||
return user.equal(sa, sb);
|
return user.equal(sa, sb);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -533,8 +616,8 @@ ircd::db::database::comparator::Compare(const Slice &a,
|
||||||
const
|
const
|
||||||
{
|
{
|
||||||
assert(bool(user.less));
|
assert(bool(user.less));
|
||||||
const string_view sa{a.data(), a.size()};
|
const string_view sa{slice(a)};
|
||||||
const string_view sb{b.data(), b.size()};
|
const string_view sb{slice(b)};
|
||||||
return user.less(sa, sb)? -1:
|
return user.less(sa, sb)? -1:
|
||||||
user.less(sb, sa)? 1:
|
user.less(sb, sa)? 1:
|
||||||
0;
|
0;
|
||||||
|
@ -644,6 +727,8 @@ ircd::db::database::column::column(database *const &d,
|
||||||
|
|
||||||
this->options.comparator = &this->cmp;
|
this->options.comparator = &this->cmp;
|
||||||
|
|
||||||
|
//this->options.prefix_extractor = std::shared_ptr<const rocksdb::SliceTransform>(rocksdb::NewNoopTransform());
|
||||||
|
|
||||||
//if(d->mergeop->merger)
|
//if(d->mergeop->merger)
|
||||||
// this->options.merge_operator = d->mergeop;
|
// this->options.merge_operator = d->mergeop;
|
||||||
|
|
||||||
|
@ -728,20 +813,30 @@ ircd::db::sequence(const database::snapshot &s)
|
||||||
}
|
}
|
||||||
|
|
||||||
ircd::db::database::snapshot::snapshot(database &d)
|
ircd::db::database::snapshot::snapshot(database &d)
|
||||||
:d{weak_from(d)}
|
:s
|
||||||
,s{[this, &d]() -> const rocksdb::Snapshot *
|
|
||||||
{
|
{
|
||||||
return d.d->GetSnapshot();
|
d.d->GetSnapshot(),
|
||||||
}()
|
[dp(weak_from(d))](const rocksdb::Snapshot *const s)
|
||||||
,[this](const rocksdb::Snapshot *const s)
|
{
|
||||||
{
|
if(!s)
|
||||||
if(!s)
|
return;
|
||||||
return;
|
|
||||||
|
|
||||||
const auto d(this->d.lock());
|
const auto d(dp.lock());
|
||||||
d->d->ReleaseSnapshot(s);
|
log.debug("'%s' @%p: snapshot(@%p) release seq[%lu]",
|
||||||
}}
|
db::name(*d),
|
||||||
|
d->d.get(),
|
||||||
|
s,
|
||||||
|
s->GetSequenceNumber());
|
||||||
|
|
||||||
|
d->d->ReleaseSnapshot(s);
|
||||||
|
}
|
||||||
|
}
|
||||||
{
|
{
|
||||||
|
log.debug("'%s' @%p: snapshot(@%p) seq[%lu]",
|
||||||
|
db::name(d),
|
||||||
|
d.d.get(),
|
||||||
|
s.get(),
|
||||||
|
sequence(*this));
|
||||||
}
|
}
|
||||||
|
|
||||||
ircd::db::database::snapshot::~snapshot()
|
ircd::db::database::snapshot::~snapshot()
|
||||||
|
@ -806,6 +901,10 @@ ircd::db::database::logs::Logv(const rocksdb::InfoLogLevel level,
|
||||||
lstrip(buf, ' ')
|
lstrip(buf, ' ')
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Skip the options for now
|
||||||
|
if(startswith(str, "Options"))
|
||||||
|
return;
|
||||||
|
|
||||||
log(translate(level), "'%s': (rdb) %s", d->name, str);
|
log(translate(level), "'%s': (rdb) %s", d->name, str);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1018,6 +1117,26 @@ ircd::db::database::events::OnColumnFamilyHandleDeletionStarted(rocksdb::ColumnF
|
||||||
// db/cell.h
|
// db/cell.h
|
||||||
//
|
//
|
||||||
|
|
||||||
|
uint64_t
|
||||||
|
ircd::db::sequence(const cell &c)
|
||||||
|
{
|
||||||
|
const database::snapshot &ss(c);
|
||||||
|
return sequence(database::snapshot(c));
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::string &
|
||||||
|
ircd::db::name(const cell &c)
|
||||||
|
{
|
||||||
|
return name(c.c);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
ircd::db::write(const cell::delta &delta,
|
||||||
|
const sopts &sopts)
|
||||||
|
{
|
||||||
|
write(&delta, &delta + 1, sopts);
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
ircd::db::write(const sopts &sopts,
|
ircd::db::write(const sopts &sopts,
|
||||||
const std::initializer_list<cell::delta> &deltas)
|
const std::initializer_list<cell::delta> &deltas)
|
||||||
|
@ -1029,34 +1148,37 @@ void
|
||||||
ircd::db::write(const std::initializer_list<cell::delta> &deltas,
|
ircd::db::write(const std::initializer_list<cell::delta> &deltas,
|
||||||
const sopts &sopts)
|
const sopts &sopts)
|
||||||
{
|
{
|
||||||
if(!deltas.size())
|
write(std::begin(deltas), std::end(deltas), sopts);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
ircd::db::write(const cell::delta *const &begin,
|
||||||
|
const cell::delta *const &end,
|
||||||
|
const sopts &sopts)
|
||||||
|
{
|
||||||
|
if(begin == end)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
auto &front(*std::begin(deltas));
|
// Find the database through one of the cell's columns. cell::deltas
|
||||||
|
// may come from different columns so we do nothing else with this.
|
||||||
|
auto &front(*begin);
|
||||||
column &c(std::get<cell &>(front).c);
|
column &c(std::get<cell &>(front).c);
|
||||||
database &d(c);
|
database &d(c);
|
||||||
|
|
||||||
rocksdb::WriteBatch batch;
|
rocksdb::WriteBatch batch;
|
||||||
for(const auto &delta : deltas)
|
std::for_each(begin, end, [&batch]
|
||||||
append(batch, delta);
|
(const cell::delta &delta)
|
||||||
|
|
||||||
auto opts(make_opts(sopts));
|
|
||||||
throw_on_error
|
|
||||||
{
|
{
|
||||||
d.d->Write(opts, &batch)
|
append(batch, delta);
|
||||||
};
|
});
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
ircd::db::write(const cell::delta &delta,
|
|
||||||
const sopts &sopts)
|
|
||||||
{
|
|
||||||
column &c(std::get<cell &>(delta).c);
|
|
||||||
database &d(c);
|
|
||||||
|
|
||||||
rocksdb::WriteBatch batch;
|
|
||||||
append(batch, delta);
|
|
||||||
auto opts(make_opts(sopts));
|
auto opts(make_opts(sopts));
|
||||||
|
log.debug("'%s' @%lu PUT %zu cell deltas",
|
||||||
|
name(d),
|
||||||
|
sequence(d),
|
||||||
|
std::distance(begin, end));
|
||||||
|
|
||||||
|
// Commitment
|
||||||
throw_on_error
|
throw_on_error
|
||||||
{
|
{
|
||||||
d.d->Write(opts, &batch)
|
d.d->Write(opts, &batch)
|
||||||
|
@ -1104,9 +1226,11 @@ ircd::db::cell::cell(column column,
|
||||||
|
|
||||||
ircd::db::cell::cell(column column,
|
ircd::db::cell::cell(column column,
|
||||||
const string_view &index,
|
const string_view &index,
|
||||||
std::unique_ptr<rocksdb::Iterator> it)
|
std::unique_ptr<rocksdb::Iterator> it,
|
||||||
|
gopts opts)
|
||||||
:c{std::move(column)}
|
:c{std::move(column)}
|
||||||
,index{index}
|
,index{index}
|
||||||
|
,ss{std::move(opts.snapshot)}
|
||||||
,it{std::move(it)}
|
,it{std::move(it)}
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
@ -1158,12 +1282,11 @@ ircd::db::cell::load(gopts opts)
|
||||||
while(tit && tit->Valid())
|
while(tit && tit->Valid())
|
||||||
{
|
{
|
||||||
auto batchres(tit->GetBatch());
|
auto batchres(tit->GetBatch());
|
||||||
std::cout << "seq: " << batchres.sequence;
|
//std::cout << "seq: " << batchres.sequence;
|
||||||
if(batchres.writeBatchPtr)
|
if(batchres.writeBatchPtr)
|
||||||
{
|
{
|
||||||
auto &batch(*batchres.writeBatchPtr);
|
auto &batch(*batchres.writeBatchPtr);
|
||||||
std::cout << " count " << batch.Count() << " ds: " << batch.GetDataSize()
|
//std::cout << " count " << batch.Count() << " ds: " << batch.GetDataSize() << " " << batch.Data() << std::endl;
|
||||||
<< " " << batch.Data() << std::endl;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
tit->Next();
|
tit->Next();
|
||||||
|
@ -1207,12 +1330,22 @@ ircd::db::cell::operator=(const string_view &s)
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
ircd::db::cell::operator string_view()
|
void
|
||||||
|
ircd::db::cell::operator()(const op &op,
|
||||||
|
const string_view &val,
|
||||||
|
const sopts &sopts)
|
||||||
|
{
|
||||||
|
write(cell::delta{op, *this, val}, sopts);
|
||||||
|
}
|
||||||
|
|
||||||
|
ircd::db::cell::operator
|
||||||
|
string_view()
|
||||||
{
|
{
|
||||||
return val();
|
return val();
|
||||||
}
|
}
|
||||||
|
|
||||||
ircd::db::cell::operator string_view()
|
ircd::db::cell::operator
|
||||||
|
string_view()
|
||||||
const
|
const
|
||||||
{
|
{
|
||||||
return val();
|
return val();
|
||||||
|
@ -1262,10 +1395,82 @@ const
|
||||||
// db/row.h
|
// db/row.h
|
||||||
//
|
//
|
||||||
|
|
||||||
|
void
|
||||||
|
ircd::db::del(row &row,
|
||||||
|
const sopts &sopts)
|
||||||
|
{
|
||||||
|
write(row::delta{op::DELETE, row}, sopts);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
ircd::db::write(const row::delta &delta,
|
||||||
|
const sopts &sopts)
|
||||||
|
{
|
||||||
|
write(&delta, &delta + 1, sopts);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
ircd::db::write(const sopts &sopts,
|
||||||
|
const std::initializer_list<row::delta> &deltas)
|
||||||
|
{
|
||||||
|
write(deltas, sopts);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
ircd::db::write(const std::initializer_list<row::delta> &deltas,
|
||||||
|
const sopts &sopts)
|
||||||
|
{
|
||||||
|
write(std::begin(deltas), std::end(deltas), sopts);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
ircd::db::write(const row::delta *const &begin,
|
||||||
|
const row::delta *const &end,
|
||||||
|
const sopts &sopts)
|
||||||
|
{
|
||||||
|
// Count the total number of cells for this transaction.
|
||||||
|
const auto cells
|
||||||
|
{
|
||||||
|
std::accumulate(begin, end, size_t(0), []
|
||||||
|
(auto ret, const row::delta &delta)
|
||||||
|
{
|
||||||
|
const auto &row(std::get<row &>(delta));
|
||||||
|
return ret += row.size();
|
||||||
|
})
|
||||||
|
};
|
||||||
|
|
||||||
|
//TODO: allocator?
|
||||||
|
std::vector<cell::delta> deltas;
|
||||||
|
deltas.reserve(cells);
|
||||||
|
|
||||||
|
// Compose all of the cells from all of the rows into a single txn
|
||||||
|
std::for_each(begin, end, [&deltas]
|
||||||
|
(const auto &delta)
|
||||||
|
{
|
||||||
|
auto &row(std::get<row &>(delta));
|
||||||
|
const auto &op(std::get<op>(delta));
|
||||||
|
std::for_each(std::begin(row), std::end(row), [&deltas, &op]
|
||||||
|
(auto &cell)
|
||||||
|
{
|
||||||
|
// For operations like DELETE which don't require a value in
|
||||||
|
// the delta, we can skip a potentially expensive load of the cell.
|
||||||
|
const auto value
|
||||||
|
{
|
||||||
|
value_required(op)? cell.val() : string_view{}
|
||||||
|
};
|
||||||
|
|
||||||
|
deltas.emplace_back(op, cell, value);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// Commitment
|
||||||
|
write(&deltas.front(), &deltas.front() + deltas.size(), sopts);
|
||||||
|
}
|
||||||
|
|
||||||
ircd::db::row::row(database &d,
|
ircd::db::row::row(database &d,
|
||||||
const string_view &key,
|
const string_view &key,
|
||||||
const vector_view<string_view> &colnames,
|
const vector_view<string_view> &colnames,
|
||||||
const gopts &opts)
|
gopts opts)
|
||||||
:its{[this, &d, &key, &colnames, &opts]
|
:its{[this, &d, &key, &colnames, &opts]
|
||||||
{
|
{
|
||||||
using std::end;
|
using std::end;
|
||||||
|
@ -1273,11 +1478,15 @@ ircd::db::row::row(database &d,
|
||||||
using rocksdb::Iterator;
|
using rocksdb::Iterator;
|
||||||
using rocksdb::ColumnFamilyHandle;
|
using rocksdb::ColumnFamilyHandle;
|
||||||
|
|
||||||
|
if(!opts.snapshot)
|
||||||
|
opts.snapshot = database::snapshot(d);
|
||||||
|
|
||||||
const rocksdb::ReadOptions options
|
const rocksdb::ReadOptions options
|
||||||
{
|
{
|
||||||
make_opts(opts)
|
make_opts(opts)
|
||||||
};
|
};
|
||||||
|
|
||||||
|
//TODO: allocator
|
||||||
std::vector<database::column *> colptr
|
std::vector<database::column *> colptr
|
||||||
{
|
{
|
||||||
colnames.empty()? d.columns.size() : colnames.size()
|
colnames.empty()? d.columns.size() : colnames.size()
|
||||||
|
@ -1296,6 +1505,7 @@ ircd::db::row::row(database &d,
|
||||||
return &d[name];
|
return &d[name];
|
||||||
});
|
});
|
||||||
|
|
||||||
|
//TODO: allocator
|
||||||
std::vector<ColumnFamilyHandle *> handles(colptr.size());
|
std::vector<ColumnFamilyHandle *> handles(colptr.size());
|
||||||
std::transform(begin(colptr), end(colptr), begin(handles), []
|
std::transform(begin(colptr), end(colptr), begin(handles), []
|
||||||
(database::column *const &ptr)
|
(database::column *const &ptr)
|
||||||
|
@ -1303,6 +1513,7 @@ ircd::db::row::row(database &d,
|
||||||
return ptr->handle.get();
|
return ptr->handle.get();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
//TODO: does this block?
|
||||||
std::vector<Iterator *> iterators;
|
std::vector<Iterator *> iterators;
|
||||||
throw_on_error
|
throw_on_error
|
||||||
{
|
{
|
||||||
|
@ -1313,7 +1524,7 @@ ircd::db::row::row(database &d,
|
||||||
for(size_t i(0); i < ret.size(); ++i)
|
for(size_t i(0); i < ret.size(); ++i)
|
||||||
{
|
{
|
||||||
std::unique_ptr<Iterator> it(iterators.at(i));
|
std::unique_ptr<Iterator> it(iterators.at(i));
|
||||||
ret[i] = cell { *colptr.at(i), key, std::move(it) };
|
ret[i] = cell { *colptr.at(i), key, std::move(it), opts };
|
||||||
}
|
}
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
|
@ -1724,12 +1935,10 @@ ircd::db::has(column &column,
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
ircd::db::column::operator()(const op &op,
|
ircd::db::column::operator()(const delta &delta,
|
||||||
const string_view &key,
|
|
||||||
const string_view &val,
|
|
||||||
const sopts &sopts)
|
const sopts &sopts)
|
||||||
{
|
{
|
||||||
operator()(delta{op, key, val}, sopts);
|
operator()(&delta, &delta + 1, sopts);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
@ -1743,27 +1952,29 @@ void
|
||||||
ircd::db::column::operator()(const std::initializer_list<delta> &deltas,
|
ircd::db::column::operator()(const std::initializer_list<delta> &deltas,
|
||||||
const sopts &sopts)
|
const sopts &sopts)
|
||||||
{
|
{
|
||||||
rocksdb::WriteBatch batch;
|
operator()(std::begin(deltas), std::end(deltas), sopts);
|
||||||
for(const auto &delta : deltas)
|
|
||||||
append(batch, *this, delta);
|
|
||||||
|
|
||||||
database &d(*this);
|
|
||||||
auto opts(make_opts(sopts));
|
|
||||||
throw_on_error
|
|
||||||
{
|
|
||||||
d.d->Write(opts, &batch)
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
ircd::db::column::operator()(const delta &delta,
|
ircd::db::column::operator()(const delta *const &begin,
|
||||||
|
const delta *const &end,
|
||||||
const sopts &sopts)
|
const sopts &sopts)
|
||||||
{
|
{
|
||||||
rocksdb::WriteBatch batch;
|
|
||||||
append(batch, *this, delta);
|
|
||||||
|
|
||||||
database &d(*this);
|
database &d(*this);
|
||||||
|
|
||||||
|
rocksdb::WriteBatch batch;
|
||||||
|
std::for_each(begin, end, [this, &batch]
|
||||||
|
(const delta &delta)
|
||||||
|
{
|
||||||
|
append(batch, *this, delta);
|
||||||
|
});
|
||||||
|
|
||||||
auto opts(make_opts(sopts));
|
auto opts(make_opts(sopts));
|
||||||
|
log.debug("'%s' @%lu PUT %zu column deltas",
|
||||||
|
name(d),
|
||||||
|
sequence(d),
|
||||||
|
std::distance(begin, end));
|
||||||
|
|
||||||
throw_on_error
|
throw_on_error
|
||||||
{
|
{
|
||||||
d.d->Write(opts, &batch)
|
d.d->Write(opts, &batch)
|
||||||
|
@ -1950,7 +2161,7 @@ const
|
||||||
if(!it)
|
if(!it)
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
if(!it->Valid())
|
if(!valid(*it))
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
|
@ -2057,11 +2268,10 @@ ircd::db::seek(column &column,
|
||||||
const string_view &key,
|
const string_view &key,
|
||||||
const gopts &opts)
|
const gopts &opts)
|
||||||
{
|
{
|
||||||
using rocksdb::Iterator;
|
|
||||||
|
|
||||||
database &d(column);
|
database &d(column);
|
||||||
database::column &c(column);
|
database::column &c(column);
|
||||||
std::unique_ptr<Iterator> ret;
|
|
||||||
|
std::unique_ptr<rocksdb::Iterator> ret;
|
||||||
seek(c, key, opts, ret);
|
seek(c, key, opts, ret);
|
||||||
return std::move(ret);
|
return std::move(ret);
|
||||||
}
|
}
|
||||||
|
@ -2388,6 +2598,18 @@ const
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
ircd::db::optstr_find_and_remove(std::string &optstr,
|
||||||
|
const std::string &what)
|
||||||
|
{
|
||||||
|
const auto pos(optstr.find(what));
|
||||||
|
if(pos == std::string::npos)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
optstr.erase(pos, what.size());
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
rocksdb::ReadOptions
|
rocksdb::ReadOptions
|
||||||
ircd::db::make_opts(const gopts &opts,
|
ircd::db::make_opts(const gopts &opts,
|
||||||
const bool &iterator)
|
const bool &iterator)
|
||||||
|
@ -2605,3 +2827,20 @@ ircd::db::reflect(const rocksdb::Histograms &type)
|
||||||
static const auto empty{"<histogram>?????"s};
|
static const auto empty{"<histogram>?????"s};
|
||||||
return it != end(names)? it->second : empty;
|
return it != end(names)? it->second : empty;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
ircd::db::value_required(const op &op)
|
||||||
|
{
|
||||||
|
switch(op)
|
||||||
|
{
|
||||||
|
case op::SET:
|
||||||
|
case op::MERGE:
|
||||||
|
case op::DELETE_RANGE:
|
||||||
|
return true;
|
||||||
|
|
||||||
|
case op::GET:
|
||||||
|
case op::DELETE:
|
||||||
|
case op::SINGLE_DELETE:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue