11#include " chanfix.h"
22
3+ #include " timers.h"
4+
35#include < algorithm>
46#include < cmath>
57#include < fstream>
@@ -187,6 +189,49 @@ ChanFixCore::ChanFixCore(Module* owner)
187189
188190ChanFixCore::~ChanFixCore ()
189191{
192+ this ->db_save_pending = false ;
193+ this ->db_save_timer = nullptr ;
194+ }
195+
196+ class ChanFixCore ::DeferredSaveTimer final
197+ : public Timer
198+ {
199+ ChanFixCore& cf;
200+
201+ public:
202+ DeferredSaveTimer (Module* owner, ChanFixCore& core, time_t seconds)
203+ : Timer(owner, seconds, true )
204+ , cf(core)
205+ {
206+ }
207+
208+ void Tick () override
209+ {
210+ // Nothing queued.
211+ if (!this ->cf .LegacyImportNeedsSave () && !this ->cf .db_save_pending )
212+ return ;
213+
214+ // Avoid writing while not fully synced.
215+ if (!Me || !Me->IsSynced ())
216+ return ;
217+
218+ this ->cf .db_save_pending = false ;
219+ this ->cf .ClearLegacyImportNeedsSave ();
220+ Anope::SaveDatabases ();
221+ }
222+ };
223+
224+ void ChanFixCore::ScheduleDBSave ()
225+ {
226+ if (Anope::ReadOnly)
227+ return ;
228+
229+ this ->db_save_pending = true ;
230+ if (this ->db_save_timer )
231+ return ;
232+
233+ // Coalesce writes: save once within ~5 seconds.
234+ this ->db_save_timer = new DeferredSaveTimer (this ->module , *this , 5 );
190235}
191236
192237void ChanFixCore::LegacyImportIfNeeded ()
@@ -316,7 +361,10 @@ void ChanFixCore::LegacyImportIfNeeded()
316361 // the existing DB, and forcing a save can overwrite the main database file.
317362 // We'll request a save later (after services are synced) from the module.
318363 if (imported > 0 )
364+ {
319365 this ->legacy_import_needs_save = true ;
366+ this ->ScheduleDBSave ();
367+ }
320368
321369 // Move the legacy file out of the way so we don't re-import.
322370 const fs::path migrated = path.string () + " .migrated" ;
@@ -355,6 +403,7 @@ CFChannelData& ChanFixCore::GetOrCreateRecord(Channel* c)
355403 rec->ts = c->created ;
356404 rec->lastupdate = Anope::CurTime;
357405 rec->QueueUpdate ();
406+ this ->ScheduleDBSave ();
358407 return *rec;
359408}
360409
@@ -780,7 +829,10 @@ void ChanFixCore::GatherTick()
780829 }
781830
782831 if (dirty)
832+ {
783833 rec.QueueUpdate ();
834+ this ->ScheduleDBSave ();
835+ }
784836 }
785837}
786838
@@ -822,11 +874,15 @@ void ChanFixCore::ExpireTick()
822874 if (keep)
823875 {
824876 if (dirty)
877+ {
825878 rec.QueueUpdate ();
879+ this ->ScheduleDBSave ();
880+ }
826881 continue ;
827882 }
828883
829884 delete recp;
885+ this ->ScheduleDBSave ();
830886 }
831887}
832888
@@ -886,7 +942,10 @@ void ChanFixCore::AutoFixTick()
886942 }
887943
888944 if (dirty)
945+ {
889946 rec.QueueUpdate ();
947+ this ->ScheduleDBSave ();
948+ }
890949 }
891950}
892951
@@ -932,6 +991,7 @@ bool ChanFixCore::RequestFix(CommandSource& source, const Anope::string& chname)
932991 rec.fix_requested = true ;
933992 rec.fix_started = 0 ;
934993 rec.QueueUpdate ();
994+ this ->ScheduleDBSave ();
935995 source.Reply (" Fix request acknowledged for %s." , chname.c_str ());
936996 return true ;
937997}
@@ -991,6 +1051,7 @@ bool ChanFixCore::RequestFixFromChanServ(CommandSource& source, const Anope::str
9911051 rec.fix_requested = true ;
9921052 rec.fix_started = 0 ;
9931053 rec.QueueUpdate ();
1054+ this ->ScheduleDBSave ();
9941055 source.Reply (" Fix request acknowledged for %s." , chname.c_str ());
9951056 return true ;
9961057}
@@ -1019,6 +1080,7 @@ bool ChanFixCore::SetMark(CommandSource& source, const Anope::string& chname, bo
10191080 rec.mark_reason = reason;
10201081 rec.mark_time = Anope::CurTime;
10211082 rec.QueueUpdate ();
1083+ this ->ScheduleDBSave ();
10221084 source.Reply (" %s is now marked." , chname.c_str ());
10231085 return true ;
10241086 }
@@ -1028,6 +1090,7 @@ bool ChanFixCore::SetMark(CommandSource& source, const Anope::string& chname, bo
10281090 rec.mark_reason .clear ();
10291091 rec.mark_time = 0 ;
10301092 rec.QueueUpdate ();
1093+ this ->ScheduleDBSave ();
10311094 source.Reply (" %s is now unmarked." , chname.c_str ());
10321095 return true ;
10331096}
@@ -1056,6 +1119,7 @@ bool ChanFixCore::SetNoFix(CommandSource& source, const Anope::string& chname, b
10561119 rec.nofix_reason = reason;
10571120 rec.nofix_time = Anope::CurTime;
10581121 rec.QueueUpdate ();
1122+ this ->ScheduleDBSave ();
10591123 source.Reply (" %s is now set to NOFIX." , chname.c_str ());
10601124 return true ;
10611125 }
@@ -1065,6 +1129,7 @@ bool ChanFixCore::SetNoFix(CommandSource& source, const Anope::string& chname, b
10651129 rec.nofix_reason .clear ();
10661130 rec.nofix_time = 0 ;
10671131 rec.QueueUpdate ();
1132+ this ->ScheduleDBSave ();
10681133 source.Reply (" %s is no longer set to NOFIX." , chname.c_str ());
10691134 return true ;
10701135}
0 commit comments