From b8b977b35ef3e8ad05a84ef1f230f3aa1449b11f Mon Sep 17 00:00:00 2001 From: Gustavo Carreno Date: Mon, 9 Dec 2024 10:35:08 +0000 Subject: [PATCH] fix: Race condition for database --- pasirclogbot.example.config | 6 +- src/bot/irclogbot.bot.pas | 4 +- src/database/irclogbot.database.pas | 109 +++++++++++++++++----------- 3 files changed, 70 insertions(+), 49 deletions(-) diff --git a/pasirclogbot.example.config b/pasirclogbot.example.config index 761632c..6cf1291 100644 --- a/pasirclogbot.example.config +++ b/pasirclogbot.example.config @@ -1,9 +1,9 @@ [IRC] -Host="irc.libera.chat" +Host="localhost" Port=6667 NickName="[PasLogBot]" UserName="paslogbot" RealName="IRC channel #pascal log bot" -Channel="#PasLogBot" +Channel="#pascal" [DB] -File="paslogbot.db" \ No newline at end of file +File="paslogbot.db" diff --git a/src/bot/irclogbot.bot.pas b/src/bot/irclogbot.bot.pas index 03d063a..aed0e84 100644 --- a/src/bot/irclogbot.bot.pas +++ b/src/bot/irclogbot.bot.pas @@ -63,7 +63,7 @@ TReplayThread = class(TTHread) protected procedure Execute; override; public - constructor Create(const AIRC: TIdIRC; const ATarget: String; + constructor Create(AIRC: TIdIRC; const ATarget: String; const ALines: TStringList); published end; @@ -105,7 +105,7 @@ procedure TReplayThread.Execute; end; end; -constructor TReplayThread.Create(const AIRC: TIdIRC; const ATarget: String; +constructor TReplayThread.Create(AIRC: TIdIRC; const ATarget: String; const ALines: TStringList); begin inherited Create(True); diff --git a/src/database/irclogbot.database.pas b/src/database/irclogbot.database.pas index 674f89c..175d49c 100644 --- a/src/database/irclogbot.database.pas +++ b/src/database/irclogbot.database.pas @@ -7,6 +7,7 @@ interface uses Classes , SysUtils +, SyncObjs , SQLite3Conn , SQLDB , IRCLogBot.Common @@ -16,6 +17,7 @@ interface { TDatabase } TDatabase = class(TObject) private + FCriticalSection: TCriticalSection; FConnection: TSQLite3Connection; FTransaction: TSQLTransaction; FQuery: TSQLQuery; @@ -60,67 +62,85 @@ procedure TDatabase.SetupTables; procedure TDatabase.Insert(ANickName, AChannel, AMessage: String); begin + FCriticalSection.Acquire; try - debug('Starting Transaction...'); - FTransaction.StartTransaction; - debug('Inserting: <%s> [%s] "%s"', [ANickName, AChannel, AMessage]); - FConnection.ExecuteDirect(Format( - 'INSERT INTO logs (nick, channel, message) VALUES (%s, %s, %s)', - [QuotedStr(ANickName), QuotedStr(AChannel), QuotedStr(AMessage)])); - FTransaction.Commit; - debug('Transaction committed.'); - except - on e:Exception do - begin - FTransaction.Rollback; - debug('Error inserting message: %s', [e.Message]); + try + debug('Starting Transaction...'); + FTransaction.StartTransaction; + try + debug('Inserting: <%s> [%s] "%s"', [ANickName, AChannel, AMessage]); + FConnection.ExecuteDirect(Format( + 'INSERT INTO logs (nick, channel, message) VALUES (%s, %s, %s)', + [QuotedStr(ANickName), QuotedStr(AChannel), QuotedStr(AMessage)])); + finally + FTransaction.Commit; + debug('Transaction committed.'); + end; + except + on e:Exception do + begin + FTransaction.Rollback; + debug('Error inserting message: %s', [e.Message]); + end; end; + finally + FCriticalSection.Release; end; end; function TDatabase.Get(ACount: Integer): TStringList; begin Result:= TStringList.Create; + FCriticalSection.Acquire; try debug('Starting transaction.'); FTransaction.StartTransaction; - FQuery.SQL.Text:= Format( - 'SELECT timestamp, nick, channel, message FROM logs ORDER BY id DESC LIMIT %d', - [ACount] - ); - FQuery.Open; - if FQuery.RecordCount > 0 then - begin - FQuery.First; - repeat - debug('Retrieving: %s [%s] %s: %s', [ - FQuery.FieldByName('timestamp').AsString, - FQuery.FieldByName('channel').AsString, - FQuery.FieldByName('nick').AsString, - FQuery.FieldByName('message').AsString - ]); - Result.Insert(0, Format('%s [%s] %s: %s',[ - FQuery.FieldByName('timestamp').AsString, - FQuery.FieldByName('channel').AsString, - FQuery.FieldByName('nick').AsString, - FQuery.FieldByName('message').AsString - ])); - FQuery.Next; - until FQuery.EOF; - FQuery.Close; - FTransaction.EndTransaction; - debug('Transaction ended.'); - end; - except - on e:Exception do - begin - debug('Error retrieving lines: %s', [e.Message]); + try + try + FQuery.SQL.Text:= Format( + 'SELECT timestamp, nick, channel, message FROM logs ORDER BY id DESC LIMIT %d', + [ACount] + ); + FQuery.Open; + if FQuery.RecordCount > 0 then + begin + FQuery.First; + repeat + debug('Retrieving: %s [%s] %s: %s', [ + FQuery.FieldByName('timestamp').AsString, + FQuery.FieldByName('channel').AsString, + FQuery.FieldByName('nick').AsString, + FQuery.FieldByName('message').AsString + ]); + Result.Insert(0, Format('%s [%s] %s: %s',[ + FQuery.FieldByName('timestamp').AsString, + FQuery.FieldByName('channel').AsString, + FQuery.FieldByName('nick').AsString, + FQuery.FieldByName('message').AsString + ])); + FQuery.Next; + until FQuery.EOF; + FQuery.Close; + end; + finally + FTransaction.EndTransaction; + debug('Transaction ended.'); + end; + except + on e:Exception do + begin + FTransaction.Rollback; + debug('Error retrieving lines: %s', [e.Message]); + end; end; + finally + FCriticalSection.Release; end; end; constructor TDatabase.Create(ADatabaseFile: String); begin + FCriticalSection:= TCriticalSection.Create; FConnection:= TSQLite3Connection.Create(nil); FTransaction:= TSQLTransaction.Create(FConnection); FConnection.Transaction:= FTransaction; @@ -133,6 +153,7 @@ constructor TDatabase.Create(ADatabaseFile: String); destructor TDatabase.Destroy; begin + FCriticalSection.Free; FQuery.Free; FTransaction.Free; FConnection.Free;