Skip to content

Commit e94b5cb

Browse files
authored
Lock MDSQLiteConnection._updateBuffer before accessing it (#76)
1 parent 62e6af1 commit e94b5cb

2 files changed

Lines changed: 22 additions & 9 deletions

File tree

PowerSync/PowerSync.Common/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
- **Breaking:** Made `Table.Name` non-nullable (default ""). This change may affect 0% of users, but it is technically a breaking change.
66
- Add support for loading custom SQLite extensions via `MDSQLiteOptions.Extensions`.
77
- Fix streaming sync retry loop reconnecting with no delay after an exception, ignoring `RetryDelayMs`.
8+
- Lock `MDSQLiteConnection._updateBuffer` to fix a rare issue with high-frequency local writes while syncing.
89
- (internal) Remove `Compiled*` classes in favor of working with `Table` and `Schema` objects directly.
910

1011
## 0.1.2

PowerSync/PowerSync.Common/MDSQLite/MDSQLiteConnection.cs

Lines changed: 21 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -21,19 +21,23 @@ public class MDSQLiteConnectionOptions(SqliteConnection database)
2121
public class MDSQLiteConnection : EventStream<DBAdapterEvents.TablesUpdatedEvent>, ILockContext
2222
{
2323
public SqliteConnection Db;
24-
private readonly List<UpdateNotification> updateBuffer;
24+
private readonly List<UpdateNotification> _updateBuffer;
25+
private readonly object _updateBufferLock = new();
2526
public MDSQLiteConnection(MDSQLiteConnectionOptions options)
2627
{
2728
Db = options.Database;
28-
updateBuffer = [];
29+
_updateBuffer = [];
2930

3031
raw.sqlite3_rollback_hook(Db.Handle, RollbackHook, IntPtr.Zero);
3132
raw.sqlite3_update_hook(Db.Handle, UpdateHook, IntPtr.Zero);
3233
}
3334

3435
private void RollbackHook(object user_data)
3536
{
36-
updateBuffer.Clear();
37+
lock (_updateBufferLock)
38+
{
39+
_updateBuffer.Clear();
40+
}
3741
}
3842

3943
private void UpdateHook(object user_data, int type, utf8z database, utf8z table, long rowId)
@@ -45,17 +49,26 @@ private void UpdateHook(object user_data, int type, utf8z database, utf8z table,
4549
23 => RowUpdateType.SQLITE_UPDATE,
4650
_ => throw new InvalidOperationException($"Unknown update type: {type}"),
4751
};
48-
updateBuffer.Add(new UpdateNotification(table.utf8_to_string(), opType, rowId));
52+
lock (_updateBufferLock)
53+
{
54+
_updateBuffer.Add(new UpdateNotification(table.utf8_to_string(), opType, rowId));
55+
}
4956
}
5057

5158
public void FlushUpdates()
5259
{
53-
if (updateBuffer.Count == 0)
60+
UpdateNotification[] updates;
61+
lock (_updateBufferLock)
5462
{
55-
return;
63+
if (_updateBuffer.Count == 0)
64+
{
65+
return;
66+
}
67+
updates = _updateBuffer.ToArray();
68+
_updateBuffer.Clear();
5669
}
5770

58-
var groupedUpdates = updateBuffer
71+
var groupedUpdates = updates
5972
.GroupBy(update => update.Table)
6073
.ToDictionary(
6174
group => group.Key,
@@ -65,11 +78,10 @@ public void FlushUpdates()
6578
var batchedUpdate = new BatchedUpdateNotification
6679
{
6780
GroupedUpdates = groupedUpdates,
68-
RawUpdates = updateBuffer.ToArray(),
81+
RawUpdates = updates.ToArray(),
6982
Tables = groupedUpdates.Keys.ToArray()
7083
};
7184

72-
updateBuffer.Clear();
7385
Emit(new DBAdapterEvents.TablesUpdatedEvent(batchedUpdate));
7486
}
7587

0 commit comments

Comments
 (0)