Skip to content

Commit bfaec2a

Browse files
committed
Retry opening database if locked.
1 parent 75dae3c commit bfaec2a

File tree

1 file changed

+31
-1
lines changed

1 file changed

+31
-1
lines changed

packages/powersync/lib/src/open_factory.dart

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import 'dart:async';
22
import 'dart:convert';
33
import 'dart:io';
44
import 'dart:isolate';
5+
import 'dart:math';
56

67
import 'package:sqlite_async/sqlite3.dart' as sqlite;
78
import '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

Comments
 (0)