0
0
Fork 0
mirror of https://github.com/matrix-construct/construct synced 2024-06-24 12:58:21 +02:00

ircd:Ⓜ️ Events database interface to m::dbs; trim vm; [inconsistent].

This commit is contained in:
Jason Volk 2018-02-08 13:23:01 -08:00
parent 7bff223748
commit 4540a32948
8 changed files with 1070 additions and 1975 deletions

View file

@ -13,32 +13,66 @@
namespace ircd::m::dbs
{
struct init;
struct cursor;
// Database instance
extern std::shared_ptr<db::database> events;
// Event property columns
constexpr const auto event_columns{event::size()};
extern std::array<db::column, event_columns> event_column;
// Event metadata columns
extern db::column state_node;
extern db::column room_events;
// Lowlevel util
string_view room_events_key(const mutable_buffer &out, const id::room &, const uint64_t &depth, const id::event &);
// Get the state root for an event (with as much information as you have)
string_view state_root(const mutable_buffer &out, const id::room &, const id::event &, const uint64_t &depth);
string_view state_root(const mutable_buffer &out, const id::room &, const id::event &);
string_view state_root(const mutable_buffer &out, const id::event &);
string_view state_root(const mutable_buffer &out, const event &);
// [GET] Query suite
bool exists(const event::id &);
void append_indexes(const event &, db::txn &);
void write(const event &, db::txn &);
// [SET (txn)] Basic write suite
string_view write(db::txn &, const mutable_buffer &rootout, const string_view &rootin, const event &);
}
namespace ircd::m::dbs
namespace ircd::m::dbs::desc
{
using closure = std::function<void (const event &)>;
using closure_bool = std::function<bool (const event &)>;
// Full description
extern const database::description events;
bool _query(const query<> &, const closure_bool &);
bool _query_event_id(const query<> &, const closure_bool &);
bool _query_in_room_id(const query<> &, const closure_bool &, const id::room &);
bool _query_for_type_state_key_in_room_id(const query<> &, const closure_bool &, const id::room &, const string_view &type, const string_view &state_key);
// Direct columns
extern const database::descriptor events_auth_events;
extern const database::descriptor events_content;
extern const database::descriptor events_depth;
extern const database::descriptor events_event_id;
extern const database::descriptor events_hashes;
extern const database::descriptor events_membership;
extern const database::descriptor events_origin;
extern const database::descriptor events_origin_server_ts;
extern const database::descriptor events_prev_events;
extern const database::descriptor events_prev_state;
extern const database::descriptor events_room_id;
extern const database::descriptor events_sender;
extern const database::descriptor events_signatures;
extern const database::descriptor events_state_key;
extern const database::descriptor events_type;
int _query_where_event_id(const query<where::equal> &, const closure_bool &);
int _query_where_room_id_at_event_id(const query<where::equal> &, const closure_bool &);
int _query_where_room_id(const query<where::equal> &, const closure_bool &);
int _query_where(const query<where::equal> &where, const closure_bool &closure);
int _query_where(const query<where::logical_and> &where, const closure_bool &closure);
// Metadata columns
extern const database::descriptor events__state_node;
extern const db::prefix_transform events__room_events__pfx;
extern const database::descriptor events__room_events;
}
namespace ircd::m
struct ircd::m::dbs::init
{
void _index_special0(const event &event, db::txn &txn, const db::op &, const string_view &prev_event_id);
void _index_special1(const event &event, db::txn &txn, const db::op &, const string_view &prev_event_id);
}
init();
~init() noexcept;
};

View file

@ -30,16 +30,13 @@ namespace ircd::m
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wsubobject-linkage"
/// The _Main Event_. Most fundamental primitive of the Matrix protocol.
/// The Main Event
///
/// This json::tuple provides at least all of the legal members of the matrix
/// standard event. This is the fundamental building block of the matrix
/// system. Rooms are collections of events. Messages between servers are
/// passed as bundles of events (or directly).
///
/// Due to the ubiquitous usage, and diversity of extensions, the class
/// member interface is somewhat minimal.
///
/// It is better to have 100 functions operate on one data structure than
/// to have 10 functions operate on 10 data structures.
/// -Alan Perlis
@ -61,11 +58,9 @@ struct ircd::m::event
json::property<name::sender, json::string>,
json::property<name::signatures, json::object>,
json::property<name::state_key, json::string>,
json::property<name::type, json::string>,
json::property<name::unsigned_, string_view>
json::property<name::type, json::string>
>
{
struct init;
struct fetch;
struct sync;
struct prev;
@ -75,10 +70,6 @@ struct ircd::m::event
using closure = std::function<void (const event &)>;
using closure_bool = std::function<bool (const event &)>;
static const db::database::description description;
static std::shared_ptr<db::database> events;
static std::array<db::column, super_type::size()> column;
using super_type::tuple;
using super_type::operator=;
@ -114,12 +105,6 @@ struct ircd::m::event::prev
};
#pragma GCC diagnostic pop
struct ircd::m::event::init
{
init();
~init() noexcept;
};
inline bool
ircd::m::my(const event &event)
{

View file

@ -67,8 +67,7 @@ struct ircd::m::init
void listeners();
void modules();
event::init _event;
state::init _state;
dbs::init _dbs;
keys::init _keys;
public:

View file

@ -13,29 +13,6 @@
/// Matrix Virtual Machine
///
/// This is the processing unit for matrix events. The front-end interface to
/// m::vm consists of two parts: The query suite (observation) and the eval
/// (modification). The back-end interface consists of modules which supply
/// the features for the vm's logic. The m::vm operates primarily as a global
/// singleton and facilitates synchronization between multiple ircd::ctx's
/// using its interfaces.
///
/// The query suite tries to satisfy a query using the current state of the vm,
/// the database, and may even use the network. An idea of a basic pseudo-
/// query: was user X a member of room Y when event Z happened? Is X a member
/// of Y right now? etc.
///
/// The vm::eval is the primary input receptacle for events. The eval of an
/// event leads to its execution and will advance the global state of IRCd. That
/// advance may broadcast its effects to clients of IRCd, federation servers,
/// and alter the responses to future queries. Execution of an event is a
/// unique phenomenon: it only happens once. IRCd has thenceforth borne witness
/// to the event and will never execute it again unless it is forgotten by
/// erasing the db. Simulation options allow for evals without execution.
///
/// The back-end provides the business logic and features for the above user
/// interfaces.
///
namespace ircd::m::vm
{
IRCD_M_EXCEPTION(m::error, VM_ERROR, http::INTERNAL_SERVER_ERROR);
@ -44,12 +21,9 @@ namespace ircd::m::vm
enum fault :uint;
struct front;
struct eval;
struct opts;
using closure = std::function<void (const event &)>;
using closure_bool = std::function<bool (const event &)>;
using dbs::where;
using dbs::query;
using dbs::cursor;
extern struct log::log log;
@ -57,65 +31,41 @@ namespace ircd::m::vm
extern uint64_t current_sequence;
extern ctx::view<const event> inserted;
bool test(const query<> &, const closure_bool &);
bool test(const query<> &);
bool until(const query<> &, const closure_bool &);
bool until(const query<> &);
void for_each(const query<> &, const closure &);
void for_each(const closure &);
size_t count(const query<> &, const closure_bool &);
size_t count(const query<> &);
event::id::buf commit(json::iov &event);
event::id::buf commit(json::iov &event, const json::iov &content);
}
/// Evaluation Options
struct ircd::m::vm::opts
/// Event Evaluation Device
///
/// This object conducts the evaluation of an event or a tape of multiple
/// events. An event is evaluated in an attempt to execute it. Events which
/// fail during evaluation won't be executed; such is the case for events which
/// have already been executed, or events which are invalid or lead to invalid
/// transitions or actions of the machine etc.
///
struct ircd::m::vm::eval
{
/// Setting to false will disable the eval from exiting with a fault and
/// instead consider all faults as errors which throw exceptions. If
/// nothrow is also specified then the exception will be stored in the
/// exception_ptr and #gp will be set.
bool faults {true};
struct opts;
/// Setting to true will prevent any exception throwing from an eval and
/// instead only use the exception_ptr field. Note that eval does not make
/// a noexcept guarantee with or without this option and it may still throw
/// some rare or unexpected exception aborting the eval.
bool nothrow {false};
struct opts static const default_opts;
/// Setting to true will cause a successful eval to commit the result
/// which writes to the db and has global effects for the server.
/// False is the simulation mode to test evaluation without effects.
bool commit {false};
const struct opts *opts {&default_opts};
db::txn txn{*dbs::events};
uint64_t cs{0};
/// Setting to false will prevent the release sequence after a commit.
/// This is a really bad thing to do. testing/debug/devel use only.
bool release {true};
fault operator()(const event &);
/// Setting to true will force the eval to be aware of an m.room.create
/// for a room for anything to succeed. There will be #ef's until found.
bool genesis {false};
eval(const event &, const struct opts & = default_opts);
eval(const struct opts &);
eval() = default;
/// Setting to true will cause #ef's until there is at least one unbroken
/// link of events down to an m.room.create.
bool cosmogonic {false};
friend string_view reflect(const fault &);
};
/// Setting to true will cause #ef's until the entire reference cone is
/// known by the evaluation (if legit). i.e full history tree.
bool fullcone {false};
/// Evaluation Options
struct ircd::m::vm::eval::opts
{
/// Setting to 1 forces evaluations to only proceed from oldest to newest
/// events; the capstan will only operate using forward mode and positive
/// accumulations. Because events only reference the past the machine won't
/// always #ef, it will just stop. More events can be input. Setting to
/// -1 is anti-causal: evaluations only proceed from newest to oldest and
/// only negative accumulations are used. 0 is automatic and may use both.
int causal {0};
/// Option to skip event signature verification when false.
bool verify_signature {true};
};
/// Individual evaluation state
@ -138,64 +88,6 @@ enum ircd::m::vm::fault
STATE, ///< Required state is missing (#st)
};
/// Event Evaluation Device
///
/// This object conducts the evaluation of an event or a tape of multiple
/// events. An event is evaluated in an attempt to execute it. Events which
/// fail during evaluation won't be executed; such is the case for events which
/// have already been executed, or events which are invalid or lead to invalid
/// transitions or actions of the machine etc.
///
/// Basic usage of the eval is simply calling the ctor like a function with an
/// event; with the default options this evaluates and executes the event
/// throwing all errors. Advanced usage of eval takes the form of an instance
/// constructed with options; the user then drives the eval in a loop,
/// responding to its needs for more data or other issues, and then continuing
/// the loop until satisfied.
///
/// The back-end interface consists of a set of modules which register with the
/// vm::eval class and provide their piece of eval functionality to instances.
///
struct ircd::m::vm::eval
{
static const struct opts default_opts;
const struct opts *opts;
db::txn txn{*event::events};
std::set<event::id> ef;
uint64_t cs {0};
fault operator()();
fault operator()(const event &);
fault operator()(const vector_view<const event> &);
fault operator()(const json::vector &);
fault operator()(const json::array &);
template<class events> eval(const struct opts &, events&&);
template<class events> eval(const events &);
eval(const struct opts &);
eval();
friend string_view reflect(const fault &);
};
/// Convenience construction passes events through to operator().
template<class events>
ircd::m::vm::eval::eval(const events &event)
:eval(default_opts)
{
operator()(event);
}
/// Convenience construction passes events through to operator().
template<class events>
ircd::m::vm::eval::eval(const struct opts &opts,
events&& event)
:eval(opts)
{
operator()(std::forward<events>(event));
}
/// The "event front" for a graph. This holds all of the childless events
/// for a room. Each childless event may exist at a different depth, but
/// we track the highest depth to increment it for issuing the next event.

File diff suppressed because it is too large Load diff

View file

@ -10,48 +10,6 @@
#include <ircd/m/m.h>
decltype(ircd::m::event::column)
ircd::m::event::column
{};
decltype(ircd::m::event::events)
ircd::m::event::events
{};
//
// init
//
ircd::m::event::init::init()
{
// Open the events database
event::events = std::make_shared<database>("events"s, ""s, event::description);
// Cache the columns for the event tuple in order for constant time lookup
std::array<string_view, event::size()> keys; //TODO: why did this happen?
_key_transform(event{}, begin(keys), end(keys)); //TODO: how did this happen?
for(size_t i(0); i < keys.size(); ++i)
event::column[i] = db::column
{
*event::events, keys[i]
};
}
ircd::m::event::init::~init()
noexcept
{
// Columns have to be closed before DB closes
for(auto &column : event::column)
column = {};
// Close DB (if no other ref)
event::events = {};
}
//
// misc
//
ircd::m::id::event
ircd::m::event_id(const event &event,
id::event::buf &buf)
@ -357,15 +315,15 @@ ircd::m::pretty_oneline(const event &event)
ircd::m::event::event(const id &id,
const mutable_buffer &buf)
{
assert(events);
assert(bool(dbs::events));
db::gopts opts;
opts.snapshot = database::snapshot{*events};
for(size_t i(0); i < column.size(); ++i)
opts.snapshot = database::snapshot{*dbs::events};
for(size_t i(0); i < dbs::event_column.size(); ++i)
{
const db::cell cell
{
column[i], id, opts
dbs::event_column[i], id, opts
};
db::assign(*this, cell, id);
@ -378,828 +336,3 @@ ircd::m::event::event(const id &id,
new (this) m::event(obj);
}
ircd::m::event::event(fetch &tab)
{
io::acquire(tab);
}
//
// Database descriptors
//
const ircd::database::descriptor
events_event_id_descriptor
{
// name
"event_id",
// explanation
R"(### protocol note:
10.1
The id of event.
10.4
MUST NOT exceed 255 bytes.
### developer note:
key is event_id. This is redundant data but we have to have it for now.
)",
// typing (key, value)
{
typeid(ircd::string_view), typeid(ircd::string_view)
}
};
const ircd::database::descriptor
events_type_descriptor
{
// name
"type",
// explanation
R"(### protocol note:
10.1
The type of event. This SHOULD be namespaced similar to Java package naming conventions
e.g. 'com.example.subdomain.event.type'.
10.4
MUST NOT exceed 255 bytes.
### developer note:
key is event_id
)",
// typing (key, value)
{
typeid(ircd::string_view), typeid(ircd::string_view)
}
};
const ircd::database::descriptor
events_content_descriptor
{
// name
"content",
// explanation
R"(### protocol note:
10.1
The fields in this object will vary depending on the type of event. When interacting
with the REST API, this is the HTTP body.
### developer note:
Since events must not exceed 65 KB the maximum size for the content is the remaining
space after all the other fields for the event are rendered.
key is event_id
)",
// typing (key, value)
{
typeid(ircd::string_view), typeid(ircd::string_view)
}
};
const ircd::database::descriptor
events_room_id_descriptor
{
// name
"room_id",
// explanation
R"(### protocol note:
10.2 (apropos room events)
Required. The ID of the room associated with this event.
10.4
MUST NOT exceed 255 bytes.
### developer note:
key is event_id
)",
// typing (key, value)
{
typeid(ircd::string_view), typeid(ircd::string_view)
}
};
const ircd::database::descriptor
events_sender_descriptor
{
// name
"sender",
// explanation
R"(### protocol note:
10.2 (apropos room events)
Required. Contains the fully-qualified ID of the user who sent this event.
10.4
MUST NOT exceed 255 bytes.
### developer note:
key is event_id
)",
// typing (key, value)
{
typeid(ircd::string_view), typeid(ircd::string_view)
}
};
const ircd::database::descriptor
events_state_key_descriptor
{
// name
"state_key",
// explanation
R"(### protocol note:
10.3 (apropos room state events)
A unique key which defines the overwriting semantics for this piece of room state.
This value is often a zero-length string. The presence of this key makes this event a
State Event. The key MUST NOT start with '_'.
10.4
MUST NOT exceed 255 bytes.
### developer note:
key is event_id
)",
// typing (key, value)
{
typeid(ircd::string_view), typeid(ircd::string_view)
}
};
const ircd::database::descriptor
events_origin_descriptor
{
// name
"origin",
// explanation
R"(### protocol note:
FEDERATION 4.1
DNS name of homeserver that created this PDU
### developer note:
key is event_id
)",
// typing (key, value)
{
typeid(ircd::string_view), typeid(ircd::string_view)
}
};
const ircd::database::descriptor
events_origin_server_ts_descriptor
{
// name
"origin_server_ts",
// explanation
R"(### protocol note:
FEDERATION 4.1
Timestamp in milliseconds on origin homeserver when this PDU was created.
### developer note:
key is event_id
value is a machine integer (binary)
TODO: consider unsigned rather than time_t because of millisecond precision
)",
// typing (key, value)
{
typeid(ircd::string_view), typeid(time_t)
}
};
const ircd::database::descriptor
events_unsigned_descriptor
{
// name
"unsigned",
// explanation
R"(### protocol note:
### developer note:
key is event_id
)",
// typing (key, value)
{
typeid(ircd::string_view), typeid(ircd::string_view)
}
};
const ircd::database::descriptor
events_signatures_descriptor
{
// name
"signatures",
// explanation
R"(### protocol note:
### developer note:
key is event_id
)",
// typing (key, value)
{
typeid(ircd::string_view), typeid(ircd::string_view)
}
};
const ircd::database::descriptor
events_auth_events_descriptor
{
// name
"auth_events",
// explanation
R"(### protocol note:
### developer note:
key is event_id.
)",
// typing (key, value)
{
typeid(ircd::string_view), typeid(ircd::string_view)
}
};
const ircd::database::descriptor
events_depth_descriptor
{
// name
"depth",
// explanation
R"(### protocol note:
### developer note:
key is event_id value is long integer
)",
// typing (key, value)
{
typeid(ircd::string_view), typeid(int64_t)
}
};
const ircd::database::descriptor
events_hashes_descriptor
{
// name
"hashes",
// explanation
R"(### protocol note:
### developer note:
key is event_id.
)",
// typing (key, value)
{
typeid(ircd::string_view), typeid(ircd::string_view)
}
};
const ircd::database::descriptor
events_membership_descriptor
{
// name
"membership",
// explanation
R"(### protocol note:
### developer note:
key is event_id.
)",
// typing (key, value)
{
typeid(ircd::string_view), typeid(ircd::string_view)
}
};
const ircd::database::descriptor
events_prev_events_descriptor
{
// name
"prev_events",
// explanation
R"(### protocol note:
### developer note:
key is event_id.
)",
// typing (key, value)
{
typeid(ircd::string_view), typeid(ircd::string_view)
}
};
const ircd::database::descriptor
events_prev_state_descriptor
{
// name
"prev_state",
// explanation
R"(### protocol note:
### developer note:
key is event_id.
)",
// typing (key, value)
{
typeid(ircd::string_view), typeid(ircd::string_view)
}
};
/// prefix transform for event_id suffixes
///
/// This transform expects a concatenation ending with an event_id which means
/// the prefix can be the same for multiple event_id's; therefor we can find
/// or iterate "event_id in X" where X is some key like a room_id
///
const ircd::db::prefix_transform
event_id_in
{
"event_id in",
[](const ircd::string_view &key)
{
return key.find('$') != key.npos;
},
[](const ircd::string_view &key)
{
return rsplit(key, '$').first;
}
};
const ircd::database::descriptor
event_id_in_sender
{
// name
"event_id in sender",
// explanation
R"(### developer note:
key is "@sender$event_id"
the prefix transform is in effect. this column indexes events by
sender offering an iterable bound of the index prefixed by sender
)",
// typing (key, value)
{
typeid(ircd::string_view), typeid(ircd::string_view)
},
// options
{},
// comparator
{},
// prefix transform
event_id_in,
};
const ircd::database::descriptor
state_head_for_event_id_in_room_id
{
// name
"state_head for event_id in room_id",
// explanation
R"(### developer note:
key is "!room_id$event_id"
the prefix transform is in effect. this column indexes events by
room_id offering an iterable bound of the index prefixed by room_id
)",
// typing (key, value)
{
typeid(ircd::string_view), typeid(ircd::string_view)
},
// options
{},
// comparator - sorts from highest to lowest
{}, //ircd::db::reverse_cmp_ircd::string_view{},
// prefix transform
event_id_in,
};
/// prefix transform for origin in
///
/// This transform expects a concatenation ending with an origin which means
/// the prefix can be the same for multiple origins; therefor we can find
/// or iterate "origin in X" where X is some repeated prefix
///
/// TODO: strings will have character conflicts. must address
const ircd::db::prefix_transform
origin_in
{
"origin in",
[](const ircd::string_view &key)
{
return has(key, ":::");
//return key.find(':') != key.npos;
},
[](const ircd::string_view &key)
{
return split(key, ":::").first;
//return rsplit(key, ':').first;
}
};
const ircd::database::descriptor
origin_in_room_id
{
// name
"origin in room_id",
// explanation
R"(### developer note:
key is "!room_id:origin"
the prefix transform is in effect. this column indexes origins in a
room_id offering an iterable bound of the index prefixed by room_id
)",
// typing (key, value)
{
typeid(ircd::string_view), typeid(ircd::string_view)
},
// options
{},
// comparator - sorts from highest to lowest
{}, //ircd::db::reverse_cmp_string_view{},
// prefix transform
origin_in,
};
const ircd::database::descriptor
origin_joined_in_room_id
{
// name
"origin_joined in room_id",
// explanation
R"(### developer note:
key is "!room_id:origin"
the prefix transform is in effect. this column indexes origins in a
room_id offering an iterable bound of the index prefixed by room_id
)",
// typing (key, value)
{
typeid(ircd::string_view), typeid(ircd::string_view)
},
// options
{},
// comparator - sorts from highest to lowest
{}, //ircd::db::reverse_cmp_string_view{},
// prefix transform
origin_in,
};
/// prefix transform for room_id
///
/// This transform expects a concatenation ending with a room_id which means
/// the prefix can be the same for multiple room_id's; therefor we can find
/// or iterate "room_id in X" where X is some repeated prefix
///
const ircd::db::prefix_transform room_id_in
{
"room_id in",
[](const ircd::string_view &key)
{
return key.find('!') != key.npos;
},
[](const ircd::string_view &key)
{
return rsplit(key, '!').first;
}
};
/// prefix transform for type,state_key in room_id
///
/// This transform is special for concatenating room_id with type and state_key
/// in that order with prefix being the room_id (this may change to room_id+
/// type
///
/// TODO: arbitrary type strings will have character conflicts. must address
/// TODO: with grammars.
const ircd::db::prefix_transform type_state_key_in_room_id
{
"type,state_key in room_id",
[](const ircd::string_view &key)
{
return key.find("..") != key.npos;
},
[](const ircd::string_view &key)
{
return split(key, "..").first;
}
};
const ircd::database::descriptor
event_id_for_type_state_key_in_room_id
{
// name
"event_id for type,state_key in room_id",
// explanation
R"(### developer note:
)",
// typing (key, value)
{
typeid(ircd::string_view), typeid(ircd::string_view)
},
// options
{},
// comparator
{},
// prefix transform
type_state_key_in_room_id
};
const ircd::database::descriptor
prev_event_id_for_event_id_in_room_id
{
// name
"prev_event_id for event_id in room_id",
// explanation
R"(### developer note:
)",
// typing (key, value)
{
typeid(ircd::string_view), typeid(ircd::string_view)
},
// options
{},
// comparator
{},
// prefix transform
event_id_in
};
/// prefix transform for event_id in room_id,type,state_key
///
/// This transform is special for concatenating room_id with type and state_key
/// and event_id in that order with prefix being the room_id,type,state_key. This
/// will index multiple event_ids with the same type,state_key in a room which
/// allows for a temporal depth to the database; event_id for type,state_key only
/// resolves to a single latest event and overwrites itself as per the room state
/// algorithm whereas this can map all of them and then allows for tracing.
///
/// TODO: arbitrary type strings will have character conflicts. must address
/// TODO: with grammars.
const ircd::db::prefix_transform
event_id_in_room_id_type_state_key
{
"event_id in room_id,type_state_key",
[](const ircd::string_view &key)
{
return has(key, '$');
},
[](const ircd::string_view &key)
{
return split(key, '$').first;
}
};
const ircd::database::descriptor
prev_event_id_for_type_state_key_event_id_in_room_id
{
// name
"prev_event_id for type,state_key,event_id in room_id",
// explanation
R"(### developer note:
)",
// typing (key, value)
{
typeid(ircd::string_view), typeid(ircd::string_view)
},
// options
{},
// comparator
{},
// prefix transform
event_id_in_room_id_type_state_key
};
const ircd::database::descriptor
state_head
{
// name
"state_head",
// explanation
R"(### developer note:
key is "!room_id"
value is the key of a state_node
)",
// typing (key, value)
{
typeid(ircd::string_view), typeid(ircd::string_view)
},
// options
{},
// comparator
{},
// prefix transform
{},
};
const ircd::database::descriptor
state_node
{
// name
"state_node",
// explanation
R"(### developer note:
)",
// typing (key, value)
{
typeid(ircd::string_view), typeid(ircd::string_view)
},
// options
{},
// comparator
{},
// prefix transform
{},
};
decltype(ircd::m::event::description)
ircd::m::event::description
{
{ "default" },
////////
//
// These columns directly represent event fields indexed by event_id and
// the value is the actual event values. Some values may be JSON, like
// content.
//
events_event_id_descriptor,
events_type_descriptor,
events_content_descriptor,
events_room_id_descriptor,
events_sender_descriptor,
events_state_key_descriptor,
events_origin_descriptor,
events_origin_server_ts_descriptor,
events_unsigned_descriptor,
events_signatures_descriptor,
events_auth_events_descriptor,
events_depth_descriptor,
events_hashes_descriptor,
events_membership_descriptor,
events_prev_events_descriptor,
events_prev_state_descriptor,
////////
//
// These columns are metadata composed from the event data. Specifically,
// they are designed for fast sequential iterations.
//
// (sender, event_id) => ()
// Sequence of all events in all rooms for a sender, EVER
// * broad but useful in cases
event_id_in_sender,
// (room_id, event_id) => (state_head)
// Sequence of all events for a room, EVER
// * broad but useful in cases
// ? eliminate for prev_event?
// ? eliminate/combine with state tree related?
state_head_for_event_id_in_room_id,
// (room_id, origin) => ()
// Sequence of all origins for a room, EVER
//TODO: value should have [JOIN, LEAVE, ...) counts/data
//TODO: remove?
origin_in_room_id,
// (room_id, origin) => ()
// Sequence of all origins with joined member for a room, AT PRESENT
// * Intended to be a fast sequential iteration for sending out messages.
origin_joined_in_room_id,
// (room_id, type, state_key) => (event_id)
// Sequence of events of type+state_key in a room, AT PRESENT
// * Fast for current room state iteration, but only works for the present.
event_id_for_type_state_key_in_room_id,
////////
//
// These columns are metadata composed from the event data. They are
// linked forward lists where the value is used to lookup the next key
// TODO: these might be better as sequences; if not removed altogether.
//
// (room_id, event_id) => (prev event_id)
// List of events in a room resolving to the previous event in a room
// in our subjective euclidean tape TOTAL order.
// * This is where any branches in the DAG are linearized based on how we
// feel the state machine should execute them one by one.
// * This is not a sequence; each value is the key for another lookup.
prev_event_id_for_event_id_in_room_id,
// (room_id, type, state_key, event_id) => (prev event_id)
// Events of a (type, state_key) in a room resolving to the previous event
// of (type, state_key) in a room in our subjective euclidean tape order.
// * Similar to the above but focuses only on state events for various
// "state chains"
prev_event_id_for_type_state_key_event_id_in_room_id,
////////
//
// These columns are metadata composed from the event data. They are
// used to create structures that can represent the state of a room
// at any given event.
//
// (room_id) => (state_head)
state_head,
// (state tree node id) => (state tree node)
//
state_node,
};

View file

@ -190,8 +190,8 @@ ircd::m::init_listener(const json::object &conf,
void
ircd::m::init::bootstrap()
{
assert(event::events);
assert(db::sequence(*event::events) == 0);
assert(dbs::events);
assert(db::sequence(*dbs::events) == 0);
ircd::log::notice
(

View file

@ -20,6 +20,14 @@ decltype(ircd::m::vm::fronts)
ircd::m::vm::fronts
{};
ircd::ctx::view<const ircd::m::event>
ircd::m::vm::inserted
{};
uint64_t
ircd::m::vm::current_sequence
{};
/// This function takes an event object vector and adds our origin and event_id
/// and hashes and signature and attempts to inject the event into the core.
/// The caller is expected to have done their best to check if this event will
@ -202,119 +210,27 @@ ircd::m::vm::commit(json::iov &iov)
namespace ircd::m::vm
{
bool check_fault_resume(eval &);
void check_fault_throw(eval &);
void write(eval &);
enum fault evaluate(eval &, const event &);
enum fault evaluate(eval &, const vector_view<const event> &);
}
decltype(ircd::m::vm::eval::default_opts)
ircd::m::vm::eval::default_opts
{};
ircd::m::vm::eval::eval()
:eval(default_opts)
{
}
ircd::m::vm::eval::eval(const struct opts &opts)
:opts{&opts}
{
}
enum ircd::m::vm::fault
ircd::m::vm::eval::operator()()
ircd::m::vm::eval::eval(const event &event,
const struct opts &opts)
:opts{&opts}
{
assert(0);
return fault::ACCEPT;
}
enum ircd::m::vm::fault
ircd::m::vm::eval::operator()(const json::array &events)
{
std::vector<m::event> event;
event.reserve(events.count());
auto it(begin(events));
for(; it != end(events); ++it)
event.emplace_back(*it);
return operator()(vector_view<const m::event>(event));
}
enum ircd::m::vm::fault
ircd::m::vm::eval::operator()(const json::vector &events)
{
std::vector<m::event> event;
event.reserve(events.count());
auto it(begin(events));
for(; it != end(events); ++it)
event.emplace_back(*it);
return operator()(vector_view<const m::event>(event));
operator()(event);
}
enum ircd::m::vm::fault
ircd::m::vm::eval::operator()(const event &event)
{
return operator()(vector_view<const m::event>(&event, 1));
}
enum ircd::m::vm::fault
ircd::m::vm::eval::operator()(const vector_view<const event> &events)
{
static const size_t max
{
128
};
for(size_t i(0); i < events.size(); i += max)
{
const vector_view<const event> evs
{
events.data() + i, std::min(events.size() - i, size_t(max))
};
enum fault code;
switch((code = evaluate(*this, evs)))
{
case fault::ACCEPT:
continue;
default:
//check_fault_throw(*this);
return code;
}
}
return fault::ACCEPT;
}
enum ircd::m::vm::fault
ircd::m::vm::evaluate(eval &eval,
const vector_view<const event> &event)
{
for(size_t i(0); i < event.size(); ++i)
{
if(evaluate(eval, event[i]) == fault::ACCEPT)
{
log.info("%s", pretty_oneline(event[i]));
vm::inserted.notify(event[i]);
}
}
write(eval);
return fault::ACCEPT;
}
enum ircd::m::vm::fault
ircd::m::vm::evaluate(eval &eval,
const event &event)
{
const auto &event_id
{
@ -323,28 +239,49 @@ ircd::m::vm::evaluate(eval &eval,
const auto &depth
{
json::get<"depth"_>(event, 0)
at<"depth"_>(event)
};
const auto &room_id
{
json::get<"room_id"_>(event)
at<"room_id"_>(event)
};
auto &front
const auto &type
{
fronts.get(room_id, event)
at<"type"_>(event)
};
front.top = depth;
auto code
const event::prev prev{event};
const json::array prev0
{
fault::ACCEPT
at<"prev_events"_>(prev)[0]
};
dbs::write(event, eval.txn);
return code;
const string_view previd
{
unquote(prev0[0])
};
char old_rootbuf[64];
const auto old_root
{
previd? dbs::state_root(old_rootbuf, previd) : string_view{}
};
char new_rootbuf[64];
const auto new_root
{
dbs::write(txn, new_rootbuf, old_root, event)
};
++cs;
log.info("%s %s",
new_root,
pretty_oneline(event));
write(*this);
return fault::ACCEPT;
}
void
@ -359,54 +296,6 @@ ircd::m::vm::write(eval &eval)
eval.cs = 0;
}
/*
bool
ircd::m::vm::check_fault_resume(eval &eval)
{
switch(fault)
{
case fault::ACCEPT:
return true;
default:
check_fault_throw(eval);
return false;
}
}
void
ircd::m::vm::check_fault_throw(eval &eval)
{
// With nothrow there is nothing else for this function to do as the user
// will have to test the fault code and error ptr manually.
if(eval.opts->nothrow)
return;
switch(fault)
{
case fault::ACCEPT:
return;
case fault::GENERAL:
if(eval.error)
std::rethrow_exception(eval.error);
// [[fallthrough]]
default:
throw VM_FAULT("(%u): %s", uint(fault), reflect(fault));
}
}
*/
ircd::ctx::view<const ircd::m::event>
ircd::m::vm::inserted
{};
uint64_t
ircd::m::vm::current_sequence
{};
ircd::m::vm::front &
ircd::m::vm::fronts::get(const room::id &room_id,
const event &event)
@ -520,92 +409,6 @@ ircd::m::vm::fetch(const room::id &room_id,
return front;
}
bool
ircd::m::vm::test(const query<> &where)
{
return test(where, [](const auto &event)
{
return true;
});
}
bool
ircd::m::vm::test(const query<> &clause,
const closure_bool &closure)
{
bool ret{false};
dbs::_query(clause, [&ret, &closure](const auto &event)
{
ret = closure(event);
return true;
});
return ret;
}
bool
ircd::m::vm::until(const query<> &where)
{
return until(where, [](const auto &event)
{
return true;
});
}
bool
ircd::m::vm::until(const query<> &clause,
const closure_bool &closure)
{
bool ret{true};
dbs::_query(clause, [&ret, &closure](const auto &event)
{
ret = closure(event);
return ret;
});
return ret;
}
size_t
ircd::m::vm::count(const query<> &where)
{
return count(where, [](const auto &event)
{
return true;
});
}
size_t
ircd::m::vm::count(const query<> &where,
const closure_bool &closure)
{
size_t i(0);
for_each(where, [&closure, &i](const auto &event)
{
i += closure(event);
});
return i;
}
void
ircd::m::vm::for_each(const closure &closure)
{
const query<where::noop> where{};
for_each(where, closure);
}
void
ircd::m::vm::for_each(const query<> &clause,
const closure &closure)
{
dbs::_query(clause, [&closure](const auto &event)
{
closure(event);
return false;
});
}
ircd::string_view
ircd::m::vm::reflect(const enum fault &code)
{