-
Notifications
You must be signed in to change notification settings - Fork 28
Sync progress status #260
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Sync progress status #260
Changes from 6 commits
Commits
Show all changes
22 commits
Select commit
Hold shift + click to select a range
83e90ee
Add sync download progress interfaces
simolus3 27623d9
Start tracking download operations
simolus3 bd01bfd
Serialize download progress over worker
simolus3 cadd682
Increment counters when receiving sync lines
simolus3 720a4e9
Adopt progress api in demo
simolus3 9d45ba3
Extract into record
simolus3 6e66936
Start migrating to new model
simolus3 8a3dded
Track operation counters from previous syncs
simolus3 a2322df
Add tests
simolus3 0ecd906
Set count_at_last in SDK
simolus3 86623f4
Improve docs
simolus3 2be5919
Send partial checkpoint in sync test
simolus3 c9d7755
Fix hasSynced briefly becoming false
simolus3 ccf67ef
fix typo
simolus3 6c7bae4
Adopt in django todolist demo too
simolus3 8271c2a
Simplify API
simolus3 740fc8d
Improve docs on downloaded fraction
simolus3 9f673bf
Merge branch 'main' into sync-progress
simolus3 c63e500
Use relative import
simolus3 ec5bfad
Fix guard sync widget for django demo
simolus3 3a5fd37
Merge branch 'main' into sync-progress
simolus3 e43e963
Merge branch 'main' into sync-progress
simolus3 File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,55 @@ | ||
import 'package:flutter/material.dart'; | ||
import 'package:powersync/powersync.dart' hide Column; | ||
import 'package:powersync_flutter_demo/powersync.dart'; | ||
|
||
/// A widget that shows [child] after a complete sync on the database has | ||
/// completed and a progress bar before that. | ||
class GuardBySync extends StatelessWidget { | ||
final Widget child; | ||
|
||
/// When set, wait only for a complete sync within the [BucketPriority] | ||
/// instead of a full sync. | ||
final BucketPriority? priority; | ||
|
||
const GuardBySync({ | ||
super.key, | ||
required this.child, | ||
this.priority, | ||
}); | ||
|
||
@override | ||
Widget build(BuildContext context) { | ||
return StreamBuilder<SyncStatus>( | ||
stream: db.statusStream, | ||
initialData: db.currentStatus, | ||
builder: (context, snapshot) { | ||
final status = snapshot.requireData; | ||
final (didSync, progress) = switch (priority) { | ||
null => ( | ||
status.hasSynced ?? false, | ||
status.downloadProgress?.untilCompletion | ||
), | ||
var priority? => ( | ||
status.statusForPriority(priority).hasSynced ?? false, | ||
status.downloadProgress?.untilPriority(priority) | ||
), | ||
}; | ||
|
||
if (didSync) { | ||
return child; | ||
} else { | ||
return Center( | ||
child: Column( | ||
children: [ | ||
const Text('Busy with sync...'), | ||
LinearProgressIndicator(value: progress?.fraction), | ||
if (progress case final progress?) | ||
Text('${progress.completed} out of ${progress.total}') | ||
], | ||
), | ||
); | ||
} | ||
}, | ||
); | ||
} | ||
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
124 changes: 124 additions & 0 deletions
124
packages/powersync_core/lib/src/sync/mutable_sync_status.dart
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,124 @@ | ||
import 'dart:async'; | ||
|
||
import 'package:collection/collection.dart'; | ||
|
||
import '../sync_status.dart'; | ||
import 'protocol.dart'; | ||
|
||
final class MutableSyncStatus { | ||
bool connected = false; | ||
bool connecting = false; | ||
bool downloading = false; | ||
bool uploading = false; | ||
|
||
InternalSyncDownloadProgress? downloadProgress; | ||
List<SyncPriorityStatus> priorityStatusEntries = const []; | ||
|
||
DateTime? lastSyncedAt; | ||
|
||
Object? uploadError; | ||
Object? downloadError; | ||
|
||
void setConnectingIfNotConnected() { | ||
if (!connected) { | ||
connecting = true; | ||
} | ||
} | ||
|
||
void setConnected() { | ||
connected = true; | ||
connecting = false; | ||
} | ||
|
||
void applyDownloadError(Object error) { | ||
connected = false; | ||
connecting = false; | ||
downloading = false; | ||
downloadProgress = null; | ||
downloadError = error; | ||
} | ||
|
||
void applyCheckpointReached(Checkpoint applied) { | ||
downloading = false; | ||
downloadError = null; | ||
final now = lastSyncedAt = DateTime.now(); | ||
priorityStatusEntries = [ | ||
if (applied.checksums.isNotEmpty) | ||
( | ||
hasSynced: true, | ||
lastSyncedAt: now, | ||
priority: maxBy( | ||
applied.checksums.map((cs) => BucketPriority(cs.priority)), | ||
(priority) => priority, | ||
compare: BucketPriority.comparator, | ||
)!, | ||
) | ||
]; | ||
} | ||
|
||
void applyCheckpointStarted(Checkpoint target) { | ||
downloading = true; | ||
// TODO: Include pending ops from interrupted download, if any... | ||
downloadProgress = InternalSyncDownloadProgress.fromZero(target); | ||
} | ||
|
||
void applyUploadError(Object error) { | ||
uploading = false; | ||
uploadError = error; | ||
} | ||
|
||
void applyBatchReceived( | ||
Map<String, BucketDescription?> currentBuckets, SyncDataBatch batch) { | ||
downloading = true; | ||
if (downloadProgress case final previousProgress?) { | ||
downloadProgress = previousProgress.incrementDownloaded([ | ||
for (final bucket in batch.buckets) | ||
if (currentBuckets[bucket.bucket] case final knownBucket?) | ||
(BucketPriority(knownBucket.priority), bucket.data.length), | ||
]); | ||
} | ||
} | ||
|
||
SyncStatus immutableSnapsot() { | ||
return SyncStatus( | ||
connected: connected, | ||
connecting: connecting, | ||
downloading: downloading, | ||
uploading: uploading, | ||
downloadProgress: downloadProgress?.asSyncDownloadProgress, | ||
priorityStatusEntries: UnmodifiableListView(priorityStatusEntries), | ||
lastSyncedAt: lastSyncedAt, | ||
hasSynced: lastSyncedAt != null, | ||
uploadError: uploadError, | ||
downloadError: downloadError, | ||
); | ||
} | ||
} | ||
|
||
final class SyncStatusStateStream { | ||
final MutableSyncStatus status = MutableSyncStatus(); | ||
SyncStatus _lastPublishedStatus = const SyncStatus(); | ||
|
||
final StreamController<SyncStatus> _statusStreamController = | ||
StreamController<SyncStatus>.broadcast(); | ||
|
||
Stream<SyncStatus> get statusStream => _statusStreamController.stream; | ||
|
||
void updateStatus(void Function(MutableSyncStatus status) change) { | ||
change(status); | ||
|
||
if (_statusStreamController.isClosed) { | ||
return; | ||
} | ||
|
||
final current = status.immutableSnapsot(); | ||
if (current != _lastPublishedStatus) { | ||
_statusStreamController.add(current); | ||
_lastPublishedStatus = current; | ||
} | ||
} | ||
|
||
void close() { | ||
_statusStreamController.close(); | ||
} | ||
} |
2 changes: 1 addition & 1 deletion
2
...es/powersync_core/lib/src/sync_types.dart → ...powersync_core/lib/src/sync/protocol.dart
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
File renamed without changes.
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.