From 879e5b7c87b090906ca2f850bd54e25c18806579 Mon Sep 17 00:00:00 2001 From: LucDeCaf Date: Wed, 12 Aug 2026 11:39:45 +0200 Subject: [PATCH 1/4] Convert opIds to longs --- .../Client/PowerSyncDatabase.cs | 2 +- .../Client/Sync/Bucket/BucketStorageAdapter.cs | 4 ++-- .../Client/Sync/Bucket/SqliteBucketStorage.cs | 18 ++++++++---------- .../Sync/Stream/StreamingSyncImplementation.cs | 4 ++-- .../PowerSync.Common/DB/Crud/CrudBatch.cs | 4 ++-- .../DB/Crud/CrudTransaction.cs | 2 +- .../Client/Sync/StreamingSyncRetryTests.cs | 2 +- .../Sync/SyncIterationControlFlowTests.cs | 4 ++-- .../Utils/Sync/MockSyncService.cs | 2 +- 9 files changed, 20 insertions(+), 22 deletions(-) diff --git a/PowerSync/PowerSync.Common/Client/PowerSyncDatabase.cs b/PowerSync/PowerSync.Common/Client/PowerSyncDatabase.cs index 3e1e7fd1..b8cc8e87 100644 --- a/PowerSync/PowerSync.Common/Client/PowerSyncDatabase.cs +++ b/PowerSync/PowerSync.Common/Client/PowerSyncDatabase.cs @@ -669,7 +669,7 @@ public async Task GetUploadQueueStats(bool includeSize = false }); } - public Task HandleCrudCheckpoint(long lastClientId, string? writeCheckpoint = null) + public Task HandleCrudCheckpoint(long lastClientId, long? writeCheckpoint = null) { return BucketStorageAdapter.HandleCrudCheckpoint(lastClientId, writeCheckpoint); } diff --git a/PowerSync/PowerSync.Common/Client/Sync/Bucket/BucketStorageAdapter.cs b/PowerSync/PowerSync.Common/Client/Sync/Bucket/BucketStorageAdapter.cs index a02d1341..5a8e1c45 100644 --- a/PowerSync/PowerSync.Common/Client/Sync/Bucket/BucketStorageAdapter.cs +++ b/PowerSync/PowerSync.Common/Client/Sync/Bucket/BucketStorageAdapter.cs @@ -135,9 +135,9 @@ public interface IBucketStorageAdapter : ICloseable Task HasCrud(); Task GetCrudBatch(int limit = 100); - Task UpdateLocalTarget(Func> callback); + Task UpdateLocalTarget(Func> callback); - Task HandleCrudCheckpoint(long lastClientId, string? writeCheckpoint = null); + Task HandleCrudCheckpoint(long lastClientId, long? writeCheckpoint = null); /// /// Get a unique client ID. diff --git a/PowerSync/PowerSync.Common/Client/Sync/Bucket/SqliteBucketStorage.cs b/PowerSync/PowerSync.Common/Client/Sync/Bucket/SqliteBucketStorage.cs index 0a132c28..88235e7f 100644 --- a/PowerSync/PowerSync.Common/Client/Sync/Bucket/SqliteBucketStorage.cs +++ b/PowerSync/PowerSync.Common/Client/Sync/Bucket/SqliteBucketStorage.cs @@ -16,7 +16,7 @@ namespace PowerSync.Common.Client.Sync.Bucket; public class SqliteBucketStorage : IBucketStorageAdapter { - public static readonly string MAX_OP_ID = "9223372036854775807"; + public const long MAX_OP_ID = 9223372036854775807; public BucketStorageEvents Events { get; } = new(); @@ -72,12 +72,10 @@ public async Task GetClientId() /// Reads the stored target checkpoint request id, or updates it when the update parameter is set. /// /// The previous checkpoint request. - private static Task TargetCheckpointRequestId(ILockContext tx, string? update = null) + private static Task TargetCheckpointRequestId(ILockContext tx, long? update = null) { - // TODO Note that we are only casting in Dart/JS because this returns a 64-bit integer we can't natively represent there. - // Turning MAX_OP_ID into a 64-bit integer here and comparing ints would be better. - return tx.Get( - "SELECT CAST(powersync_control(?, ?) AS TEXT) AS r", + return tx.Get( + "SELECT powersync_control(?, ?) AS r", [PowerSyncControlCommand.TARGET_CHECKPOINT_REQUEST_ID, update]); } @@ -94,7 +92,7 @@ public class ResultDetail private record SequenceResult(long seq); - public async Task UpdateLocalTarget(Func> callback) + public async Task UpdateLocalTarget(Func> callback) { var seqBeforeResult = await db.ReadTransaction(async tx => { @@ -118,7 +116,7 @@ public async Task UpdateLocalTarget(Func> callback) return false; } - string opId = await callback(); + long opId = await callback(); logger.LogDebug("[updateLocalTarget] Updating target to checkpoint {message}", opId); @@ -154,7 +152,7 @@ public async Task UpdateLocalTarget(Func> callback) return true; }); } - public Task HandleCrudCheckpoint(long lastClientId, string? writeCheckpoint = null) + public Task HandleCrudCheckpoint(long lastClientId, long? writeCheckpoint = null) { return db.WriteTransaction(async tx => { @@ -165,7 +163,7 @@ public Task HandleCrudCheckpoint(long lastClientId, string? writeCheckpoint = nu await TargetCheckpointRequestId( tx, - !string.IsNullOrEmpty(writeCheckpoint) && !crudRemaining ? writeCheckpoint : MAX_OP_ID); + writeCheckpoint is not null && !crudRemaining ? writeCheckpoint : MAX_OP_ID); }); } diff --git a/PowerSync/PowerSync.Common/Client/Sync/Stream/StreamingSyncImplementation.cs b/PowerSync/PowerSync.Common/Client/Sync/Stream/StreamingSyncImplementation.cs index 1dbeeccd..350c99b0 100644 --- a/PowerSync/PowerSync.Common/Client/Sync/Stream/StreamingSyncImplementation.cs +++ b/PowerSync/PowerSync.Common/Client/Sync/Stream/StreamingSyncImplementation.cs @@ -804,13 +804,13 @@ public void Close() } public record ResponseData( - [property: JsonProperty("write_checkpoint")] string WriteCheckpoint + [property: JsonProperty("write_checkpoint")] long WriteCheckpoint ); public record ApiResponse( [property: JsonProperty("data")] ResponseData Data ); - public async Task GetWriteCheckpoint() + public async Task GetWriteCheckpoint() { var clientId = await Options.Adapter.GetClientId(); var path = $"/write-checkpoint2.json?client_id={clientId}"; diff --git a/PowerSync/PowerSync.Common/DB/Crud/CrudBatch.cs b/PowerSync/PowerSync.Common/DB/Crud/CrudBatch.cs index 60934a9e..fb6d99e1 100644 --- a/PowerSync/PowerSync.Common/DB/Crud/CrudBatch.cs +++ b/PowerSync/PowerSync.Common/DB/Crud/CrudBatch.cs @@ -3,13 +3,13 @@ namespace PowerSync.Common.DB.Crud; using System; using System.Threading.Tasks; -public class CrudBatch(CrudEntry[] Crud, bool HaveMore, Func CompleteCallback) +public class CrudBatch(CrudEntry[] Crud, bool HaveMore, Func CompleteCallback) { public CrudEntry[] Crud { get; private set; } = Crud; public bool HaveMore { get; private set; } = HaveMore; - public async Task Complete(string? checkpoint = null) + public async Task Complete(long? checkpoint = null) { await CompleteCallback(checkpoint); } diff --git a/PowerSync/PowerSync.Common/DB/Crud/CrudTransaction.cs b/PowerSync/PowerSync.Common/DB/Crud/CrudTransaction.cs index da63a14a..7a0ddce3 100644 --- a/PowerSync/PowerSync.Common/DB/Crud/CrudTransaction.cs +++ b/PowerSync/PowerSync.Common/DB/Crud/CrudTransaction.cs @@ -3,7 +3,7 @@ namespace PowerSync.Common.DB.Crud; using System; using System.Threading.Tasks; -public class CrudTransaction(CrudEntry[] crud, Func complete, long? transactionId = null) : CrudBatch(crud, false, complete) +public class CrudTransaction(CrudEntry[] crud, Func complete, long? transactionId = null) : CrudBatch(crud, false, complete) { public long? TransactionId { get; private set; } = transactionId; } diff --git a/Tests/PowerSync/PowerSync.Common.Tests/Client/Sync/StreamingSyncRetryTests.cs b/Tests/PowerSync/PowerSync.Common.Tests/Client/Sync/StreamingSyncRetryTests.cs index 659af874..69294c33 100644 --- a/Tests/PowerSync/PowerSync.Common.Tests/Client/Sync/StreamingSyncRetryTests.cs +++ b/Tests/PowerSync/PowerSync.Common.Tests/Client/Sync/StreamingSyncRetryTests.cs @@ -99,7 +99,7 @@ SemaphoreSlim signal public override Task Get(string path, Dictionary? headers = null) { var response = new StreamingSyncImplementation.ApiResponse( - new StreamingSyncImplementation.ResponseData("1") + new StreamingSyncImplementation.ResponseData(1) ); return Task.FromResult((T)(object)response); } diff --git a/Tests/PowerSync/PowerSync.Common.Tests/Client/Sync/SyncIterationControlFlowTests.cs b/Tests/PowerSync/PowerSync.Common.Tests/Client/Sync/SyncIterationControlFlowTests.cs index 94a00085..43c86914 100644 --- a/Tests/PowerSync/PowerSync.Common.Tests/Client/Sync/SyncIterationControlFlowTests.cs +++ b/Tests/PowerSync/PowerSync.Common.Tests/Client/Sync/SyncIterationControlFlowTests.cs @@ -255,8 +255,8 @@ public Task Control(string op, object? payload) public Task NextCrudItem() => Task.FromResult(null); public Task HasCrud() => Task.FromResult(false); public Task GetCrudBatch(int limit = 100) => Task.FromResult(null); - public Task UpdateLocalTarget(Func> callback) => Task.FromResult(false); - public Task HandleCrudCheckpoint(long lastClientId, string? writeCheckpoint = null) => Task.CompletedTask; + public Task UpdateLocalTarget(Func> callback) => Task.FromResult(false); + public Task HandleCrudCheckpoint(long lastClientId, long? writeCheckpoint = null) => Task.CompletedTask; public Task GetClientId() => Task.FromResult("test-client"); public void Close() { } } diff --git a/Tests/PowerSync/PowerSync.Common.Tests/Utils/Sync/MockSyncService.cs b/Tests/PowerSync/PowerSync.Common.Tests/Utils/Sync/MockSyncService.cs index 2169d1d3..e37baed0 100644 --- a/Tests/PowerSync/PowerSync.Common.Tests/Utils/Sync/MockSyncService.cs +++ b/Tests/PowerSync/PowerSync.Common.Tests/Utils/Sync/MockSyncService.cs @@ -187,7 +187,7 @@ public override Task PostStreamRaw(SyncStreamOptions options) public override Task Get(string path, Dictionary? headers = null) { var response = new StreamingSyncImplementation.ApiResponse( - new StreamingSyncImplementation.ResponseData("1") + new StreamingSyncImplementation.ResponseData(1) ); return Task.FromResult((T)(object)response); From fc740208aa664e445ef6f618b4ed38e9673604f3 Mon Sep 17 00:00:00 2001 From: LucDeCaf Date: Wed, 12 Aug 2026 11:46:58 +0200 Subject: [PATCH 2/4] Changelog --- PowerSync/PowerSync.Common/CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/PowerSync/PowerSync.Common/CHANGELOG.md b/PowerSync/PowerSync.Common/CHANGELOG.md index cd33192f..90ace1d9 100644 --- a/PowerSync/PowerSync.Common/CHANGELOG.md +++ b/PowerSync/PowerSync.Common/CHANGELOG.md @@ -3,6 +3,7 @@ ## 1.0.1 - Full release. +- Use `long` for opIds instead of `string`. ## 1.0.0 (unlisted) From 60217e121c9146fb75f49e607e1fb81e2772bcf0 Mon Sep 17 00:00:00 2001 From: LucDeCaf Date: Wed, 12 Aug 2026 11:59:57 +0200 Subject: [PATCH 3/4] Return raw name from OnChange --- PowerSync/PowerSync.Common/CHANGELOG.md | 1 + PowerSync/PowerSync.Common/Client/WatchManager.cs | 4 +--- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/PowerSync/PowerSync.Common/CHANGELOG.md b/PowerSync/PowerSync.Common/CHANGELOG.md index cd33192f..86b92e8b 100644 --- a/PowerSync/PowerSync.Common/CHANGELOG.md +++ b/PowerSync/PowerSync.Common/CHANGELOG.md @@ -3,6 +3,7 @@ ## 1.0.1 - Full release. +- `PowerSyncDatabase.OnChange` now returns the underlying raw table name instead of the view name to mirror PowerSync JS. ## 1.0.0 (unlisted) diff --git a/PowerSync/PowerSync.Common/Client/WatchManager.cs b/PowerSync/PowerSync.Common/Client/WatchManager.cs index 3deb382a..4df2210b 100644 --- a/PowerSync/PowerSync.Common/Client/WatchManager.cs +++ b/PowerSync/PowerSync.Common/Client/WatchManager.cs @@ -73,11 +73,9 @@ public IAsyncEnumerable OnChange(SQLWatchOptions? options) refreshOnSchemaChange: false ); - // TODO: powersync-js onChange returns table names in `ps_data__{table}` format. - // We should make a decision on whether or not to mirror that before v1. return Stream(subscription, changed => Task.FromResult(new WatchOnChangeEvent { - ChangedTables = [.. changed.Select(InternalToFriendlyTableName)] + ChangedTables = [.. changed] })); } From e2a6bed614a44185c387a8644d49b1bab88b88b6 Mon Sep 17 00:00:00 2001 From: LucDeCaf Date: Wed, 12 Aug 2026 15:38:34 +0200 Subject: [PATCH 4/4] Update changelog to reference API changes --- PowerSync/PowerSync.Common/CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/PowerSync/PowerSync.Common/CHANGELOG.md b/PowerSync/PowerSync.Common/CHANGELOG.md index ca0e917d..2d2e3edf 100644 --- a/PowerSync/PowerSync.Common/CHANGELOG.md +++ b/PowerSync/PowerSync.Common/CHANGELOG.md @@ -4,7 +4,7 @@ - Full release. - `PowerSyncDatabase.OnChange` now returns the underlying raw table name instead of the view name to mirror PowerSync JS. -- Use `long` for opIds instead of `string`. +- Use `long` for op IDs instead of `string`. This affects the types returned by some methods used in `PowerSyncBackendConnector.UploadData`, namely `PowerSyncDatabase.GetNextCrudTransaction()` and `PowerSyncDatabase.GetCrudBatch()`. ## 1.0.0 (unlisted)