// The Construct // // Copyright (C) The Construct Developers, Authors & Contributors // Copyright (C) 2016-2020 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. decltype(ircd::m::dbs::room_head) ircd::m::dbs::room_head; decltype(ircd::m::dbs::desc::room_head__comp) ircd::m::dbs::desc::room_head__comp { { "name", "ircd.m.dbs._room_head.comp" }, { "default", string_view{} }, }; decltype(ircd::m::dbs::desc::room_head__block__size) ircd::m::dbs::desc::room_head__block__size { { "name", "ircd.m.dbs._room_head.block.size" }, { "default", long(4_KiB) }, }; decltype(ircd::m::dbs::desc::room_head__meta_block__size) ircd::m::dbs::desc::room_head__meta_block__size { { "name", "ircd.m.dbs._room_head.meta_block.size" }, { "default", long(4_KiB) }, }; decltype(ircd::m::dbs::desc::room_head__cache__size) ircd::m::dbs::desc::room_head__cache__size { { { "name", "ircd.m.dbs._room_head.cache.size" }, { "default", long(8_MiB) }, }, [](conf::item &) { const size_t &value{room_head__cache__size}; db::capacity(db::cache(dbs::room_head), value); } }; /// prefix transform for room_id,event_id in room_id /// const ircd::db::prefix_transform ircd::m::dbs::desc::room_head__pfx { "_room_head", [](const string_view &key) { return has(key, "\0"_sv); }, [](const string_view &key) { return split(key, '\0').first; } }; /// This column stores unreferenced (head) events for a room. /// const ircd::db::descriptor ircd::m::dbs::desc::room_head { // name "_room_head", // explanation R"(Unreferenced events in a room. [room_id | event_id => event_idx] The key is a room_id and event_id concatenation. The value is an event_idx of the event_id in the key. The key amalgam was specifically selected to allow for DELETES sent to the WAL "in the blind" for all prev_events when any new event is saved to the database, without making any read IO's to look up anything about the prev reference to remove. This is a fast-moving column where unreferenced events are inserted and then deleted the first time another event is seen which references it so it collects a lot of DELETE commands in the WAL and has to be compacted often to reduce them out. )", // typing (key, value) { typeid(string_view), typeid(uint64_t) }, // options {}, // comparator {}, // prefix transform room_head__pfx, // drop column false, // cache size bool(cache_enable)? -1 : 0, // cache size for compressed assets 0, //no compresed cache // bloom filter bits 0, //table too ephemeral for bloom generation/usefulness // expect queries hit false, // block size size_t(room_head__block__size), // meta_block size size_t(room_head__meta_block__size), // compression string_view{room_head__comp}, // compactor {}, // compaction priority algorithm "kByCompensatedSize"s, // target file size {}, // max bytes for each level {}, // compaction_period 60s * 60 * 24 * 1 // compact the room head every day. }; // // indexer // void ircd::m::dbs::_index_room_head(db::txn &txn, const event &event, const opts &opts) { assert(opts.appendix.test(appendix::ROOM_HEAD)); assert(opts.event_idx); assert(event.event_id); const ctx::critical_assertion ca; thread_local char buf[ROOM_HEAD_KEY_MAX_SIZE]; const string_view &key { room_head_key(buf, at<"room_id"_>(event), event.event_id) }; db::txn::append { txn, room_head, { opts.op, key, byte_view{opts.event_idx} } }; } void ircd::m::dbs::_index_room_head_resolve(db::txn &txn, const event &event, const opts &opts) { assert(opts.appendix.test(appendix::ROOM_HEAD_RESOLVE)); //TODO: If op is DELETE and we are deleting this event and thereby //TODO: potentially creating a gap in the reference graph (just for us //TODO: though) can we *re-add* the prev_events to the head? if(opts.op != db::op::SET) return; const event::prev prev{event}; for(size_t i(0); i < prev.prev_events_count(); ++i) { const auto &event_id { prev.prev_event(i) }; thread_local char buf[ROOM_HEAD_KEY_MAX_SIZE]; const ctx::critical_assertion ca; const string_view &key { room_head_key(buf, at<"room_id"_>(event), event_id) }; db::txn::append { txn, room_head, { db::op::DELETE, key, } }; } } // // key // ircd::string_view ircd::m::dbs::room_head_key(const string_view &amalgam) { const auto &key { lstrip(amalgam, '\0') }; return { key }; } ircd::string_view ircd::m::dbs::room_head_key(const mutable_buffer &out_, const id::room &room_id, const id::event &event_id) { mutable_buffer out{out_}; consume(out, copy(out, room_id)); consume(out, copy(out, '\0')); consume(out, copy(out, event_id)); return { data(out_), data(out) }; }