Only cache a dir after its children are synced and cached

This commit is contained in:
Ming Ming 2022-12-08 00:54:47 +08:00
parent d5d69144de
commit a3d3c6c375
2 changed files with 77 additions and 16 deletions

View file

@ -568,6 +568,22 @@ class FileSqliteDbDataSource implements FileDataSource {
static final _log = Logger("entity.file.data_source.FileSqliteDbDataSource");
}
class IntermediateSyncState {
const IntermediateSyncState({
required this.account,
required this.dir,
required this.remoteTouchEtag,
required this.files,
required this.shouldCache,
});
final Account account;
final File dir;
final String? remoteTouchEtag;
final List<File> files;
final bool shouldCache;
}
class FileCachedDataSource implements FileDataSource {
FileCachedDataSource(
this._c, {
@ -598,21 +614,29 @@ class FileCachedDataSource implements FileDataSource {
Account account,
File dir, {
required String? remoteTouchEtag,
}) async {
final state = await beginSync(
account,
dir,
remoteTouchEtag: remoteTouchEtag,
);
return concludeSync(state);
}
Future<IntermediateSyncState> beginSync(
Account account,
File dir, {
required String? remoteTouchEtag,
}) async {
try {
final remote = await _remoteSrc.list(account, dir);
await FileSqliteCacheUpdater(_c)(account, dir, remote: remote);
if (shouldCheckCache) {
// update our local touch token to match the remote one
try {
_log.info("[list] Update outdated local etag: ${dir.path}");
await _c.touchManager.setLocalEtag(account, dir, remoteTouchEtag);
} catch (e, stacktrace) {
_log.shout("[list] Failed while setLocalToken", e, stacktrace);
// ignore error
}
}
return remote;
return IntermediateSyncState(
account: account,
dir: dir,
remoteTouchEtag: remoteTouchEtag,
files: remote,
shouldCache: true,
);
} on ApiException catch (e) {
if (e.response.statusCode == 404) {
_log.info("[list] File removed: $dir");
@ -622,7 +646,13 @@ class FileCachedDataSource implements FileDataSource {
_log.warning(
"[list] Failed while remove from db, file not cached?", e);
}
return [];
return IntermediateSyncState(
account: account,
dir: dir,
remoteTouchEtag: remoteTouchEtag,
files: [],
shouldCache: false,
);
} else if (e.response.statusCode == 403) {
_log.info("[list] E2E encrypted dir: $dir");
try {
@ -633,13 +663,40 @@ class FileCachedDataSource implements FileDataSource {
_log.warning(
"[list] Failed while emptying from db, file not cached?", e);
}
return [];
return IntermediateSyncState(
account: account,
dir: dir,
remoteTouchEtag: remoteTouchEtag,
files: [],
shouldCache: false,
);
} else {
rethrow;
}
}
}
Future<List<File>> concludeSync(IntermediateSyncState state) async {
if (!state.shouldCache) {
return state.files;
}
await FileSqliteCacheUpdater(_c)(state.account, state.dir,
remote: state.files);
if (shouldCheckCache) {
// update our local touch token to match the remote one
try {
_log.info("[list] Update outdated local etag: ${state.dir.path}");
await _c.touchManager
.setLocalEtag(state.account, state.dir, state.remoteTouchEtag);
} catch (e, stacktrace) {
_log.shout("[list] Failed while setLocalToken", e, stacktrace);
// ignore error
}
}
return state.files;
}
@override
listSingle(Account account, File f) async {
final remote = await _remoteSrc.listSingle(account, f);

View file

@ -56,9 +56,12 @@ class SyncDir {
}
_log.info("[_syncDir] Dir changed: ${remoteDir.path}");
final children = await FileCachedDataSource(_c, shouldCheckCache: true)
.sync(account, remoteDir, remoteTouchEtag: status.item2);
final dataSrc = FileCachedDataSource(_c, shouldCheckCache: true);
final syncState = await dataSrc.beginSync(account, remoteDir,
remoteTouchEtag: status.item2);
final children = syncState.files;
if (!isRecursive) {
await dataSrc.concludeSync(syncState);
return true;
}
final subDirs = children
@ -88,6 +91,7 @@ class SyncDir {
}
progress.next();
}
await dataSrc.concludeSync(syncState);
return true;
}