diff --git a/include/ircd/m/dbs/dbs.h b/include/ircd/m/dbs/dbs.h index 092345083..8eba75b9e 100644 --- a/include/ircd/m/dbs/dbs.h +++ b/include/ircd/m/dbs/dbs.h @@ -50,6 +50,7 @@ namespace ircd::m::dbs::appendix #include "event_horizon.h" // event_id | event_idx #include "event_sender.h" // sender | event_idx || hostpart | localpart, event_idx #include "event_type.h" // type | event_idx +#include "event_state.h" // state_key, type, room_id, depth, event_idx #include "room_events.h" // room_id | depth, event_idx #include "room_state.h" // room_id | type, state_key => event_idx #include "room_state_space.h" // room_id | type, state_key, depth, event_idx @@ -155,6 +156,9 @@ enum ircd::m::dbs::appendix::index /// Involves the event_type column (reverse index on the event type). EVENT_TYPE, + /// Involves the event_state column. + EVENT_STATE, + /// Involves room_events table. ROOM_EVENTS, diff --git a/include/ircd/m/dbs/event_state.h b/include/ircd/m/dbs/event_state.h new file mode 100644 index 000000000..2c1ccf5e3 --- /dev/null +++ b/include/ircd/m/dbs/event_state.h @@ -0,0 +1,45 @@ +// Matrix Construct +// +// Copyright (C) Matrix Construct Developers, Authors & Contributors +// Copyright (C) 2016-2019 Jason Volk +// +// Permission to use, copy, modify, and/or distribute this software for any +// purpose with or without fee is hereby granted, provided that the above +// copyright notice and this permission notice is present in all copies. The +// full license for this software is available in the LICENSE file. + +#pragma once +#define HAVE_IRCD_M_DBS_EVENT_STATE_H + +namespace ircd::m::dbs +{ + constexpr size_t EVENT_STATE_KEY_MAX_SIZE + { + 0 + + event::STATE_KEY_MAX_SIZE + + event::TYPE_MAX_SIZE + + id::MAX_SIZE + + 1 + + 8 + + 8 + }; + + using event_state_tuple = std::tuple; + + string_view event_state_key(const mutable_buffer &out, const event_state_tuple &); + event_state_tuple event_state_key(const string_view &); + + // state_key, type, room_id, depth, event_idx + extern db::domain event_state; +} + +namespace ircd::m::dbs::desc +{ + // events _event_state + extern conf::item events__event_state__block__size; + extern conf::item events__event_state__meta_block__size; + extern conf::item events__event_state__cache__size; + extern conf::item events__event_state__cache_comp__size; + extern const db::comparator events__event_state__cmp; + extern const db::descriptor events__event_state; +} diff --git a/matrix/dbs.cc b/matrix/dbs.cc index af7413278..473faeb9d 100644 --- a/matrix/dbs.cc +++ b/matrix/dbs.cc @@ -51,6 +51,11 @@ decltype(ircd::m::dbs::event_type) ircd::m::dbs::event_type {}; +/// Linkage for a reference to the event_state column. +decltype(ircd::m::dbs::event_state) +ircd::m::dbs::event_state +{}; + /// Linkage for a reference to the room_head column decltype(ircd::m::dbs::room_head) ircd::m::dbs::room_head @@ -184,6 +189,7 @@ ircd::m::dbs::init::init(const string_view &servername, event_horizon = db::domain{*events, desc::events__event_horizon.name}; event_sender = db::domain{*events, desc::events__event_sender.name}; event_type = db::domain{*events, desc::events__event_type.name}; + event_state = db::domain{*events, desc::events__event_state.name}; room_head = db::domain{*events, desc::events__room_head.name}; room_events = db::domain{*events, desc::events__room_events.name}; room_joined = db::domain{*events, desc::events__room_joined.name}; @@ -243,6 +249,7 @@ namespace ircd::m::dbs static void _index_room_head(db::txn &, const event &, const write_opts &); static void _index_room_events(db::txn &, const event &, const write_opts &); static void _index_room(db::txn &, const event &, const write_opts &); + static void _index_event_state(db::txn &, const event &, const write_opts &); static void _index_event_type(db::txn &, const event &, const write_opts &); static void _index_event_sender(db::txn &, const event &, const write_opts &); static void _index_event_horizon_resolve(db::txn &, const event &, const write_opts &); //query @@ -351,6 +358,9 @@ ircd::m::dbs::_index_event(db::txn &txn, if(opts.appendix.test(appendix::EVENT_TYPE)) _index_event_type(txn, event, opts); + if(opts.appendix.test(appendix::EVENT_STATE)) + _index_event_state(txn, event, opts); + if(opts.appendix.test(appendix::EVENT_REFS) && opts.event_refs.any()) _index_event_refs(txn, event, opts); @@ -1186,6 +1196,35 @@ ircd::m::dbs::_index_event_type(db::txn &txn, }; } +void +ircd::m::dbs::_index_event_state(db::txn &txn, + const event &event, + const write_opts &opts) +{ + assert(opts.appendix.test(appendix::EVENT_STATE)); + assert(json::get<"type"_>(event)); + assert(opts.event_idx); + + if(!defined(json::get<"state_key"_>(event))) + return; + + thread_local char buf[EVENT_STATE_KEY_MAX_SIZE]; + db::txn::append + { + txn, dbs::event_state, + { + opts.op, event_state_key(buf, event_state_tuple + { + at<"state_key"_>(event), + at<"type"_>(event), + at<"room_id"_>(event), + at<"depth"_>(event), + opts.event_idx, + }) + } + }; +} + void ircd::m::dbs::_index_room(db::txn &txn, const event &event, @@ -2505,6 +2544,206 @@ ircd::m::dbs::desc::events__event_type size_t(events__event_type__meta_block__size), }; +// +// event_state +// + +decltype(ircd::m::dbs::desc::events__event_state__block__size) +ircd::m::dbs::desc::events__event_state__block__size +{ + { "name", "ircd.m.dbs.events._event_state.block.size" }, + { "default", 512L }, +}; + +decltype(ircd::m::dbs::desc::events__event_state__meta_block__size) +ircd::m::dbs::desc::events__event_state__meta_block__size +{ + { "name", "ircd.m.dbs.events._event_state.meta_block.size" }, + { "default", 2048L }, +}; + +decltype(ircd::m::dbs::desc::events__event_state__cache__size) +ircd::m::dbs::desc::events__event_state__cache__size +{ + { + { "name", "ircd.m.dbs.events._event_state.cache.size" }, + { "default", long(16_MiB) }, + }, [] + { + const size_t &value{events__event_state__cache__size}; + db::capacity(db::cache(event_state), value); + } +}; + +decltype(ircd::m::dbs::desc::events__event_state__cache_comp__size) +ircd::m::dbs::desc::events__event_state__cache_comp__size +{ + { + { "name", "ircd.m.dbs.events._event_state.cache_comp.size" }, + { "default", long(0_MiB) }, + }, [] + { + const size_t &value{events__event_state__cache_comp__size}; + db::capacity(db::cache_compressed(event_state), value); + } +}; + +ircd::string_view +ircd::m::dbs::event_state_key(const mutable_buffer &out_, + const event_state_tuple &tuple) +{ + assert(size(out_) >= EVENT_TYPE_KEY_MAX_SIZE); + + const auto &[state_key, type, room_id, depth, event_idx] + { + tuple + }; + + mutable_buffer out{out_}; + consume(out, copy(out, state_key)); + if(!type) + return {data(out_), data(out)}; + + consume(out, copy(out, "\0"_sv)); + consume(out, copy(out, type)); + if(!room_id) + return {data(out_), data(out)}; + + consume(out, copy(out, "\0"_sv)); + consume(out, copy(out, room_id)); + if(!room_id) + return {data(out_), data(out)}; + + consume(out, copy(out, "\0"_sv)); + if(depth < 0) + return {data(out_), data(out)}; + + consume(out, copy(out, byte_view(depth))); + if(!event_idx) + return {data(out_), data(out)}; + + consume(out, copy(out, byte_view(event_idx))); + return {data(out_), data(out)}; +} + +ircd::m::dbs::event_state_tuple +ircd::m::dbs::event_state_key(const string_view &amalgam) +{ + string_view parts[4]; + const auto num + { + tokens(amalgam, '\0', parts) + }; + + assert(num <= 4); + assert(num <= 3 || size(parts[3]) == 16); + return event_state_tuple + { + parts[0], + parts[1], + num >= 3? + m::room::id{parts[2]}: + m::room::id{}, + num >= 4? + int64_t(byte_view(parts[3].substr(0, 8))): + -1L, + num >= 4? + event::idx(byte_view(parts[3].substr(8))): + 0UL, + }; +} + +const ircd::db::comparator +ircd::m::dbs::desc::events__event_state__cmp +{ + "_event_state", + + // less + [](const string_view &a, const string_view &b) + { + const event_state_tuple key[2] + { + event_state_key(a), + event_state_key(b), + }; + + if(std::get<0>(key[0]) != std::get<0>(key[1])) + return std::get<0>(key[0]) < std::get<0>(key[1]); + + if(std::get<1>(key[0]) != std::get<1>(key[1])) + return std::get<1>(key[0]) < std::get<1>(key[1]); + + if(std::get(key[0]) != std::get(key[1])) + return std::get(key[0]) < std::get(key[1]); + + if(std::get(key[0]) != std::get(key[1])) + return std::get(key[0]) > std::get(key[1]); + + if(std::get(key[0]) != std::get(key[1])) + return std::get(key[0]) > std::get(key[1]); + + return false; + }, + + // equal + [](const string_view &a, const string_view &b) + { + return a == b; + } +}; + +const ircd::db::descriptor +ircd::m::dbs::desc::events__event_state +{ + // name + "_event_state", + + // explanation + R"(Index of states of events. + + state_key, type, room_id, depth, event_idx => -- + + The state transitions of events are indexed by this column, + based on the state_key property. + + )", + + // typing (key, value) + { + typeid(string_view), typeid(string_view) + }, + + // options + {}, + + // comparator + events__event_state__cmp, + + // prefix transform + {}, + + // drop column + false, + + // cache size + bool(events_cache_enable)? -1 : 0, //uses conf item + + // cache size for compressed assets + bool(events_cache_comp_enable)? -1 : 0, + + // bloom filter bits + 0, + + // expect queries hit + false, + + // block size + size_t(events__event_state__block__size), + + // meta_block size + size_t(events__event_state__meta_block__size), +}; + // // room_head // @@ -4897,6 +5136,10 @@ ircd::m::dbs::desc::events // Mapping of type strings to event_idx's of that type. events__event_type, + // state_key, type, room_id, depth, event_idx + // Mapping of event states, indexed for application features. + events__event_state, + // (room_id, (depth, event_idx)) // Sequence of all events for a room, ever. events__room_events,