server: add torrentClientPollIntervalIdle to adjust polling when idle

Some automations like notification of download completion rely on
polling of torrent list. Plus, in the future, we might add more
automation capabilities such as "Pause after reaching certain ratio".

As such, don't completely stop polling when idle. Instead, add a config
"torrentClientPollIntervalIdle" to adjust the interval. The default is
(1000 * 60 * 15) ms aka 15 minutes that should allow low resources
consumption and ensure the automations like logging of download completion
time can work within reasonable error margin.

Fixes: 8452d51b79
Bug: #45, Flood-UI/flood#405
This commit is contained in:
Jesse Chan
2020-10-24 00:52:48 +08:00
parent 08fa52409b
commit aa0c7cf3db
4 changed files with 41 additions and 22 deletions

View File

@@ -91,6 +91,12 @@ const {argv} = require('yargs')
hidden: true,
type: 'number',
})
.option('clientpollidle', {
default: 1000 * 60 * 15,
describe: 'ADVANCED: How often (in ms) Flood will request the torrent list when no user is present',
hidden: true,
type: 'number',
})
.option('rtorrent', {
default: false,
describe: 'ADVANCED: rTorrent daemon managed by Flood',
@@ -174,6 +180,7 @@ const CONFIG = {
floodServerProxy: argv.proxy,
maxHistoryStates: argv.maxhistorystates,
torrentClientPollInterval: argv.clientpoll,
torrentClientPollIntervalIdle: argv.clientpollidle,
secret,
ssl: argv.ssl,
sslKey: argv.sslkey || path.resolve(path.join(argv.rundir, 'key.pem')),

1
config.d.ts vendored
View File

@@ -12,6 +12,7 @@ declare const CONFIG: {
floodServerProxy: string;
maxHistoryStates: number;
torrentClientPollInterval: number;
torrentClientPollIntervalIdle: number;
secret: string;
ssl: boolean;
sslKey: string;

View File

@@ -1,5 +1,5 @@
// This is the configuration file for Flood, a React-based frontend for the
// rtorrent BitTorrent client.
// rTorrent BitTorrent client.
// Copy this file to ./config.js and make changes below.
// config.js must exist before running `npm run build`.
@@ -12,19 +12,24 @@ const CONFIG = {
// the `/flood` of Flood's web server.
// See https://github.com/Flood-UI/flood/wiki/Using-Flood-behind-a-reverse-proxy
baseURI: '/',
// Flood uses a local nedb database to keep track of users, torrents,
// and activity. The database is regularly purged to remove outdated data.
// This value dictates how old data is, in milliseconds, before being purged.
dbCleanInterval: 1000 * 60 * 60,
// Where to store the local nedb database.
dbPath: './run/db/',
// Where to store Flood's temporary files
tempPath: './run/temp/',
// If this is true, there will be no users and no attempt to
// authenticate or password-protect any page. In that case,
// instead of per-user config, the following configUser settings
// will be used.
disableUsersAndAuth: false,
// Settings for the no-user configuration.
configUser: {
// {ClientConnectionSettings}
@@ -33,30 +38,48 @@ const CONFIG = {
version: 1,
socket: '/data/rtorrent.sock',
},
// The host that Flood should listen for web connections on.
// If you want to connect to Flood from hosts other that the one it is running
// on, you should change this value.
// To listen on all interfaces, change to `floodServerHost: '0.0.0.0'`..
floodServerHost: '127.0.0.1',
// The port that Flood should listen for web connections on.
floodServerPort: 3000,
// Used for development. See the "Local Development" section of README.md
// for detail.
floodServerProxy: 'http://127.0.0.1:3000',
// Flood keeps a history of torrent download and upload speeds.
// This value dictates the number of individual records per period to keep.
maxHistoryStates: 30,
// How often (in milliseconds) Flood will request the torrent list from.
// How often (in milliseconds) Flood will request the torrent list.
// This value affects how often values are updated when a user is present.
// {torrentClientPollIntervalIdle} will be used when no user is present.
// Note that poll intervals only affect activity stream. API requests
// like "GET /api/torrents" always trigger fresh torrent list fetch.
torrentClientPollInterval: 1000 * 2,
// How often (in milliseconds) Flood will request the torrent list when no user is present.
// {torrentClientPollInterval} will be used when at least one user is present. This value
// usually affects some automations such as notification of download completion. Automations
// that rely on torrent properties may be delayed within the interval.
torrentClientPollIntervalIdle: 1000 * 60 * 15,
// A unique secret for signing messages with JWT (see https://jwt.io).
// Change this to something unique and hard to guess.
// You can use 'uuidgen' or 'cat /proc/sys/kernel/random/uuid' or 'uuidgenerator.net'.
// eslint-disable-next-line no-undef
secret: process.env.FLOOD_SECRET || CHANGE_ME,
// Configuration for SSL, if using SSL with the Flood service directly.
ssl: false,
sslKey: '/absolute/path/to/key/',
sslCert: '/absolute/path/to/certificate/',
// disk space service checks disk space of mounted partitions
diskUsageService: {
// assign desired mounts to include. Refer to "Mounted on" column of `df -P`
@@ -64,6 +87,7 @@ const CONFIG = {
// "/mnt/disk"
// ]
},
// Allowed paths for file operations
// allowedPaths: ['/mnt/download', '/data/download'],
};

View File

@@ -15,8 +15,7 @@ interface TorrentServiceEvents {
}
class TorrentService extends BaseService<TorrentServiceEvents> {
errorCount = 0;
pollEnabled = false;
pollInterval = config.torrentClientPollIntervalIdle;
pollTimeout: NodeJS.Timeout | null = null;
torrentListSummary: TorrentListSummary = {id: Date.now(), torrents: {}};
@@ -41,25 +40,22 @@ class TorrentService extends BaseService<TorrentServiceEvents> {
// starts polling when the first streaming listener is added
this.on('newListener', (event) => {
if (!this.pollEnabled && event === 'TORRENT_LIST_DIFF_CHANGE') {
this.pollEnabled = true;
this.deferFetchTorrentList();
if (event === 'TORRENT_LIST_DIFF_CHANGE') {
this.pollInterval = config.torrentClientPollInterval;
}
});
// stops polling when the last streaming listener is removed
this.on('removeListener', (event) => {
if (event === 'TORRENT_LIST_DIFF_CHANGE' && this.listenerCount('TORRENT_LIST_DIFF_CHANGE') === 0) {
this.pollEnabled = false;
this.pollInterval = config.torrentClientPollIntervalIdle;
}
});
};
}
deferFetchTorrentList(interval = config.torrentClientPollInterval || 2000) {
if (this.pollEnabled) {
this.pollTimeout = setTimeout(this.fetchTorrentList, interval);
}
deferFetchTorrentList() {
this.pollTimeout = setTimeout(this.fetchTorrentList, this.pollInterval || 2000);
}
destroy() {
@@ -94,15 +90,7 @@ class TorrentService extends BaseService<TorrentServiceEvents> {
}
handleFetchTorrentListError() {
let nextInterval = config.torrentClientPollInterval || 2000;
// If more than 2 consecutive errors have occurred, then we delay the next request.
this.errorCount += 1;
if (this.errorCount > 2) {
nextInterval = Math.min(nextInterval + 2 ** this.errorCount, 1000 * 60);
}
this.deferFetchTorrentList(nextInterval);
this.deferFetchTorrentList();
this.emit('FETCH_TORRENT_LIST_ERROR');
return null;
@@ -118,7 +106,6 @@ class TorrentService extends BaseService<TorrentServiceEvents> {
this.deferFetchTorrentList();
this.errorCount = 0;
this.emit('FETCH_TORRENT_LIST_SUCCESS');
return this.torrentListSummary;
}