Skip to content

Commit cf050e9

Browse files
committed
Add sync progress APIs
1 parent 33e6051 commit cf050e9

File tree

4 files changed

+106
-5
lines changed

4 files changed

+106
-5
lines changed

packages/common/src/client/AbstractPowerSyncDatabase.ts

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ import {
3232
type PowerSyncConnectionOptions,
3333
type RequiredAdditionalConnectionOptions
3434
} from './sync/stream/AbstractStreamingSyncImplementation.js';
35+
import { FULL_SYNC_PRIORITY } from 'src/db/crud/SyncProgress.js';
3536

3637
export interface DisconnectAndClearOptions {
3738
/** When set to false, data in local-only tables is preserved. */
@@ -146,11 +147,6 @@ export const isPowerSyncDatabaseOptionsWithSettings = (test: any): test is Power
146147
return typeof test == 'object' && isSQLOpenOptions(test.database);
147148
};
148149

149-
/**
150-
* The priority used by the core extension to indicate that a full sync was completed.
151-
*/
152-
const FULL_SYNC_PRIORITY = 2147483647;
153-
154150
export abstract class AbstractPowerSyncDatabase extends BaseObserver<PowerSyncDBListener> {
155151
/**
156152
* Transactions should be queued in the DBAdapter, but we also want to prevent
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
import type { SyncStatus } from "./SyncStatus.js";
2+
3+
// (bucket, progress) pairs
4+
export type InternalProgressInformation = Record<string, {
5+
priority: number, // Priority of the associated buckets
6+
atLast: number, // Total ops at last completed sync, or 0
7+
sinceLast: number, // Total ops _since_ the last completed sync.
8+
targetCount: number, // Total opcount for next checkpoint as indicated by service.
9+
}>;
10+
11+
/**
12+
* The priority used by the core extension to indicate that a full sync was completed.
13+
*/
14+
export const FULL_SYNC_PRIORITY = 2147483647;
15+
16+
/**
17+
* Information about a progressing download made by the PowerSync SDK.
18+
*
19+
* To obtain these values, use {@link SyncProgress}, available through
20+
* {@link SyncStatus#downloadProgress}.
21+
*/
22+
export interface ProgressWithOperations {
23+
/**
24+
* The total amount of operations to download for the current sync iteration
25+
* to complete.
26+
*/
27+
total: number;
28+
/**
29+
* The amount of operations that have already been downloaded.
30+
*/
31+
completed: number;
32+
33+
/**
34+
* Relative progress, as {@link completed} of {@link total}. This will be a number
35+
* between `0.0` and `1.0`.
36+
*/
37+
fraction: number;
38+
};
39+
40+
/**
41+
* Provides realtime progress on how PowerSync is downloading rows.
42+
*
43+
* The reported progress always reflects the status towards th end of a sync iteration (after
44+
* which a consistent snapshot of all buckets is available locally).
45+
*
46+
* In rare cases (in particular, when a [compacting](https://docs.powersync.com/usage/lifecycle-maintenance/compacting-buckets)
47+
* operation takes place between syncs), it's possible for the returned numbers to be slightly
48+
* inaccurate. For this reason, {@link SyncProgress} should be seen as an approximation of progress.
49+
* The information returned is good enough to build progress bars, but not exact enough to track
50+
* individual download counts.
51+
*
52+
* Also note that data is downloaded in bulk, which means that individual counters are unlikely
53+
* to be updated one-by-one.
54+
*/
55+
export class SyncProgress {
56+
constructor(protected internal: InternalProgressInformation) {}
57+
58+
get untilCompletion(): ProgressWithOperations {
59+
return this.untilPriority(FULL_SYNC_PRIORITY);
60+
}
61+
62+
untilPriority(priority: number): ProgressWithOperations {
63+
let total = 0;
64+
let downloaded = 0;
65+
66+
for (const progress of Object.values(this.internal)) {
67+
// Include higher-priority buckets, which are represented by lower numbers.
68+
if (progress.priority <= priority) {
69+
downloaded += progress.sinceLast;
70+
total += progress.targetCount - progress.atLast;
71+
}
72+
}
73+
74+
let progress = total == 0 ? 0.0 : downloaded / total;
75+
return {
76+
total,
77+
completed: downloaded,
78+
fraction: progress,
79+
}
80+
}
81+
}

packages/common/src/db/crud/SyncStatus.ts

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,14 @@
1+
import { InternalProgressInformation, SyncProgress } from "./SyncProgress.js";
2+
13
export type SyncDataFlowStatus = Partial<{
24
downloading: boolean;
35
uploading: boolean;
6+
/**
7+
* Internal information about how far we are downloading operations in buckets.
8+
*
9+
* Please use the {@link SyncStatus#downloadProgress} property to track sync progress.
10+
*/
11+
downloadProgress: InternalProgressInformation,
412
}>;
513

614
export interface SyncPriorityStatus {
@@ -74,6 +82,21 @@ export class SyncStatus {
7482
return (this.options.priorityStatusEntries ?? []).slice().sort(SyncStatus.comparePriorities);
7583
}
7684

85+
/**
86+
* A realtime progress report on how many operations have been downloaded and
87+
* how many are necessary in total to complete the next sync iteration.
88+
*
89+
* This field is only set when {@link SyncDataFlowStatus#downloading} is also true.
90+
*/
91+
get downloadProgress(): SyncProgress | null {
92+
const internalProgress = this.options.dataFlow?.downloadProgress;
93+
if (internalProgress == null) {
94+
return null;
95+
}
96+
97+
return new SyncProgress(internalProgress);
98+
}
99+
77100
/**
78101
* Reports a pair of {@link SyncStatus#hasSynced} and {@link SyncStatus#lastSyncedAt} fields that apply
79102
* to a specific bucket priority instead of the entire sync operation.

packages/common/src/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ export * from './client/sync/stream/AbstractStreamingSyncImplementation.js';
1919
export * from './client/sync/stream/streaming-sync-types.js';
2020
export { MAX_OP_ID } from './client/constants.js';
2121

22+
export { ProgressWithOperations, SyncProgress } from './db/crud/SyncProgress.js';
2223
export * from './db/crud/SyncStatus.js';
2324
export * from './db/crud/UploadQueueStatus.js';
2425
export * from './db/schema/Schema.js';

0 commit comments

Comments
 (0)