@@ -2,6 +2,7 @@ import 'dart:async';
22import 'dart:convert' ;
33import 'dart:io' ;
44import 'dart:isolate' ;
5+ import 'dart:math' ;
56
67import 'package:sqlite_async/sqlite3.dart' as sqlite;
78import 'package:sqlite_async/sqlite_async.dart' ;
@@ -39,12 +40,41 @@ class PowerSyncOpenFactory extends DefaultSqliteOpenFactory {
3940 sqlite.Database open (SqliteOpenOptions options) {
4041 // ignore: deprecated_member_use_from_same_package
4142 _sqliteSetup? .setup ();
42- final db = super . open (options);
43+ final db = _retriedOpen (options);
4344 db.execute ('PRAGMA recursive_triggers = TRUE' );
4445 setupFunctions (db);
4546 return db;
4647 }
4748
49+ /// When opening the powersync connection and the standard write connection
50+ /// at the same time, one could fail with this error:
51+ ///
52+ /// SqliteException(5): while opening the database, automatic extension loading failed: , database is locked (code 5)
53+ ///
54+ /// It happens before we have a chance to set the busy timeout, so we just
55+ /// retry opening the database.
56+ ///
57+ /// Usually a delay of 1-2ms is sufficient for the next try to succeed, but
58+ /// we increase the retry delay up to 16ms per retry, and a maximum of 500ms
59+ /// in total.
60+ sqlite.Database _retriedOpen (SqliteOpenOptions options) {
61+ final stopwatch = Stopwatch ()..start ();
62+ var retryDelay = 2 ;
63+ while (stopwatch.elapsedMilliseconds < 500 ) {
64+ try {
65+ return super .open (options);
66+ } catch (e) {
67+ if (e is sqlite.SqliteException && e.resultCode == 5 ) {
68+ sleep (Duration (milliseconds: retryDelay));
69+ retryDelay = min (retryDelay * 2 , 16 );
70+ continue ;
71+ }
72+ rethrow ;
73+ }
74+ }
75+ throw AssertionError ('Cannot reach this point' );
76+ }
77+
4878 void setupFunctions (sqlite.Database db) {
4979 db.createFunction (
5080 functionName: 'uuid' ,
0 commit comments