feat: handle SyncResetV1 (#20732)

* feat: handle SyncResetV1

* auto retry on reset and handle SyncCompleteV1

* fix tests

---------

Co-authored-by: shenlong-tanwen <139912620+shalong-tanwen@users.noreply.github.com>
Co-authored-by: Alex <alex.tran1502@gmail.com>
This commit is contained in:
shenlong 2025-09-08 20:18:26 +05:30 committed by GitHub
parent 5acf909235
commit fd4c2acde8
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 122 additions and 80 deletions

View file

@ -18,7 +18,8 @@ class SyncApiRepository {
}
Future<void> streamChanges(
Function(List<SyncEvent>, Function() abort) onData, {
Future<void> Function(List<SyncEvent>, Function() abort, Function() reset) onData, {
Function()? onReset,
int batchSize = kSyncEventBatchSize,
http.Client? httpClient,
}) async {
@ -69,6 +70,8 @@ class SyncApiRepository {
shouldAbort = true;
}
final reset = onReset ?? () {};
try {
final response = await client.send(request);
@ -91,12 +94,12 @@ class SyncApiRepository {
continue;
}
await onData(_parseLines(lines), abort);
await onData(_parseLines(lines), abort, reset);
lines.clear();
}
if (lines.isNotEmpty && !shouldAbort) {
await onData(_parseLines(lines), abort);
await onData(_parseLines(lines), abort, reset);
}
} catch (error, stack) {
_logger.severe("Error processing stream", error, stack);
@ -156,7 +159,8 @@ const _kResponseMap = <SyncEntityType, Function(Object)>{
SyncEntityType.albumToAssetV1: SyncAlbumToAssetV1.fromJson,
SyncEntityType.albumToAssetBackfillV1: SyncAlbumToAssetV1.fromJson,
SyncEntityType.albumToAssetDeleteV1: SyncAlbumToAssetDeleteV1.fromJson,
SyncEntityType.syncAckV1: _SyncAckV1.fromJson,
SyncEntityType.syncAckV1: _SyncEmptyDto.fromJson,
SyncEntityType.syncResetV1: _SyncEmptyDto.fromJson,
SyncEntityType.memoryV1: SyncMemoryV1.fromJson,
SyncEntityType.memoryDeleteV1: SyncMemoryDeleteV1.fromJson,
SyncEntityType.memoryToAssetV1: SyncMemoryAssetV1.fromJson,
@ -172,8 +176,9 @@ const _kResponseMap = <SyncEntityType, Function(Object)>{
SyncEntityType.personDeleteV1: SyncPersonDeleteV1.fromJson,
SyncEntityType.assetFaceV1: SyncAssetFaceV1.fromJson,
SyncEntityType.assetFaceDeleteV1: SyncAssetFaceDeleteV1.fromJson,
SyncEntityType.syncCompleteV1: _SyncEmptyDto.fromJson,
};
class _SyncAckV1 {
static _SyncAckV1? fromJson(dynamic _) => _SyncAckV1();
class _SyncEmptyDto {
static _SyncEmptyDto? fromJson(dynamic _) => _SyncEmptyDto();
}

View file

@ -29,6 +29,36 @@ class SyncStreamRepository extends DriftDatabaseRepository {
SyncStreamRepository(super.db) : _db = db;
Future<void> reset() async {
_logger.fine("SyncResetV1 received. Resetting remote entities");
try {
await _db.exclusively(() async {
// foreign_keys PRAGMA is no-op within transactions
// https://www.sqlite.org/pragma.html#pragma_foreign_keys
await _db.customStatement('PRAGMA foreign_keys = OFF');
await transaction(() async {
await _db.assetFaceEntity.deleteAll();
await _db.memoryAssetEntity.deleteAll();
await _db.memoryEntity.deleteAll();
await _db.partnerEntity.deleteAll();
await _db.personEntity.deleteAll();
await _db.remoteAlbumAssetEntity.deleteAll();
await _db.remoteAlbumEntity.deleteAll();
await _db.remoteAlbumUserEntity.deleteAll();
await _db.remoteAssetEntity.deleteAll();
await _db.remoteExifEntity.deleteAll();
await _db.stackEntity.deleteAll();
await _db.userEntity.deleteAll();
await _db.userMetadataEntity.deleteAll();
});
await _db.customStatement('PRAGMA foreign_keys = ON');
});
} catch (error, stack) {
_logger.severe('Error: SyncResetV1', error, stack);
rethrow;
}
}
Future<void> deleteUsersV1(Iterable<SyncUserDeleteV1> data) async {
try {
await _db.userEntity.deleteWhere((row) => row.id.isIn(data.map((e) => e.userId)));