Add incremental backoff to the filter/permchannels/xline_db modules.

Closes #1671.
This commit is contained in:
Sadie Powell 2023-07-21 11:46:49 +01:00
parent 1a746b2c7f
commit 68480cee67
4 changed files with 91 additions and 11 deletions

View File

@ -1909,12 +1909,20 @@
# 'saveperiod' determines how often to check if the database needs to be
# saved to disk. Defaults to every five seconds.
#
# 'backoff' is the value to multiply the saveperiod by every time a save
# fails. When the save succeeds the period will be reset.
#
# 'maxbackoff' is the maximum write period that should be allowed even
# if incremental backoff is enabled.
#
# 'operonly' determines whether a server operator or services server is
# needed to enable the permchannels mode. You should generally keep this
# set to yes unless you know what you are doing.
#<permchanneldb filename="permchannels.conf"
# listmodes="yes"
# saveperiod="5s"
# backoff="2"
# maxbackoff="5m"
# operonly="yes">
#<include file="permchannels.conf" missingokay="yes">
#
@ -2658,7 +2666,10 @@
# Specify the filename for the xline database and how often to check whether
# the database needs to be saved here.
#<xlinedb filename="xline.db" saveperiod="5s">
#<xlinedb filename="xline.db"
# saveperiod="5s"
# backoff="2"
# maxbackoff="5m">
#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
# ____ _ _____ _ _ ____ _ _ _ #

View File

@ -206,6 +206,9 @@ private:
bool dirty = false;
std::string filterconf;
Regex::Engine* factory;
unsigned long saveperiod;
unsigned long maxbackoff;
unsigned char backoff;
void FreeFilters();
public:
@ -238,6 +241,7 @@ public:
ModResult OnPreCommand(std::string& command, CommandBase::Params& parameters, LocalUser* user, bool validated) override;
void OnUnloadModule(Module* mod) override;
bool Tick() override;
bool WriteDatabase();
bool AppliesToMe(User* user, const FilterResult& filter, int flags);
void ReadFilters();
static bool StringToFilterAction(const std::string& str, FilterAction& fa);
@ -645,7 +649,10 @@ void ModuleFilter::ReadConfig(ConfigStatus& status)
filterconf = tag->getString("filename");
if (!filterconf.empty())
filterconf = ServerInstance->Config->Paths.PrependConfig(filterconf);
SetInterval(tag->getDuration("saveperiod", 5));
saveperiod = tag->getDuration("saveperiod", 5);
backoff = tag->getNum<uint8_t>("backoff", 0);
maxbackoff = tag->getDuration("maxbackoff", saveperiod * 120, saveperiod);
SetInterval(saveperiod);
factory = RegexEngine ? (RegexEngine.operator->()) : nullptr;
@ -934,9 +941,29 @@ void ModuleFilter::OnUnloadModule(Module* mod)
bool ModuleFilter::Tick()
{
if (!dirty) // No need to write.
return true;
if (dirty)
{
if (WriteDatabase())
{
// If we were previously unable to write but now can then reset the time interval.
if (GetInterval() != saveperiod)
SetInterval(saveperiod, false);
dirty = false;
}
else
{
// Back off a bit to avoid spamming opers.
if (backoff > 1)
SetInterval(std::min(GetInterval() * backoff, maxbackoff), false);
ServerInstance->Logs.Debug(MODNAME, "Trying again in {} seconds", GetInterval());
}
}
return true;
};
bool ModuleFilter::WriteDatabase()
{
if (filterconf.empty()) // Nothing to write to.
{
dirty = false;
@ -949,7 +976,7 @@ bool ModuleFilter::Tick()
{
ServerInstance->SNO.WriteToSnoMask('f', "Unable to save filters to \"{}\": {} ({})",
newfilterconf, strerror(errno), errno);
return true;
return false;
}
stream
@ -977,7 +1004,7 @@ bool ModuleFilter::Tick()
{
ServerInstance->SNO.WriteToSnoMask('f', "Unable to save filters to \"{}\": {} ({})",
newfilterconf, strerror(errno), errno);
return true;
return false;
}
stream.close();
@ -990,7 +1017,7 @@ bool ModuleFilter::Tick()
{
ServerInstance->SNO.WriteToSnoMask('f', "Unable to replace old filter config \"{}\" with \"{}\": {} ({})",
filterconf, newfilterconf, strerror(errno), errno);
return true;
return false;
}
dirty = false;

View File

@ -176,10 +176,15 @@ class ModulePermanentChannels final
, public Timer
{
private:
PermChannel p;
bool dirty = false;
bool loaded = false;
bool save_listmodes;
unsigned long saveperiod;
unsigned long maxbackoff;
unsigned char backoff;
public:
ModulePermanentChannels()
@ -195,7 +200,10 @@ public:
permchannelsconf = tag->getString("filename");
save_listmodes = tag->getBool("listmodes", true);
p.SetOperOnly(tag->getBool("operonly", true));
SetInterval(tag->getDuration("saveperiod", 5));
saveperiod = tag->getDuration("saveperiod", 5);
backoff = tag->getNum<uint8_t>("backoff", 0);
maxbackoff = tag->getDuration("maxbackoff", saveperiod * 120, saveperiod);
SetInterval(saveperiod);
if (!permchannelsconf.empty())
permchannelsconf = ServerInstance->Config->Paths.PrependConfig(permchannelsconf);
@ -289,8 +297,23 @@ public:
bool Tick() override
{
if (dirty)
WriteDatabase(p, save_listmodes);
dirty = false;
{
if (WriteDatabase(p, save_listmodes))
{
// If we were previously unable to write but now can then reset the time interval.
if (GetInterval() != saveperiod)
SetInterval(saveperiod, false);
dirty = false;
}
else
{
// Back off a bit to avoid spamming opers.
if (backoff > 1)
SetInterval(std::min(GetInterval() * backoff, maxbackoff), false);
ServerInstance->Logs.Debug(MODNAME, "Trying again in {} seconds", GetInterval());
}
}
return true;
}

View File

@ -39,6 +39,9 @@ class ModuleXLineDB final
private:
bool dirty;
std::string xlinedbpath;
unsigned long saveperiod;
unsigned long maxbackoff;
unsigned char backoff;
public:
ModuleXLineDB()
@ -57,7 +60,10 @@ public:
*/
const auto& Conf = ServerInstance->Config->ConfValue("xlinedb");
xlinedbpath = ServerInstance->Config->Paths.PrependData(Conf->getString("filename", "xline.db", 1));
SetInterval(Conf->getDuration("saveperiod", 5));
saveperiod = Conf->getDuration("saveperiod", 5);
backoff = Conf->getNum<uint8_t>("backoff", 0);
maxbackoff = Conf->getDuration("maxbackoff", saveperiod * 120, saveperiod);
SetInterval(saveperiod);
// Read xlines before attaching to events
ReadDatabase();
@ -91,7 +97,20 @@ public:
if (dirty)
{
if (WriteDatabase())
{
// If we were previously unable to write but now can then reset the time interval.
if (GetInterval() != saveperiod)
SetInterval(saveperiod, false);
dirty = false;
}
else
{
// Back off a bit to avoid spamming opers.
if (backoff > 1)
SetInterval(std::min(GetInterval() * backoff, maxbackoff), false);
ServerInstance->Logs.Debug(MODNAME, "Trying again in {} seconds", GetInterval());
}
}
return true;
}