Skip to content

Commit c9b7fb3

Browse files
authored
Merge pull request #46 from powersync-ja/fixes
Minor Fixes
2 parents 6792beb + bfaec2a commit c9b7fb3

File tree

3 files changed

+61
-14
lines changed

3 files changed

+61
-14
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',

packages/powersync/test/bucket_storage_test.dart

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -675,7 +675,7 @@ void main() {
675675
]));
676676
});
677677

678-
test('should revert a failing update', () async {
678+
test('should revert a failing insert', () async {
679679
await bucketStorage.saveSyncData(SyncDataBatch([
680680
SyncBucketData(
681681
bucket: 'bucket1',
@@ -688,7 +688,7 @@ void main() {
688688
writeCheckpoint: '3',
689689
checksums: [BucketChecksum(bucket: 'bucket1', checksum: 6)]));
690690

691-
// Local save
691+
// Local insert, later rejected by server
692692
db.execute('INSERT INTO assets(id, description) VALUES(?, ?)',
693693
['O3', 'inserted']);
694694
final batch = bucketStorage.getCrudBatch();
@@ -725,7 +725,7 @@ void main() {
725725
writeCheckpoint: '3',
726726
checksums: [BucketChecksum(bucket: 'bucket1', checksum: 6)]));
727727

728-
// Local save
728+
// Local delete, later rejected by server
729729
db.execute('DELETE FROM assets WHERE id = ?', ['O2']);
730730

731731
expect(db.select('SELECT description FROM assets WHERE id = \'O2\''),
@@ -750,7 +750,7 @@ void main() {
750750
]));
751751
});
752752

753-
test('should revert a failing insert', () async {
753+
test('should revert a failing update', () async {
754754
await bucketStorage.saveSyncData(SyncDataBatch([
755755
SyncBucketData(
756756
bucket: 'bucket1',
@@ -763,11 +763,15 @@ void main() {
763763
writeCheckpoint: '3',
764764
checksums: [BucketChecksum(bucket: 'bucket1', checksum: 6)]));
765765

766-
// Local save
767-
db.execute('DELETE FROM assets WHERE id = ?', ['O2']);
766+
// Local update, later rejected by server
767+
db.execute(
768+
'UPDATE assets SET description = ? WHERE id = ?', ['updated', 'O2']);
768769

769-
expect(db.select('SELECT description FROM assets WHERE id = \'O2\''),
770-
equals([]));
770+
expect(
771+
db.select('SELECT description FROM assets WHERE id = \'O2\''),
772+
equals([
773+
{'description': 'updated'}
774+
]));
771775
// Simulate a permissions error when uploading - data should be preserved.
772776
final batch = bucketStorage.getCrudBatch();
773777
await batch!.complete();

packages/powersync/test/offline_online_test.dart

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import 'dart:convert';
2+
13
import 'package:powersync/powersync.dart';
24
import 'package:test/test.dart';
35

@@ -113,16 +115,27 @@ void main() {
113115
await tx.execute('DELETE FROM local_assets');
114116
});
115117

118+
final crud = (await db.getAll('SELECT data FROM ps_crud ORDER BY id'))
119+
.map((d) => jsonDecode(d['data']))
120+
.toList();
116121
expect(
117-
await db.getAll('SELECT data FROM ps_crud ORDER BY id'),
122+
crud,
118123
equals([
119124
{
120-
'data':
121-
'{"op":"PUT","type":"customers","id":"$customerId","data":{"name":"test customer","email":"test@example.org"}}'
125+
"op": "PUT",
126+
"type": "customers",
127+
"id": customerId,
128+
"data": {"email": "test@example.org", "name": "test customer"}
122129
},
123130
{
124-
'data':
125-
'{"op":"PUT","type":"assets","id":"$assetId","data":{"user_id":"$userId","customer_id":"$customerId","description":"test."}}'
131+
"op": "PUT",
132+
"type": "assets",
133+
"id": assetId,
134+
"data": {
135+
"user_id": userId,
136+
"customer_id": customerId,
137+
"description": "test."
138+
}
126139
}
127140
]));
128141
});

0 commit comments

Comments
 (0)