@@ -735,7 +735,31 @@ public function execute_sqlite_query( string $sql, array $params = array() ): PD
735735 */
736736 public function begin_transaction (): void {
737737 if ( 0 === $ this ->transaction_level ) {
738- $ this ->execute_sqlite_query ( 'BEGIN ' );
738+ /*
739+ * When we're executing a statement that will write to the database,
740+ * we need to use "BEGIN IMMEDIATE" to open a write transaction.
741+ *
742+ * This is needed to avoid the "database is locked" error (SQLITE_BUSY)
743+ * when SQLite can't upgrade a read transaction to a write transaction,
744+ * because another connection is modifying the database.
745+ *
746+ * From the SQLite documentation:
747+ *
748+ * If a write statement occurs while a read transaction is active,
749+ * then the read transaction is upgraded to a write transaction if
750+ * possible. If some other database connection has already modified
751+ * the database or is already in the process of modifying the database,
752+ * then upgrading to a write transaction is not possible and the write
753+ * statement will fail with SQLITE_BUSY.
754+ *
755+ * See:
756+ * - https://www.sqlite.org/lang_transaction.html
757+ * - https://www.sqlite.org/rescode.html#busy
758+ *
759+ * For better performance, we could also consider opening the write
760+ * transaction later in the session - just before the first write.
761+ */
762+ $ this ->execute_sqlite_query ( $ this ->is_readonly ? 'BEGIN ' : 'BEGIN IMMEDIATE ' );
739763 } else {
740764 $ this ->execute_sqlite_query ( 'SAVEPOINT LEVEL ' . $ this ->transaction_level );
741765 }
0 commit comments