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/iterable_extension.dart';
import 'package:nc_photos/object_extension.dart'; import 'package:nc_photos/object_extension.dart';
import 'package:nc_photos/or_null.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/touch_token_manager.dart';
import 'package:nc_photos/use_case/compat/v32.dart'; import 'package:nc_photos/use_case/compat/v32.dart';
import 'package:path/path.dart' as path_lib; import 'package:path/path.dart' as path_lib;
@ -539,13 +540,18 @@ class FileCachedDataSource implements FileDataSource {
// generate a new random token // generate a new random token
final token = const Uuid().v4().replaceAll("-", ""); final token = const Uuid().v4().replaceAll("-", "");
const tokenManager = TouchTokenManager();
final dir = File(path: path_lib.dirname(f.path)); final dir = File(path: path_lib.dirname(f.path));
await tokenManager.setLocalToken(account, dir, token); await const TouchTokenManager().setLocalToken(account, dir, token);
final fileRepo = FileRepo(this); // don't update remote token that frequently
await tokenManager.setRemoteToken(fileRepo, account, dir, token); (_touchTokenThrottlers["${account.url}/${dir.path}"] ??= Throttler(
_log.info( onTriggered: _updateRemoteTouchToken,
"[updateMetadata] New touch token '$token' for dir '${dir.path}'"); logTag: "FileCachedDataSource._touchTokenThrottlers",
))
.trigger(
maxResponceTime: const Duration(seconds: 20),
maxPendingCount: 20,
data: _TouchTokenThrottlerData(account, dir, token),
);
} }
@override @override
@ -575,6 +581,25 @@ class FileCachedDataSource implements FileDataSource {
await _remoteSrc.createDir(account, path); 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 DiContainer _c;
final bool shouldCheckCache; final bool shouldCheckCache;
final FileForwardCacheManager? forwardCacheManager; final FileForwardCacheManager? forwardCacheManager;
@ -582,9 +607,19 @@ class FileCachedDataSource implements FileDataSource {
final _remoteSrc = const FileWebdavDataSource(); final _remoteSrc = const FileWebdavDataSource();
final FileSqliteDbDataSource _sqliteDbSrc; final FileSqliteDbDataSource _sqliteDbSrc;
final _touchTokenThrottlers = <String, Throttler<_TouchTokenThrottlerData>>{};
static final _log = Logger("entity.file.data_source.FileCachedDataSource"); 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 /// Forward cache for listing AppDb dirs
/// ///
/// It's very expensive to list a dir and its sub-dirs one by one in multiple /// 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/app_localizations.dart';
import 'package:nc_photos/di_container.dart'; import 'package:nc_photos/di_container.dart';
import 'package:nc_photos/entity/file.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/entity/file_util.dart' as file_util;
import 'package:nc_photos/event/event.dart'; import 'package:nc_photos/event/event.dart';
import 'package:nc_photos/event/native_event.dart'; import 'package:nc_photos/event/native_event.dart';
@ -230,6 +231,12 @@ class _MetadataTask {
NativeEvent.fire(FileExifUpdatedEvent(_processedIds).toEvent()); NativeEvent.fire(FileExifUpdatedEvent(_processedIds).toEvent());
_processedIds = []; _processedIds = [];
} }
final c = KiwiContainer().resolve<DiContainer>();
if (c.fileRepo.dataSrc is FileCachedDataSource) {
await (c.fileRepo.dataSrc as FileCachedDataSource)
.updateRemoteTouchTokenNow();
}
} }
Future<void> _updateMetadata() async { Future<void> _updateMetadata() async {

View file

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