UNPKG

signalk-server

Version:

An implementation of a [Signal K](http://signalk.org) server for boats.

111 lines 4.27 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.BackpressureManager = void 0; exports.parseBackpressureThresholds = parseBackpressureThresholds; const LatestValuesAccumulator_1 = require("./LatestValuesAccumulator"); const debug_1 = require("./debug"); const debug = (0, debug_1.createDebug)('signalk-server:backpressure'); const DEFAULT_ENTER_THRESHOLD = 512 * 1024; const DEFAULT_EXIT_THRESHOLD = 1024; const DEFAULT_MAX_BUFFER_SIZE = 4 * 512 * 1024; const DEFAULT_MAX_BUFFER_CHECK_TIME = 30 * 1000; function parseBackpressureThresholds(configFallbacks) { return { enterThreshold: process.env.BACKPRESSURE_ENTER ? parseInt(process.env.BACKPRESSURE_ENTER, 10) : DEFAULT_ENTER_THRESHOLD, exitThreshold: process.env.BACKPRESSURE_EXIT ? parseInt(process.env.BACKPRESSURE_EXIT, 10) : DEFAULT_EXIT_THRESHOLD, maxBufferSize: process.env.MAXSENDBUFFERSIZE ? parseInt(process.env.MAXSENDBUFFERSIZE, 10) : (configFallbacks?.maxSendBufferSize ?? DEFAULT_MAX_BUFFER_SIZE), maxBufferCheckTime: process.env.MAXSENDBUFFERCHECKTIME ? parseInt(process.env.MAXSENDBUFFERCHECKTIME, 10) : (configFallbacks?.maxSendBufferCheckTime ?? DEFAULT_MAX_BUFFER_CHECK_TIME) }; } class BackpressureManager { active = false; accumulator = new Map(); since = null; bufferSizeExceeded = undefined; transport; options; constructor(transport, options) { this.transport = transport; this.options = options; } onDrain() { if (this.active && this.accumulator.size > 0) { if (this.transport.getBufferLength() <= this.options.exitThreshold) { this.flush(); } } } send(delta) { const bufferLength = this.transport.getBufferLength(); if (this.active || bufferLength > this.options.enterThreshold) { if (!this.active) { this.active = true; this.since = Date.now(); debug('Entering backpressure for %s (buffer: %d)', this.transport.id, bufferLength); } (0, LatestValuesAccumulator_1.accumulateLatestValue)(this.accumulator, delta); } else { this.options.beforeWrite?.(delta); this.transport.write(delta); } this.assertBufferSize(bufferLength); } flush() { if (this.accumulator.size === 0) return; const countBefore = this.accumulator.size; const duration = this.since ? Date.now() - this.since : 0; const deltas = (0, LatestValuesAccumulator_1.buildFlushDeltas)(this.accumulator, duration); for (const delta of deltas) { this.options.beforeWrite?.(delta); this.transport.write(delta); } this.accumulator.clear(); this.active = false; this.since = null; debug('Flushed %d accumulated values for %s', countBefore, this.transport.id); } assertBufferSize(knownBufferLength) { if (this.options.maxBufferSize === 0) return; const bufferLength = knownBufferLength ?? this.transport.getBufferLength(); if (bufferLength > this.options.maxBufferSize) { if (!this.bufferSizeExceeded) { console.warn(`${this.transport.id} outgoing buffer > max:${bufferLength}`); this.bufferSizeExceeded = Date.now(); } if (Date.now() - this.bufferSizeExceeded > this.options.maxBufferCheckTime) { console.error('Send buffer overflow, terminating connection ' + this.transport.id); this.transport.destroy(); } } else { this.bufferSizeExceeded = undefined; } } clear() { this.accumulator.clear(); this.active = false; this.since = null; this.bufferSizeExceeded = undefined; } get isActive() { return this.active; } get accumulatorSize() { return this.accumulator.size; } } exports.BackpressureManager = BackpressureManager; //# sourceMappingURL=BackpressureManager.js.map