From 799594443b29c80b55ecb9a61fe0e438349e5e44 Mon Sep 17 00:00:00 2001 From: Ming Ming Date: Thu, 14 Jul 2022 22:29:49 +0800 Subject: [PATCH] Throttle calls to update remote touch token --- app/lib/entity/file/data_source.dart | 47 ++++++++++++++++++++++++---- app/lib/service.dart | 7 +++++ app/lib/throttler.dart | 22 ++++++++++--- 3 files changed, 65 insertions(+), 11 deletions(-) diff --git a/app/lib/entity/file/data_source.dart b/app/lib/entity/file/data_source.dart index 9f9e1d23..7bcbfa55 100644 --- a/app/lib/entity/file/data_source.dart +++ b/app/lib/entity/file/data_source.dart @@ -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 updateRemoteTouchTokenNow() async { + for (final t in _touchTokenThrottlers.values) { + await t.triggerNow(); + } + } + + Future _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 = >{}; + 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 diff --git a/app/lib/service.dart b/app/lib/service.dart index 66e9b43c..417382b9 100644 --- a/app/lib/service.dart +++ b/app/lib/service.dart @@ -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(); + if (c.fileRepo.dataSrc is FileCachedDataSource) { + await (c.fileRepo.dataSrc as FileCachedDataSource) + .updateRemoteTouchTokenNow(); + } } Future _updateMetadata() async { diff --git a/app/lib/throttler.dart b/app/lib/throttler.dart index b2a81973..46d5fb86 100644 --- a/app/lib/throttler.dart +++ b/app/lib/throttler.dart @@ -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 { _currentResponseTime = null; _count = 0; _maxCount = null; + // _data may be used else where, thus we must not call List.clear() here _data = []; } - void _doTrigger() { - onTriggered?.call(_data); - clear(); + Future triggerNow() async { + if (_hasPendingEvent) { + _log.info("[triggerNow]$_logTag Triggered"); + return _doTrigger(); + } } + Future _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>? onTriggered; + final FutureOr Function(List)? onTriggered; /// Extra tag printed with logs from this class final String? logTag;