UNPKG

nats

Version:

Node.js client for NATS, a lightweight, high-performance cloud native messaging system

395 lines 16.3 kB
"use strict"; /* * Copyright 2020-2022 The NATS Authors * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; var __asyncValues = (this && this.__asyncValues) || function (o) { if (!Symbol.asyncIterator) throw new TypeError("Symbol.asyncIterator is not defined."); var m = o[Symbol.asyncIterator], i; return m ? m.call(o) : (o = typeof __values === "function" ? __values(o) : o[Symbol.iterator](), i = {}, verb("next"), verb("throw"), verb("return"), i[Symbol.asyncIterator] = function () { return this; }, i); function verb(n) { i[n] = o[n] && function (v) { return new Promise(function (resolve, reject) { v = o[n](v), settle(resolve, reject, v.done, v.value); }); }; } function settle(resolve, reject, d, v) { Promise.resolve(v).then(function(v) { resolve({ value: v, done: d }); }, reject); } }; Object.defineProperty(exports, "__esModule", { value: true }); exports.humanizeBytes = exports.msgThroughput = exports.throughput = exports.Bench = exports.Metric = void 0; const types_1 = require("./types"); const nuid_1 = require("./nuid"); const util_1 = require("./util"); const core_1 = require("./core"); class Metric { constructor(name, duration) { this.name = name; this.duration = duration; this.date = Date.now(); this.payload = 0; this.msgs = 0; this.bytes = 0; } toString() { const sec = (this.duration) / 1000; const mps = Math.round(this.msgs / sec); const label = this.asyncRequests ? "asyncRequests" : ""; let minmax = ""; if (this.max) { minmax = `${this.min}/${this.max}`; } return `${this.name}${label ? " [asyncRequests]" : ""} ${humanizeNumber(mps)} msgs/sec - [${sec.toFixed(2)} secs] ~ ${throughput(this.bytes, sec)} ${minmax}`; } toCsv() { return `"${this.name}",${new Date(this.date).toISOString()},${this.lang},${this.version},${this.msgs},${this.payload},${this.bytes},${this.duration},${this.asyncRequests ? this.asyncRequests : false}\n`; } static header() { return `Test,Date,Lang,Version,Count,MsgPayload,Bytes,Millis,Async\n`; } } exports.Metric = Metric; class Bench { constructor(nc, opts = { msgs: 100000, size: 128, subject: "", asyncRequests: false, pub: false, sub: false, req: false, rep: false, }) { this.nc = nc; this.callbacks = opts.callbacks || false; this.msgs = opts.msgs || 0; this.size = opts.size || 0; this.subject = opts.subject || nuid_1.nuid.next(); this.asyncRequests = opts.asyncRequests || false; this.pub = opts.pub || false; this.sub = opts.sub || false; this.req = opts.req || false; this.rep = opts.rep || false; this.perf = new util_1.Perf(); this.payload = this.size ? new Uint8Array(this.size) : types_1.Empty; if (!this.pub && !this.sub && !this.req && !this.rep) { throw new Error("no bench option selected"); } } run() { return __awaiter(this, void 0, void 0, function* () { this.nc.closed() .then((err) => { if (err) { throw new core_1.NatsError(`bench closed with an error: ${err.message}`, core_1.ErrorCode.Unknown, err); } }); if (this.callbacks) { yield this.runCallbacks(); } else { yield this.runAsync(); } return this.processMetrics(); }); } processMetrics() { const nc = this.nc; const { lang, version } = nc.protocol.transport; if (this.pub && this.sub) { this.perf.measure("pubsub", "pubStart", "subStop"); } if (this.req && this.rep) { this.perf.measure("reqrep", "reqStart", "reqStop"); } const measures = this.perf.getEntries(); const pubsub = measures.find((m) => m.name === "pubsub"); const reqrep = measures.find((m) => m.name === "reqrep"); const req = measures.find((m) => m.name === "req"); const rep = measures.find((m) => m.name === "rep"); const pub = measures.find((m) => m.name === "pub"); const sub = measures.find((m) => m.name === "sub"); const stats = this.nc.stats(); const metrics = []; if (pubsub) { const { name, duration } = pubsub; const m = new Metric(name, duration); m.msgs = this.msgs * 2; m.bytes = stats.inBytes + stats.outBytes; m.lang = lang; m.version = version; m.payload = this.payload.length; metrics.push(m); } if (reqrep) { const { name, duration } = reqrep; const m = new Metric(name, duration); m.msgs = this.msgs * 2; m.bytes = stats.inBytes + stats.outBytes; m.lang = lang; m.version = version; m.payload = this.payload.length; metrics.push(m); } if (pub) { const { name, duration } = pub; const m = new Metric(name, duration); m.msgs = this.msgs; m.bytes = stats.outBytes; m.lang = lang; m.version = version; m.payload = this.payload.length; metrics.push(m); } if (sub) { const { name, duration } = sub; const m = new Metric(name, duration); m.msgs = this.msgs; m.bytes = stats.inBytes; m.lang = lang; m.version = version; m.payload = this.payload.length; metrics.push(m); } if (rep) { const { name, duration } = rep; const m = new Metric(name, duration); m.msgs = this.msgs; m.bytes = stats.inBytes + stats.outBytes; m.lang = lang; m.version = version; m.payload = this.payload.length; metrics.push(m); } if (req) { const { name, duration } = req; const m = new Metric(name, duration); m.msgs = this.msgs; m.bytes = stats.inBytes + stats.outBytes; m.lang = lang; m.version = version; m.payload = this.payload.length; metrics.push(m); } return metrics; } runCallbacks() { return __awaiter(this, void 0, void 0, function* () { const jobs = []; if (this.sub) { const d = (0, util_1.deferred)(); jobs.push(d); let i = 0; this.nc.subscribe(this.subject, { max: this.msgs, callback: () => { i++; if (i === 1) { this.perf.mark("subStart"); } if (i === this.msgs) { this.perf.mark("subStop"); this.perf.measure("sub", "subStart", "subStop"); d.resolve(); } }, }); } if (this.rep) { const d = (0, util_1.deferred)(); jobs.push(d); let i = 0; this.nc.subscribe(this.subject, { max: this.msgs, callback: (_, m) => { m.respond(this.payload); i++; if (i === 1) { this.perf.mark("repStart"); } if (i === this.msgs) { this.perf.mark("repStop"); this.perf.measure("rep", "repStart", "repStop"); d.resolve(); } }, }); } if (this.pub) { const job = (() => __awaiter(this, void 0, void 0, function* () { this.perf.mark("pubStart"); for (let i = 0; i < this.msgs; i++) { this.nc.publish(this.subject, this.payload); } yield this.nc.flush(); this.perf.mark("pubStop"); this.perf.measure("pub", "pubStart", "pubStop"); }))(); jobs.push(job); } if (this.req) { const job = (() => __awaiter(this, void 0, void 0, function* () { if (this.asyncRequests) { this.perf.mark("reqStart"); const a = []; for (let i = 0; i < this.msgs; i++) { a.push(this.nc.request(this.subject, this.payload, { timeout: 20000 })); } yield Promise.all(a); this.perf.mark("reqStop"); this.perf.measure("req", "reqStart", "reqStop"); } else { this.perf.mark("reqStart"); for (let i = 0; i < this.msgs; i++) { yield this.nc.request(this.subject); } this.perf.mark("reqStop"); this.perf.measure("req", "reqStart", "reqStop"); } }))(); jobs.push(job); } yield Promise.all(jobs); }); } runAsync() { return __awaiter(this, void 0, void 0, function* () { const jobs = []; if (this.rep) { let first = false; const sub = this.nc.subscribe(this.subject, { max: this.msgs }); const job = (() => __awaiter(this, void 0, void 0, function* () { var _a, e_1, _b, _c; try { for (var _d = true, sub_1 = __asyncValues(sub), sub_1_1; sub_1_1 = yield sub_1.next(), _a = sub_1_1.done, !_a; _d = true) { _c = sub_1_1.value; _d = false; const m = _c; if (!first) { this.perf.mark("repStart"); first = true; } m.respond(this.payload); } } catch (e_1_1) { e_1 = { error: e_1_1 }; } finally { try { if (!_d && !_a && (_b = sub_1.return)) yield _b.call(sub_1); } finally { if (e_1) throw e_1.error; } } yield this.nc.flush(); this.perf.mark("repStop"); this.perf.measure("rep", "repStart", "repStop"); }))(); jobs.push(job); } if (this.sub) { let first = false; const sub = this.nc.subscribe(this.subject, { max: this.msgs }); const job = (() => __awaiter(this, void 0, void 0, function* () { var _e, e_2, _f, _g; try { for (var _h = true, sub_2 = __asyncValues(sub), sub_2_1; sub_2_1 = yield sub_2.next(), _e = sub_2_1.done, !_e; _h = true) { _g = sub_2_1.value; _h = false; const _m = _g; if (!first) { this.perf.mark("subStart"); first = true; } } } catch (e_2_1) { e_2 = { error: e_2_1 }; } finally { try { if (!_h && !_e && (_f = sub_2.return)) yield _f.call(sub_2); } finally { if (e_2) throw e_2.error; } } this.perf.mark("subStop"); this.perf.measure("sub", "subStart", "subStop"); }))(); jobs.push(job); } if (this.pub) { const job = (() => __awaiter(this, void 0, void 0, function* () { this.perf.mark("pubStart"); for (let i = 0; i < this.msgs; i++) { this.nc.publish(this.subject, this.payload); } yield this.nc.flush(); this.perf.mark("pubStop"); this.perf.measure("pub", "pubStart", "pubStop"); }))(); jobs.push(job); } if (this.req) { const job = (() => __awaiter(this, void 0, void 0, function* () { if (this.asyncRequests) { this.perf.mark("reqStart"); const a = []; for (let i = 0; i < this.msgs; i++) { a.push(this.nc.request(this.subject, this.payload, { timeout: 20000 })); } yield Promise.all(a); this.perf.mark("reqStop"); this.perf.measure("req", "reqStart", "reqStop"); } else { this.perf.mark("reqStart"); for (let i = 0; i < this.msgs; i++) { yield this.nc.request(this.subject); } this.perf.mark("reqStop"); this.perf.measure("req", "reqStart", "reqStop"); } }))(); jobs.push(job); } yield Promise.all(jobs); }); } } exports.Bench = Bench; function throughput(bytes, seconds) { return `${humanizeBytes(bytes / seconds)}/sec`; } exports.throughput = throughput; function msgThroughput(msgs, seconds) { return `${(Math.floor(msgs / seconds))} msgs/sec`; } exports.msgThroughput = msgThroughput; function humanizeBytes(bytes, si = false) { const base = si ? 1000 : 1024; const pre = si ? ["k", "M", "G", "T", "P", "E"] : ["K", "M", "G", "T", "P", "E"]; const post = si ? "iB" : "B"; if (bytes < base) { return `${bytes.toFixed(2)} ${post}`; } const exp = parseInt(Math.log(bytes) / Math.log(base) + ""); const index = parseInt((exp - 1) + ""); return `${(bytes / Math.pow(base, exp)).toFixed(2)} ${pre[index]}${post}`; } exports.humanizeBytes = humanizeBytes; function humanizeNumber(n) { return n.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ","); } //# sourceMappingURL=bench.js.map