Skip to content

Commit 83e90ee

Browse files
committed
Add sync download progress interfaces
1 parent 58098fb commit 83e90ee

File tree

2 files changed

+90
-1
lines changed

2 files changed

+90
-1
lines changed

packages/powersync_core/lib/powersync_core.dart

+2-1
Original file line numberDiff line numberDiff line change
@@ -10,5 +10,6 @@ export 'src/exceptions.dart';
1010
export 'src/log.dart';
1111
export 'src/open_factory.dart';
1212
export 'src/schema.dart';
13-
export 'src/sync_status.dart';
13+
export 'src/sync_status.dart'
14+
hide InternalSyncDownloadProgress, OperationCounter;
1415
export 'src/uuid.dart';

packages/powersync_core/lib/src/sync_status.dart

+88
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import 'package:collection/collection.dart';
2+
import 'package:meta/meta.dart';
23

34
final class SyncStatus {
45
/// true if currently connected.
@@ -18,6 +19,12 @@ final class SyncStatus {
1819
/// This is only true when [connected] is also true.
1920
final bool downloading;
2021

22+
/// A realtime progress report on how many operations have been downloaded and
23+
/// how many are necessary in total to complete the next sync iteration.
24+
///
25+
/// This field is only set when [downloading] is also true.
26+
final SyncDownloadProgress? downloadProgress;
27+
2128
/// true if uploading changes
2229
final bool uploading;
2330

@@ -47,6 +54,7 @@ final class SyncStatus {
4754
this.connecting = false,
4855
this.lastSyncedAt,
4956
this.hasSynced,
57+
this.downloadProgress,
5058
this.downloading = false,
5159
this.uploading = false,
5260
this.downloadError,
@@ -202,3 +210,83 @@ class UploadQueueStats {
202210
}
203211
}
204212
}
213+
214+
@internal
215+
typedef OperationCounter = ({BucketPriority priority, int opCount});
216+
217+
@internal
218+
final class InternalSyncDownloadProgress {
219+
final List<OperationCounter> downloaded;
220+
final List<OperationCounter> target;
221+
222+
final int _totalDownloaded;
223+
final int _totalTarget;
224+
225+
InternalSyncDownloadProgress(this.downloaded, this.target)
226+
: _totalDownloaded = downloaded.map((e) => e.opCount).sum,
227+
_totalTarget = target.map((e) => e.opCount).sum;
228+
229+
static int sumInPriority(
230+
List<OperationCounter> counters, BucketPriority priority) {
231+
return counters
232+
.where((e) => e.priority >= priority)
233+
.map((e) => e.opCount)
234+
.sum;
235+
}
236+
}
237+
238+
/// Provides realtime progress about how PowerSync is downloading rows.
239+
///
240+
/// The reported progress always reflects the status towards the end of a
241+
/// sync iteration (after which a consistent snapshot of all buckets is
242+
/// available locally). Note that [downloaded] starts at `0` every time an
243+
/// iteration begins.
244+
/// This has an effect when iterations are interrupted. Consider this flow
245+
/// as an example:
246+
///
247+
/// 1. The client comes online for the first time and has to synchronize a
248+
/// large amount of rows (say 100k). Here, [downloaded] starts at `0` and
249+
/// [total] would be the `100,000` rows.
250+
/// 2. The client makes some progress, so that [downloaded] is perhaps
251+
/// `60,000`.
252+
/// 3. The client briefly looses connectivity.
253+
/// 4. Back online, a new sync iteration starts. This means that [downloaded]
254+
/// is reset to `0`. However, since half of the target has already been
255+
/// downloaded in the earlier iteration, [total] is now set to `40,000` to
256+
/// reflect the remaining rows to download in the new iteration.
257+
extension type SyncDownloadProgress._(InternalSyncDownloadProgress _internal) {
258+
/// The amount of operations that have been downloaded in the current sync
259+
/// iteration.
260+
///
261+
/// This number always starts at zero as [SyncStatus.downloading] changes
262+
/// from `false` to `true`.
263+
int get downloaded => _internal._totalDownloaded;
264+
265+
/// The total amount of operations expected for this sync operation.
266+
int get total => _internal._totalTarget;
267+
268+
/// The fraction of [total] operations that have already been [downloaded], as
269+
/// a number between 0 and 1.
270+
double get progress => _internal._totalDownloaded / _internal._totalTarget;
271+
272+
int downloadedFor(BucketPriority priority) {
273+
return InternalSyncDownloadProgress.sumInPriority(
274+
_internal.downloaded, priority);
275+
}
276+
277+
int totalFor(BucketPriority priority) {
278+
return InternalSyncDownloadProgress.sumInPriority(
279+
_internal.target, priority);
280+
}
281+
282+
double progressFor(BucketPriority priority) {
283+
final downloaded = downloadedFor(priority);
284+
final total = totalFor(priority);
285+
286+
if (total == 0) {
287+
return 0;
288+
}
289+
290+
return downloaded / total;
291+
}
292+
}

0 commit comments

Comments
 (0)