Throttle calls to update remote touch token

This commit is contained in:
Ming Ming 2022-07-14 22:29:49 +08:00
parent 7563fa2ad6
commit 799594443b
3 changed files with 65 additions and 11 deletions

View file

@ -17,6 +17,7 @@ import 'package:nc_photos/exception.dart';
import 'package:nc_photos/iterable_extension.dart';
import 'package:nc_photos/object_extension.dart';
import 'package:nc_photos/or_null.dart';
import 'package:nc_photos/throttler.dart';
import 'package:nc_photos/touch_token_manager.dart';
import 'package:nc_photos/use_case/compat/v32.dart';
import 'package:path/path.dart' as path_lib;
@ -539,13 +540,18 @@ class FileCachedDataSource implements FileDataSource {
// generate a new random token
final token = const Uuid().v4().replaceAll("-", "");
const tokenManager = TouchTokenManager();
final dir = File(path: path_lib.dirname(f.path));
await tokenManager.setLocalToken(account, dir, token);
final fileRepo = FileRepo(this);
await tokenManager.setRemoteToken(fileRepo, account, dir, token);
_log.info(
"[updateMetadata] New touch token '$token' for dir '${dir.path}'");
await const TouchTokenManager().setLocalToken(account, dir, token);
// don't update remote token that frequently
(_touchTokenThrottlers["${account.url}/${dir.path}"] ??= Throttler(
onTriggered: _updateRemoteTouchToken,
logTag: "FileCachedDataSource._touchTokenThrottlers",
))
.trigger(
maxResponceTime: const Duration(seconds: 20),
maxPendingCount: 20,
data: _TouchTokenThrottlerData(account, dir, token),
);
}
@override
@ -575,6 +581,25 @@ class FileCachedDataSource implements FileDataSource {
await _remoteSrc.createDir(account, path);
}
Future<void> updateRemoteTouchTokenNow() async {
for (final t in _touchTokenThrottlers.values) {
await t.triggerNow();
}
}
Future<void> _updateRemoteTouchToken(
List<_TouchTokenThrottlerData> data) async {
try {
final fileRepo = FileRepo(this);
final d = data.last;
await const TouchTokenManager()
.setRemoteToken(fileRepo, d.account, d.dir, d.token);
} catch (e, stackTrace) {
_log.shout("[_updateRemoteTouchToken] Failed while setRemoteToken", e,
stackTrace);
}
}
final DiContainer _c;
final bool shouldCheckCache;
final FileForwardCacheManager? forwardCacheManager;
@ -582,9 +607,19 @@ class FileCachedDataSource implements FileDataSource {
final _remoteSrc = const FileWebdavDataSource();
final FileSqliteDbDataSource _sqliteDbSrc;
final _touchTokenThrottlers = <String, Throttler<_TouchTokenThrottlerData>>{};
static final _log = Logger("entity.file.data_source.FileCachedDataSource");
}
class _TouchTokenThrottlerData {
const _TouchTokenThrottlerData(this.account, this.dir, this.token);
final Account account;
final File dir;
final String token;
}
/// Forward cache for listing AppDb dirs
///
/// It's very expensive to list a dir and its sub-dirs one by one in multiple

View file

@ -10,6 +10,7 @@ import 'package:nc_photos/app_init.dart' as app_init;
import 'package:nc_photos/app_localizations.dart';
import 'package:nc_photos/di_container.dart';
import 'package:nc_photos/entity/file.dart';
import 'package:nc_photos/entity/file/data_source.dart';
import 'package:nc_photos/entity/file_util.dart' as file_util;
import 'package:nc_photos/event/event.dart';
import 'package:nc_photos/event/native_event.dart';
@ -230,6 +231,12 @@ class _MetadataTask {
NativeEvent.fire(FileExifUpdatedEvent(_processedIds).toEvent());
_processedIds = [];
}
final c = KiwiContainer().resolve<DiContainer>();
if (c.fileRepo.dataSrc is FileCachedDataSource) {
await (c.fileRepo.dataSrc as FileCachedDataSource)
.updateRemoteTouchTokenNow();
}
}
Future<void> _updateMetadata() async {

View file

@ -1,7 +1,6 @@
import 'dart:async';
import 'dart:math' as math;
import 'package:flutter/rendering.dart';
import 'package:logging/logging.dart';
import 'package:nc_photos/int_util.dart';
@ -56,17 +55,30 @@ class Throttler<T> {
_currentResponseTime = null;
_count = 0;
_maxCount = null;
// _data may be used else where, thus we must not call List.clear() here
_data = <T>[];
}
void _doTrigger() {
onTriggered?.call(_data);
clear();
Future<void> triggerNow() async {
if (_hasPendingEvent) {
_log.info("[triggerNow]$_logTag Triggered");
return _doTrigger();
}
}
Future<void> _doTrigger() async {
if (_hasPendingEvent) {
final data = _data;
clear();
await onTriggered?.call(data);
}
}
bool get _hasPendingEvent => _count > 0;
String get _logTag => logTag == null ? "" : "[$logTag]";
final ValueChanged<List<T>>? onTriggered;
final FutureOr<void> Function(List<T>)? onTriggered;
/// Extra tag printed with logs from this class
final String? logTag;