1
0
mirror of https://github.com/laurent22/joplin.git synced 2024-12-18 09:35:20 +02:00
joplin/packages/app-desktop/services/plugins/BackOffHandler.ts

65 lines
2.5 KiB
TypeScript

import Logger from '@joplin/lib/Logger';
import time from '@joplin/lib/time';
const logger = Logger.create('BackOffHandler');
// This handler performs two checks:
//
// 1. If the plugin makes many API calls one after the other, a delay is going
// to be applied before responding. The delay is set using backOffIntervals_.
// When a plugin needs to be throttled that way a warning is displayed so
// that the author gets an opportunity to fix it.
//
// 2. If the plugin makes many simultaneous calls (over 100), the handler throws
// an exception to stop the plugin. In that case the plugin will be broken,
// but most plugins will not get this error anyway because call are usually
// made in sequence. It might reveal a bug though - for example if the plugin
// makes a call every 1 second, but does not wait for the response (or assume
// the response will come in less than one second). In that case, the back
// off intervals combined with the incorrect code will make the plugin fail.
export default class BackOffHandler {
private backOffIntervals_ = Array(100).fill(0).concat([0, 1, 1, 2, 3, 5, 8]);
private lastRequestTime_ = 0;
private pluginId_: string;
private resetBackOffInterval_ = (this.backOffIntervals_[this.backOffIntervals_.length - 1] + 1) * 1000;
private backOffIndex_ = 0;
private waitCount_ = 0;
private maxWaitCount_ = 100;
public constructor(pluginId: string) {
this.pluginId_ = pluginId;
}
private backOffInterval() {
const now = Date.now();
if (now - this.lastRequestTime_ > this.resetBackOffInterval_) {
this.backOffIndex_ = 0;
} else {
this.backOffIndex_++;
}
this.lastRequestTime_ = now;
const effectiveIndex = this.backOffIndex_ >= this.backOffIntervals_.length ? this.backOffIntervals_.length - 1 : this.backOffIndex_;
return this.backOffIntervals_[effectiveIndex];
}
public async wait(path: string, args: any) {
const interval = this.backOffInterval();
if (!interval) return;
this.waitCount_++;
logger.warn(`Plugin ${this.pluginId_}: Applying a backoff of ${interval} seconds due to frequent plugin API calls. Consider reducing the number of calls, caching the data, or requesting more data per call. API call was: `, path, args, `[Wait count: ${this.waitCount_}]`);
if (this.waitCount_ > this.maxWaitCount_) throw new Error(`Plugin ${this.pluginId_}: More than ${this.maxWaitCount_} API alls are waiting - aborting. Please consider queuing the API calls in your plugins to reduce the load on the application.`);
await time.sleep(interval);
this.waitCount_--;
}
}