diff --git a/include/ircd/m/dbs.h b/include/ircd/m/dbs.h index da17112fa..7691fbc63 100644 --- a/include/ircd/m/dbs.h +++ b/include/ircd/m/dbs.h @@ -35,6 +35,7 @@ namespace ircd::m::dbs extern db::column event_idx; // event_id => event_idx extern db::column event_json; // event_idx => full json extern db::index event_refs; // event_idx | ref_type, event_idx + extern db::index event_sender; // host | local, event_idx extern db::index room_head; // room_id | event_id => event_idx extern db::index room_events; // room_id | depth, event_idx => node_id extern db::index room_joined; // room_id | origin, member => event_idx @@ -50,6 +51,11 @@ namespace ircd::m::dbs std::tuple event_refs_key(const string_view &amalgam); string_view reflect(const ref &); + constexpr size_t EVENT_SENDER_KEY_MAX_SIZE {id::MAX_SIZE + 1 + 8}; + string_view event_sender_key(const mutable_buffer &out, const string_view &origin, const string_view &localpart = {}, const event::idx & = 0); + string_view event_sender_key(const mutable_buffer &out, const id::user &, const event::idx &); + std::tuple event_sender_key(const string_view &amalgam); + constexpr size_t ROOM_HEAD_KEY_MAX_SIZE {id::MAX_SIZE + 1 + id::MAX_SIZE}; string_view room_head_key(const mutable_buffer &out, const id::room &, const id::event &); string_view room_head_key(const string_view &amalgam); @@ -135,6 +141,9 @@ struct ircd::m::dbs::write_opts /// Whether the event removes the prev_events it references from the /// room_head. This defaults to true and should almost always be true. bool room_refs {true}; + + /// Whether to update the event_sender index. + bool event_sender {true}; }; /// Types of references indexed by event_refs. This is a single byte integer, @@ -250,6 +259,14 @@ namespace ircd::m::dbs::desc extern const db::comparator events__event_refs__cmp; extern const db::descriptor events__event_refs; + // events sender + extern conf::item events__event_sender__block__size; + extern conf::item events__event_sender__meta_block__size; + extern conf::item events__event_sender__cache__size; + extern conf::item events__event_sender__cache_comp__size; + extern const db::prefix_transform events__event_sender__pfx; + extern const db::descriptor events__event_sender; + // room head mapping sequence extern conf::item events__room_head__block__size; extern conf::item events__room_head__meta_block__size; @@ -304,6 +321,7 @@ namespace ircd::m::dbs string_view _index_redact(db::txn &, const event &, const write_opts &); string_view _index_other(db::txn &, const event &, const write_opts &); string_view _index_room(db::txn &, const event &, const write_opts &); + void _index_event_sender(db::txn &, const event &, const write_opts &); void _index_event_refs_m_room_redaction(db::txn &, const event &, const write_opts &); void _index_event_refs_m_receipt_m_read(db::txn &, const event &, const write_opts &); void _index_event_refs_m_relates_m_reply(db::txn &, const event &, const write_opts &); diff --git a/ircd/m_dbs.cc b/ircd/m_dbs.cc index b7ed33c12..515d16bd2 100644 --- a/ircd/m_dbs.cc +++ b/ircd/m_dbs.cc @@ -36,6 +36,11 @@ decltype(ircd::m::dbs::event_refs) ircd::m::dbs::event_refs {}; +/// Linkage for a reference to the event_sender column. +decltype(ircd::m::dbs::event_sender) +ircd::m::dbs::event_sender +{}; + /// Linkage for a reference to the room_head column decltype(ircd::m::dbs::room_head) ircd::m::dbs::room_head @@ -140,6 +145,7 @@ ircd::m::dbs::init::init(std::string dbopts) event_idx = db::column{*events, desc::events__event_idx.name}; event_json = db::column{*events, desc::events__event_json.name}; event_refs = db::index{*events, desc::events__event_refs.name}; + event_sender = db::index{*events, desc::events__event_sender.name}; room_head = db::index{*events, desc::events__room_head.name}; room_events = db::index{*events, desc::events__room_events.name}; room_joined = db::index{*events, desc::events__room_joined.name}; @@ -326,6 +332,9 @@ ircd::m::dbs::_index_event(db::txn &txn, if(opts.event_refs.any()) _index_event_refs(txn, event, opts); + + if(opts.event_sender) + _index_event_sender(txn, event, opts); } void @@ -721,6 +730,30 @@ ircd::m::dbs::_index_event_refs_m_room_redaction(db::txn &txn, }; } +void +ircd::m::dbs::_index_event_sender(db::txn &txn, + const event &event, + const write_opts &opts) +{ + assert(opts.event_sender); + assert(opts.event_idx); + assert(json::get<"sender"_>(event)); + + thread_local char buf[EVENT_SENDER_KEY_MAX_SIZE]; + const string_view &key + { + event_sender_key(buf, at<"sender"_>(event), opts.event_idx) + }; + + db::txn::append + { + txn, dbs::event_sender, + { + opts.op, key, string_view{} + } + }; +} + ircd::string_view ircd::m::dbs::_index_room(db::txn &txn, const event &event, @@ -1559,6 +1592,185 @@ ircd::m::dbs::desc::events__event_refs size_t(events__event_refs__meta_block__size), }; +// +// event_sender +// + +decltype(ircd::m::dbs::desc::events__event_sender__block__size) +ircd::m::dbs::desc::events__event_sender__block__size +{ + { "name", "ircd.m.dbs.events._event_sender.block.size" }, + { "default", 512L }, +}; + +decltype(ircd::m::dbs::desc::events__event_sender__meta_block__size) +ircd::m::dbs::desc::events__event_sender__meta_block__size +{ + { "name", "ircd.m.dbs.events._event_sender.meta_block.size" }, + { "default", 4096L }, +}; + +decltype(ircd::m::dbs::desc::events__event_sender__cache__size) +ircd::m::dbs::desc::events__event_sender__cache__size +{ + { + { "name", "ircd.m.dbs.events._event_sender.cache.size" }, + { "default", long(16_MiB) }, + }, [] + { + const size_t &value{events__event_sender__cache__size}; + db::capacity(db::cache(event_sender), value); + } +}; + +decltype(ircd::m::dbs::desc::events__event_sender__cache_comp__size) +ircd::m::dbs::desc::events__event_sender__cache_comp__size +{ + { + { "name", "ircd.m.dbs.events._event_sender.cache_comp.size" }, + { "default", long(0_MiB) }, + }, [] + { + const size_t &value{events__event_sender__cache_comp__size}; + db::capacity(db::cache_compressed(event_sender), value); + } +}; + +ircd::string_view +ircd::m::dbs::event_sender_key(const mutable_buffer &out, + const user::id &user_id, + const event::idx &event_idx) +{ + return event_sender_key(out, user_id.host(), user_id.local(), event_idx); +} + +ircd::string_view +ircd::m::dbs::event_sender_key(const mutable_buffer &out_, + const string_view &origin, + const string_view &localpart, + const event::idx &event_idx) +{ + assert(size(out_) >= EVENT_SENDER_KEY_MAX_SIZE); + assert(!event_idx || localpart); + assert(!localpart || startswith(localpart, '@')); + + mutable_buffer out{out_}; + consume(out, copy(out, origin)); + consume(out, copy(out, localpart)); + + if(localpart && event_idx) + { + consume(out, copy(out, "\0"_sv)); + consume(out, copy(out, byte_view(event_idx))); + } + + return { data(out_), data(out) }; +} + +std::tuple +ircd::m::dbs::event_sender_key(const string_view &amalgam) +{ + const auto &parts + { + split(amalgam, '\0') + }; + + + assert(!empty(parts.first) && !empty(parts.second)); + assert(startswith(parts.first, '@')); + + return + { + parts.first, + byte_view(parts.second), + }; +} + +const ircd::db::prefix_transform +ircd::m::dbs::desc::events__event_sender__pfx +{ + "_event_sender", + [](const string_view &key) + { + return has(key, '@'); + }, + + [](const string_view &key) + { + const auto &parts + { + split(key, '@') + }; + + return parts.first; + } +}; + +const ircd::db::descriptor +ircd::m::dbs::desc::events__event_sender +{ + // name + "_event_sender", + + // explanation + R"(Index of senders to their events. + + origin | localpart, event_idx => -- + + The senders of events are indexes by this column. This allows for all + events from a sender to be iterated. Additionally, all events from a + server and all known servers can be iterated from this column. + + They key is made from a user mxid and an event_id, where the mxid is + part-swapped so the origin comes first, and the @localpart comes after. + Lookups can be performed for an origin or a full user_mxid. + + The prefix transform is in effect; the prefix domain is the origin. We + can efficiently iterate all events from an origin. We can slightly less + efficiently iterate all users from an origin, as well as iterate all + origins known. + + Note that the indexer of this column ignores the actual "origin" field + of an event. Only the "sender" data is used here. + + )", + + // typing (key, value) + { + typeid(string_view), typeid(string_view) + }, + + // options + {}, + + // comparator + {}, + + // prefix transform + events__event_sender__pfx, + + // 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_sender__block__size), + + // meta_block size + size_t(events__event_sender__meta_block__size), +}; + // // room_head // @@ -3746,6 +3958,10 @@ ircd::m::dbs::desc::events // Reverse mapping of the event reference graph. events__event_refs, + // origin | sender, event_idx + // Mapping of senders to event_idx's they are the sender of. + events__event_sender, + // (room_id, (depth, event_idx)) => (state_root) // Sequence of all events for a room, ever. events__room_events,