mirror of
https://github.com/matrix-construct/construct
synced 2024-11-12 13:01:07 +01:00
ircd:Ⓜ️:room: Add consolidated room::purge interface w/ options.
This commit is contained in:
parent
ae5af6a7ed
commit
0fdeda3743
7 changed files with 281 additions and 83 deletions
|
@ -12,8 +12,71 @@
|
||||||
#define HAVE_IRCD_M_ROOM_PURGE_H
|
#define HAVE_IRCD_M_ROOM_PURGE_H
|
||||||
|
|
||||||
/// Erase the room from the database. Cuidado!
|
/// Erase the room from the database. Cuidado!
|
||||||
|
///
|
||||||
|
/// The room purge is an application of multiple event::purge operations.
|
||||||
|
/// By default the entire room is purged. The options can tweak specifics.
|
||||||
|
///
|
||||||
struct ircd::m::room::purge
|
struct ircd::m::room::purge
|
||||||
:returns<size_t>
|
:returns<size_t>
|
||||||
{
|
{
|
||||||
purge(const room &);
|
struct opts;
|
||||||
|
|
||||||
|
static const struct opts opts_default;
|
||||||
|
static log::log log;
|
||||||
|
|
||||||
|
private:
|
||||||
|
const m::room &room;
|
||||||
|
const struct opts &opts;
|
||||||
|
db::txn txn;
|
||||||
|
|
||||||
|
bool match(const event::idx &, const event &) const;
|
||||||
|
bool match(const uint64_t &, const event::idx &) const;
|
||||||
|
void timeline();
|
||||||
|
void state();
|
||||||
|
void commit();
|
||||||
|
|
||||||
|
public:
|
||||||
|
purge(const m::room &, const struct opts & = opts_default);
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Options for purge
|
||||||
|
struct ircd::m::room::purge::opts
|
||||||
|
{
|
||||||
|
/// Limit purge to the index window
|
||||||
|
pair<event::idx> idx
|
||||||
|
{
|
||||||
|
0, std::numeric_limits<event::idx>::max()
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Limit purge to the depth window
|
||||||
|
pair<uint64_t> depth
|
||||||
|
{
|
||||||
|
0, std::numeric_limits<uint64_t>::max()
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Limit purge to events matching the filter
|
||||||
|
const m::event_filter *filter {nullptr};
|
||||||
|
|
||||||
|
/// Set to false to not purge any state events.
|
||||||
|
bool state {true};
|
||||||
|
|
||||||
|
/// Set to false to not purge the present state, but prior (replaced)
|
||||||
|
/// states will be purged if other options permit.
|
||||||
|
bool present {true};
|
||||||
|
|
||||||
|
/// Set to false to not purge replaced states; the only state events
|
||||||
|
/// considered for purge are present states if other options permit.
|
||||||
|
bool history {true};
|
||||||
|
|
||||||
|
/// Timeline in this context refers to non-state events. Set to false to
|
||||||
|
/// only allow state events to be purged; true to allow non-state events
|
||||||
|
/// if other options permit.
|
||||||
|
bool timeline {true};
|
||||||
|
|
||||||
|
/// Log an INFO message for the final transaction; takes precedence
|
||||||
|
/// if both debuglog and infolog are true.
|
||||||
|
bool infolog_txn {false};
|
||||||
|
|
||||||
|
/// Log a DEBUG message for the final transaction.
|
||||||
|
bool debuglog_txn {true};
|
||||||
};
|
};
|
||||||
|
|
|
@ -97,7 +97,6 @@ struct ircd::m::room::state
|
||||||
static event::idx next(const event::idx &);
|
static event::idx next(const event::idx &);
|
||||||
|
|
||||||
static bool present(const event::idx &);
|
static bool present(const event::idx &);
|
||||||
static size_t purge_replaced(const room::id &);
|
|
||||||
static bool is(std::nothrow_t, const event::idx &);
|
static bool is(std::nothrow_t, const event::idx &);
|
||||||
static bool is(const event::idx &);
|
static bool is(const event::idx &);
|
||||||
};
|
};
|
||||||
|
|
|
@ -122,6 +122,7 @@ libircd_matrix_la_SOURCES += room_content.cc
|
||||||
libircd_matrix_la_SOURCES += room_message.cc
|
libircd_matrix_la_SOURCES += room_message.cc
|
||||||
libircd_matrix_la_SOURCES += room_messages.cc
|
libircd_matrix_la_SOURCES += room_messages.cc
|
||||||
libircd_matrix_la_SOURCES += room_power.cc
|
libircd_matrix_la_SOURCES += room_power.cc
|
||||||
|
libircd_matrix_la_SOURCES += room_purge.cc
|
||||||
libircd_matrix_la_SOURCES += room_state.cc
|
libircd_matrix_la_SOURCES += room_state.cc
|
||||||
libircd_matrix_la_SOURCES += room_state_history.cc
|
libircd_matrix_la_SOURCES += room_state_history.cc
|
||||||
libircd_matrix_la_SOURCES += room_state_space.cc
|
libircd_matrix_la_SOURCES += room_state_space.cc
|
||||||
|
|
|
@ -8,24 +8,6 @@
|
||||||
// copyright notice and this permission notice is present in all copies. The
|
// copyright notice and this permission notice is present in all copies. The
|
||||||
// full license for this software is available in the LICENSE file.
|
// full license for this software is available in the LICENSE file.
|
||||||
|
|
||||||
ircd::m::room::purge::purge(const room &room)
|
|
||||||
:returns<size_t>{0}
|
|
||||||
{
|
|
||||||
db::txn txn
|
|
||||||
{
|
|
||||||
*m::dbs::events
|
|
||||||
};
|
|
||||||
|
|
||||||
room.for_each([this, &txn]
|
|
||||||
(const m::event::idx &event_idx)
|
|
||||||
{
|
|
||||||
ret += m::event::purge(txn, event_idx);
|
|
||||||
});
|
|
||||||
|
|
||||||
assert(ret || !txn.size());
|
|
||||||
txn();
|
|
||||||
}
|
|
||||||
|
|
||||||
ircd::m::room
|
ircd::m::room
|
||||||
ircd::m::create(const id::room &room_id,
|
ircd::m::create(const id::room &room_id,
|
||||||
const id::user &creator,
|
const id::user &creator,
|
||||||
|
|
191
matrix/room_purge.cc
Normal file
191
matrix/room_purge.cc
Normal file
|
@ -0,0 +1,191 @@
|
||||||
|
// Matrix Construct
|
||||||
|
//
|
||||||
|
// Copyright (C) Matrix Construct Developers, Authors & Contributors
|
||||||
|
// Copyright (C) 2016-2023 Jason Volk <jason@zemos.net>
|
||||||
|
//
|
||||||
|
// 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::room::purge::opts_default)
|
||||||
|
ircd::m::room::purge::opts_default;
|
||||||
|
|
||||||
|
decltype(ircd::m::room::purge::log)
|
||||||
|
ircd::m::room::purge::log
|
||||||
|
{
|
||||||
|
"m.room.purge"
|
||||||
|
};
|
||||||
|
|
||||||
|
ircd::m::room::purge::purge(const m::room &room,
|
||||||
|
const struct opts &opts)
|
||||||
|
:returns<size_t>
|
||||||
|
{
|
||||||
|
0
|
||||||
|
}
|
||||||
|
,room
|
||||||
|
{
|
||||||
|
room
|
||||||
|
}
|
||||||
|
,opts
|
||||||
|
{
|
||||||
|
opts
|
||||||
|
}
|
||||||
|
,txn
|
||||||
|
{
|
||||||
|
*dbs::events
|
||||||
|
}
|
||||||
|
{
|
||||||
|
if(opts.timeline)
|
||||||
|
timeline();
|
||||||
|
else if(opts.state)
|
||||||
|
state();
|
||||||
|
|
||||||
|
commit();
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
ircd::m::room::purge::commit()
|
||||||
|
{
|
||||||
|
assert(ret || !txn.size());
|
||||||
|
if(!ret)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if(opts.debuglog_txn || opts.infolog_txn)
|
||||||
|
log::logf
|
||||||
|
{
|
||||||
|
log, opts.infolog_txn? log::level::INFO: log::level::DEBUG,
|
||||||
|
"Purging %s events:%zu txn[cells:%zu bytes:%zu] opts[st:%b pr:%b hs:%b tl:%b depth[%lu:%lu]]",
|
||||||
|
string_view{room.room_id},
|
||||||
|
ret,
|
||||||
|
txn.size(),
|
||||||
|
txn.bytes(),
|
||||||
|
opts.state,
|
||||||
|
opts.present,
|
||||||
|
opts.history,
|
||||||
|
opts.timeline,
|
||||||
|
opts.depth.first,
|
||||||
|
opts.depth.second,
|
||||||
|
};
|
||||||
|
|
||||||
|
txn();
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
ircd::m::room::purge::state()
|
||||||
|
{
|
||||||
|
const room::state::space space
|
||||||
|
{
|
||||||
|
room
|
||||||
|
};
|
||||||
|
|
||||||
|
space.for_each([this]
|
||||||
|
(const auto &, const auto &, const auto &depth, const auto &event_idx)
|
||||||
|
{
|
||||||
|
if(!match(depth, event_idx))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
const event::fetch event
|
||||||
|
{
|
||||||
|
std::nothrow, event_idx
|
||||||
|
};
|
||||||
|
|
||||||
|
if(unlikely(!event.valid))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
if(!match(event_idx, event))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
const auto purged
|
||||||
|
{
|
||||||
|
event::purge(txn, event_idx, event)
|
||||||
|
};
|
||||||
|
|
||||||
|
ret += purged;
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
ircd::m::room::purge::timeline()
|
||||||
|
{
|
||||||
|
room::events it
|
||||||
|
{
|
||||||
|
room, opts.depth.second
|
||||||
|
};
|
||||||
|
|
||||||
|
for(; it && it.depth() >= opts.depth.first; --it)
|
||||||
|
{
|
||||||
|
if(!match(it.depth(), it.event_idx()))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
bool valid;
|
||||||
|
const event &event
|
||||||
|
{
|
||||||
|
it.fetch(std::nothrow, &valid)
|
||||||
|
};
|
||||||
|
|
||||||
|
if(unlikely(!valid))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if(!match(it.event_idx(), event))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
const bool purged
|
||||||
|
{
|
||||||
|
event::purge(txn, it.event_idx(), event)
|
||||||
|
};
|
||||||
|
|
||||||
|
ret += purged;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
ircd::m::room::purge::match(const uint64_t &depth,
|
||||||
|
const event::idx &event_idx)
|
||||||
|
const
|
||||||
|
{
|
||||||
|
if(depth < opts.depth.first)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if(depth > opts.depth.second)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if(event_idx < opts.idx.first)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if(event_idx > opts.idx.second)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
ircd::m::room::purge::match(const event::idx &event_idx,
|
||||||
|
const event &event)
|
||||||
|
const
|
||||||
|
{
|
||||||
|
if(!opts.timeline)
|
||||||
|
if(!defined(json::get<"state_key"_>(event)))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if(!opts.state || (!opts.present && !opts.history))
|
||||||
|
if(defined(json::get<"state_key"_>(event)))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if(opts.filter)
|
||||||
|
if(!m::match(*opts.filter, event))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if(!opts.present)
|
||||||
|
if(defined(json::get<"state_key"_>(event)))
|
||||||
|
if(room::state::present(event_idx))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if(!opts.history)
|
||||||
|
if(defined(json::get<"state_key"_>(event)))
|
||||||
|
if(!room::state::present(event_idx))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
|
@ -777,39 +777,6 @@ ircd::m::room::state::is(std::nothrow_t,
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t
|
|
||||||
ircd::m::room::state::purge_replaced(const room::id &room_id)
|
|
||||||
{
|
|
||||||
db::txn txn
|
|
||||||
{
|
|
||||||
*m::dbs::events
|
|
||||||
};
|
|
||||||
|
|
||||||
size_t ret(0);
|
|
||||||
m::room::events it
|
|
||||||
{
|
|
||||||
room_id, uint64_t(0)
|
|
||||||
};
|
|
||||||
|
|
||||||
if(!it)
|
|
||||||
return ret;
|
|
||||||
|
|
||||||
for(; it; ++it)
|
|
||||||
{
|
|
||||||
const m::event::idx &event_idx(it.event_idx());
|
|
||||||
static const auto no_action{[](const auto &) noexcept {}};
|
|
||||||
if(!m::get(std::nothrow, event_idx, "state_key", no_action))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if(!m::event::refs(event_idx).count(m::dbs::ref::NEXT_STATE))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
// TODO: erase event
|
|
||||||
}
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool
|
bool
|
||||||
ircd::m::room::state::present(const event::idx &event_idx)
|
ircd::m::room::state::present(const event::idx &event_idx)
|
||||||
{
|
{
|
||||||
|
|
|
@ -10938,28 +10938,6 @@ console_cmd__room__state__space__rebuild(opt &out, const string_view &line)
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
|
||||||
console_cmd__room__state__purge__replaced(opt &out, const string_view &line)
|
|
||||||
{
|
|
||||||
const params param{line, " ",
|
|
||||||
{
|
|
||||||
"room_id",
|
|
||||||
}};
|
|
||||||
|
|
||||||
const auto &room_id
|
|
||||||
{
|
|
||||||
m::room_id(param.at(0))
|
|
||||||
};
|
|
||||||
|
|
||||||
const size_t ret
|
|
||||||
{
|
|
||||||
m::room::state::purge_replaced(room_id)
|
|
||||||
};
|
|
||||||
|
|
||||||
out << "erased " << ret << std::endl;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool
|
bool
|
||||||
console_cmd__room__state__rebuild(opt &out, const string_view &line)
|
console_cmd__room__state__rebuild(opt &out, const string_view &line)
|
||||||
{
|
{
|
||||||
|
@ -12257,22 +12235,39 @@ console_cmd__room__purge(opt &out, const string_view &line)
|
||||||
{
|
{
|
||||||
const params param{line, " ",
|
const params param{line, " ",
|
||||||
{
|
{
|
||||||
"room_id",
|
"room_id", "op", "start_depth", "end_depth"
|
||||||
}};
|
}};
|
||||||
|
|
||||||
const auto &room_id
|
const auto room_id
|
||||||
{
|
{
|
||||||
m::room_id(param.at(0))
|
m::room_id(param.at(0))
|
||||||
};
|
};
|
||||||
|
|
||||||
const m::room room
|
|
||||||
{
|
|
||||||
room_id
|
|
||||||
};
|
|
||||||
|
|
||||||
const size_t ret
|
const size_t ret
|
||||||
{
|
{
|
||||||
m::room::purge(room)
|
m::room::purge
|
||||||
|
{
|
||||||
|
room_id,
|
||||||
|
{
|
||||||
|
.depth =
|
||||||
|
{
|
||||||
|
param.at<uint64_t>("start_depth", 0),
|
||||||
|
param.at<uint64_t>("end_depth", -1UL)
|
||||||
|
},
|
||||||
|
|
||||||
|
// When "timeline" is given here we don't purge state
|
||||||
|
.state = !has(param["op"], "timeline"),
|
||||||
|
|
||||||
|
// When "history" is given here we don't purge present state
|
||||||
|
.present = !has(param["op"], "history"),
|
||||||
|
|
||||||
|
// When "state" is given here we don't purge non-state
|
||||||
|
.timeline = !has(param["op"], "state") && !has(param["op"], "history"),
|
||||||
|
|
||||||
|
// Log an INFO message
|
||||||
|
.infolog_txn = true,
|
||||||
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
out << "erased " << ret << std::endl;
|
out << "erased " << ret << std::endl;
|
||||||
|
|
Loading…
Reference in a new issue