signal-cli icon indicating copy to clipboard operation
signal-cli copied to clipboard

SQLite database is locked error

Open dukhaSlayer opened this issue 11 months ago • 5 comments

By default, SQLite doesn't support multiple connections. It can be enabled either with a shared cache or write-ahead log feature.

Default connection # for Hikari: DEFAULT_POOL_SIZE = 10, which under the load produces exceptions: org.sqlite.SQLiteException: [SQLITE_BUSY] The database file is locked (database is locked)

I'd advise doing

config.setMaximumPoolSize(1);

in org.asamk.signal.manager.storage.Database#getHikariDataSource or trying enabling WAL with PRAGMA (not sure it will solve the issue completely though, if two connections will try to write simultaneously).

More info: https://stackoverflow.com/questions/10325683/can-i-read-and-write-to-a-sqlite-database-concurrently-from-multiple-connections

dukhaSlayer avatar Jan 09 '25 19:01 dukhaSlayer

Also wanted to ask why it is using IMMEDIATE transaction mode? :)

        sqliteConfig.setTransactionMode(SQLiteConfig.TransactionMode.IMMEDIATE);

dukhaSlayer avatar Jan 09 '25 19:01 dukhaSlayer

Unfortunately setting max pool size to 1 makes things worse, or just reveals a bigger problem. Instead of randomly raising the exception, it just times out at first big query like key value store

Lately i am having a lot of SQLITE_BUSY exceptions in signal-cli, my application stops at least once a day because db was busy, and doesn't want to recover. I tried hiding all db communications in synchronized environment, didn't help, but i noticed that it usually times out at setting autocommit to false, trying to not use autocommit=false/commit pair didn't help either, it still breaks time after time

GooseBravo101 avatar Jan 14 '25 13:01 GooseBravo101

sqlite does support multiple read connections at the same time, though every write will block the database. Setting the pool size to 1 will prevent any parallel read connections. IMMEDIATE transaction mode is used so that explicitly started transactions are immediately started as write transactions. The sqlite default behavior would be to start transactions as readonly and upgrade them to write transactions when an update statement is executed, which can lead to unpredictable behavior if the upgrade fails. It times out at setting autocommit to false because that's when the explicit write transaction is started. Disabling that can lead to data corruption due to not using a transaction for updating data.

Enabling WAL might help here. I've enabled WAL mode in e11e0930209e330649af182eaa0317d2d987d4e8, can you try the latest master if it works better?

AsamK avatar Jan 14 '25 21:01 AsamK

Ok Now it just completely freezes without obvious exception Still receiving some envelopes, but not receiving messages. Eventually it starts spitting this error, and after that complete freeze Exception: org.signal.libsignal.protocol.DuplicateMessageException: message with old counter 1 / 0 (ProtocolDuplicateMessageException)

Note that i am running signal-cli in dbus daemon mode, maybe there is something broken there

GooseBravo101 avatar Jan 20 '25 08:01 GooseBravo101

So,

Setting the pool size to 1 will prevent any parallel read connections.

Not really. If we don't use SQLiteConfig.TransactionMode.IMMEDIATE - we might have multiple read connections.

which can lead to unpredictable behavior if the upgrade fails.

I'm not sure why the behavior is unpredictable. When the upgrade fails it will be as if the transaction didn't happen, i.e. data will stay the same. Maybe I'm missing something and you mean some Whisper protocol side effects?

Now, in this ticket https://github.com/AsamK/signal-cli/issues/1675 (sorry, I've created two of them, I just thought the problem is smaller in the beginning) I described the issue.

dukhaSlayer avatar Jan 22 '25 16:01 dukhaSlayer