UNPKG

ipfs-bitswap

Version:

JavaScript implementation of the Bitswap data exchange protocol used by IPFS

118 lines 4.13 kB
import { EventEmitter } from 'events'; import MovingAverage, {} from '@vascosantos/moving-average'; export class Stat extends EventEmitter { _options; _queue; _stats; _frequencyLastTime; _frequencyAccumulators; _movingAverages; _enabled; _timeout; constructor(initialCounters, options) { super(); this._options = options; this._queue = []; this._stats = {}; this._frequencyLastTime = Date.now(); this._frequencyAccumulators = {}; this._movingAverages = {}; this._update = this._update.bind(this); initialCounters.forEach((key) => { this._stats[key] = BigInt(0); this._movingAverages[key] = {}; this._options.movingAverageIntervals.forEach((interval) => { const ma = this._movingAverages[key][interval] = MovingAverage(interval); ma.push(this._frequencyLastTime, 0); }); }); this._enabled = this._options.enabled; } enable() { this._enabled = true; } disable() { this._enabled = false; } stop() { if (this._timeout != null) { clearTimeout(this._timeout); } } get snapshot() { return Object.assign({}, this._stats); } get movingAverages() { return Object.assign({}, this._movingAverages); } push(counter, inc) { if (this._enabled) { this._queue.push([counter, inc, Date.now()]); this._resetComputeTimeout(); } } _resetComputeTimeout() { if (this._timeout != null) { clearTimeout(this._timeout); } this._timeout = setTimeout(this._update, this._nextTimeout()); } _nextTimeout() { // calculate the need for an update, depending on the queue length const urgency = this._queue.length / this._options.computeThrottleMaxQueueSize; return Math.max(this._options.computeThrottleTimeout * (1 - urgency), 0); } _update() { this._timeout = undefined; if (this._queue.length > 0) { let last; while (this._queue.length > 0) { const op = last = this._queue.shift(); (op != null) && this._applyOp(op); } (last != null) && this._updateFrequency(last[2]); // contains timestamp of last op this.emit('update', this._stats); } } _updateFrequency(latestTime) { const timeDiff = latestTime - this._frequencyLastTime; if (timeDiff > 0) { Object.keys(this._stats).forEach((key) => { this._updateFrequencyFor(key, timeDiff, latestTime); }); } this._frequencyLastTime = latestTime; } _updateFrequencyFor(key, timeDiffMS, latestTime) { const count = this._frequencyAccumulators[key] ?? 0; this._frequencyAccumulators[key] = 0; const hz = (count / timeDiffMS) * 1000; let movingAverages = this._movingAverages[key]; if (movingAverages == null) { movingAverages = this._movingAverages[key] = {}; } this._options.movingAverageIntervals.forEach((movingAverageInterval) => { let movingAverage = movingAverages[movingAverageInterval]; if (movingAverage == null) { movingAverage = movingAverages[movingAverageInterval] = MovingAverage(movingAverageInterval); } movingAverage.push(latestTime, hz); }); } _applyOp(op) { const key = op[0]; const inc = op[1]; if (typeof inc !== 'number') { throw new Error(`invalid increment number: ${inc}`); } if (!Object.prototype.hasOwnProperty.call(this._stats, key)) { this._stats[key] = BigInt(0); } this._stats[key] = BigInt(this._stats[key]) + BigInt(inc); if (this._frequencyAccumulators[key] == null) { this._frequencyAccumulators[key] = 0; } this._frequencyAccumulators[key] += inc; } } //# sourceMappingURL=stat.js.map