{"id":13494048,"url":"https://github.com/jeffbski/bench-rest","last_synced_at":"2025-04-04T15:11:12.757Z","repository":{"id":6269810,"uuid":"7503238","full_name":"jeffbski/bench-rest","owner":"jeffbski","description":"bench-rest - benchmark REST (HTTP/HTTPS) API's. node.js client module for easy load testing / benchmarking REST API's using a simple structure/DSL can create REST flows with setup and teardown and returns (measured) metrics.","archived":false,"fork":false,"pushed_at":"2022-04-07T16:04:26.000Z","size":84,"stargazers_count":304,"open_issues_count":15,"forks_count":51,"subscribers_count":17,"default_branch":"master","last_synced_at":"2025-03-28T14:07:33.870Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/jeffbski.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2013-01-08T14:52:29.000Z","updated_at":"2025-02-06T01:13:13.000Z","dependencies_parsed_at":"2022-09-13T11:10:47.393Z","dependency_job_id":null,"html_url":"https://github.com/jeffbski/bench-rest","commit_stats":null,"previous_names":[],"tags_count":15,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jeffbski%2Fbench-rest","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jeffbski%2Fbench-rest/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jeffbski%2Fbench-rest/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jeffbski%2Fbench-rest/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/jeffbski","download_url":"https://codeload.github.com/jeffbski/bench-rest/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247198463,"owners_count":20900080,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-07-31T19:01:21.278Z","updated_at":"2025-04-04T15:11:12.741Z","avatar_url":"https://github.com/jeffbski.png","language":"JavaScript","funding_links":[],"categories":["JavaScript"],"sub_categories":[],"readme":"# bench-rest benchmark REST API's\n\nNode.js client module for easy load testing / benchmarking REST (HTTP/HTTPS) API's using a simple structure/DSL can create REST flows with setup and teardown and returns (measured) metrics.\n\nRoughly `bench-rest` = [mikeal/request](https://github.com/mikeal/request) + [caolan/async](https://github.com/caolan/async) + [felixge/node-measured](https://github.com/felixge/node-measured)\n\n[![Build Status](https://secure.travis-ci.org/jeffbski/bench-rest.png?branch=master)](http://travis-ci.org/jeffbski/bench-rest)\n[![Known Vulnerabilities](https://snyk.io/test/github/jeffbski/bench-rest/d1fca5a48c8b5d6583a637810da529a9886a4952/badge.svg)](https://snyk.io/test/github/jeffbski/bench-rest/d1fca5a48c8b5d6583a637810da529a9886a4952)\n\n## Contents on this page\n\n - \u003ca href=\"#installation\"\u003eInstallation\u003c/a\u003e\n - \u003ca href=\"#prog-usage\"\u003eProgrammatic usage\u003c/a\u003e\n - \u003ca href=\"#cmd-usage\"\u003eCommand-line usage\u003c/a\u003e\n - \u003ca href=\"#goals\"\u003eGoals\u003c/a\u003e\n - \u003ca href=\"#detailed-usage\"\u003eDetailed usage\u003c/a\u003e\n   - \u003ca href=\"#returns\"\u003eReturns EventEmitter\u003c/a\u003e\n   - \u003ca href=\"#stats\"\u003eStats (metrics) and errorCount benchmark results\u003c/a\u003e\n   - \u003ca href=\"#shortcuts\"\u003eShortcuts for expressing the REST flow\u003c/a\u003e\n   - \u003ca href=\"#run-options\"\u003eRun options - number of iterations and concurrency\u003c/a\u003e\n   - \u003ca href=\"#rest-flow\"\u003eREST operations flow\u003c/a\u003e\n   - \u003ca href=\"#tokens\"\u003eToken substitution\u003c/a\u003e\n   - \u003ca href=\"#pre-post\"\u003ePre/post operation processing\u003c/a\u003e\n - \u003ca href=\"#why\"\u003eWhy create this project?\u003c/a\u003e\n - \u003ca href=\"#tuning\"\u003eTuning\u003c/a\u003e\n   - \u003ca href=\"#tuning-mac\"\u003eTuning Mac OS\u003c/a\u003e\n - \u003ca href=\"#modules\"\u003eKey modules leveraged\u003c/a\u003e\n - \u003ca href=\"#get-involved\"\u003eGet Involved\u003c/a\u003e\n - \u003ca href=\"#license\"\u003eMIT License\u003c/a\u003e\n\n\n\u003ca name=\"installation\"/\u003e\n\n## Installation\n\nRequires node.js \u003e= 0.10\n\n```bash\n# If using programmatically\nnpm install bench-rest\n\n# OR possibly with -g option if planning to use from command line\nnpm install -g bench-rest\n```\n\n\u003ca name=\"prog-usage\"/\u003e\n\n## Programmatic Usage\n\nSimple flow performing 100 iterations with 10 concurrent connections\n\n```javascript\n  var benchrest = require('bench-rest');\n  var flow = 'http://localhost:8000/';  // can use as simple single GET\n\n  // OR more powerfully define an array of REST operations with substitution\n  // This does a unique PUT and then a GET for each iteration\n  var flow = {\n    main: [\n      { put: 'http://localhost:8000/foo_#{INDEX}', json: 'mydata_#{INDEX}' },\n      { get: 'http://localhost:8000/foo_#{INDEX}' }\n    ]\n  };\n\n  // if the above flow will be used with the command line runner or\n  // programmatically from a separate file then export it.\n  module.exports = flow;\n\n  // There are even more flow options like setup and teardown, see detailed usage\n\n  var runOptions = {\n    limit: 10,     // concurrent connections\n    iterations: 100  // number of iterations to perform\n  };\n  benchrest(flow, runOptions)\n    .on('error', function (err, ctxName) { console.error('Failed in %s with err: ', ctxName, err); })\n    .on('end', function (stats, errorCount) {\n      console.log('error count: ', errorCount);\n      console.log('stats', stats);\n    });\n```\n\nSee \u003ca href=\"#detailed-usage\"\u003eDetailed Usage\u003c/a\u003e section below for more details\n\n\n\u003ca name=\"cmd-usage\"/\u003e\n\n## Command-line usage\n\n```bash\n# if installed with -g\nbench-rest\n\n# otherwise use from node_modules\nnode_modules/.bin/bench-rest\n```\n\nOutputs\n\n```\n  Usage: bench-rest [options] \u003cflow-js-path-or-GET-URL\u003e\n\n  Options:\n\n    -h, --help                   output usage information\n    -V, --version                output the version number\n    -n --iterations \u003cinteger\u003e    Number of iterations to run, defaults to 1\n    -a --prealloc \u003cinteger\u003e      Max iterations to preallocate, defaults 100000\n    -c --concurrency \u003cinteger\u003e   Concurrent operations, defaults to 10\n    -d --progress \u003cinteger\u003e      Display progress bar (\u003e 0), update every N ms, defaults 1000\n    -u --user \u003cusername\u003e         User for basic authentication, default no auth\n    -p --password \u003cpassword\u003e     Password for basic authentication\n    -e --evaluate \u003cflow-string\u003e  Evaluate flow from string, not file\n\n  Examples:\n\n    bench-rest -n 100 -c 100 ./examples/simple.js\n    bench-rest -n 100 -c 100 -u \"joe\" -p \"secret\" /foo/flow.js\n    bench-rest -n 10 -c 2 http://localhost:8000/\n    bench-rest -n 10 -c 2 -e \"{ head: 'http://localhost:8000/' }\"\n```\n\nRunning this\n\n```bash\nbench-rest -n 1000 -c 50 ./examples/simple.js\n```\n\nwould output\n\n```\nBenchmarking 1000 iteration(s) using up to 50 concurrent connections\n\nUsing flow from: /Users/barczewskij/projects/bench-rest/examples/simple.js\n { main: [ { get: 'http://localhost:8000/' } ] }\nProgress [=======================================] 100% 0.0s conc:49 1341/s\n\nerrors:  0\nstats:  { totalElapsed: 894,\n  main:\n   { meter:\n      { mean: 1240.6947890818858,\n        count: 1000,\n        currentRate: 1240.6947890818858,\n        '1MinuteRate': 0,\n        '5MinuteRate': 0,\n        '15MinuteRate': 0 },\n     histogram:\n      { min: 4,\n        max: 89,\n        sum: 41603,\n        variance: 242.0954864864864,\n        mean: 41.603,\n        stddev: 15.55941793533699,\n        count: 1000,\n        median: 42,\n        p75: 50,\n        p95: 70.94999999999993,\n        p99: 81.99000000000001,\n        p999: 88.99900000000002 } } }\n```\n\nIt has one expected required parameter which is the path to a node.js\nfile which exports a REST flow. For example:\n\n```javascript\n  var flow = {\n    main: [{ get: 'http://localhost:8000/' }]  // could be an array of REST operations\n  };\n\n  // if the above flow will be used with the command line runner or\n  // programmatically from a separate file then export it.\n  module.exports = flow;\n```\n\nCheck for example flows in the `examples` directory.\n\nSee \u003ca href=\"#detailed-usage\"\u003eDetailed Usage\u003c/a\u003e for more details on creating more advanced REST flows.\n\n\n\n\u003ca name=\"goals\"/\u003e\n\n## Goals\n\n - Easy to create REST (HTTP/HTTPS) flows for benchmarking\n - Generate good concurrency (at least 8K concurrent connections for single proc on Mac OS X)\n - Obtain metrics from the runs with average, total, min, max, histogram, req/s\n - Allow iterations to vary easily using token subsitution\n - Run programmatically so can be used with CI server\n - Flow can have setup and teardown operations for startup and shutdown as well as for each iteration\n - Ability to automatically handles cookies separately for each iteration\n - Ability to automatically follows redirects for operations\n - Errors will automatically stop an iterations flow and be tracked\n - Easy use and handling of etags\n - Allows pre/post processing or verification of data\n - Provide programmatically and via cmd line the dynamic concurrency count\n\n\u003ca name=\"detailed-usage\"/\u003e\n\n## Detailed Usage\n\nAdvanced flow with setup/teardown and multiple steps to benchmark in each iteration\n\n```javascript\n  var benchrest = require('bench-rest');\n  var flow = {\n    before: [],      // operations to do before anything\n    beforeMain: [],  // operations to do before each iteration\n    main: [  // the main flow for each iteration, #{INDEX} is unique iteration counter token\n      { put: 'http://localhost:8000/foo_#{INDEX}', json: 'mydata_#{INDEX}' },\n      { get: 'http://localhost:8000/foo_#{INDEX}' }\n    ],\n    afterMain: [{ del: 'http://localhost:8000/foo_#{INDEX}' }],   // operations to do after each iteration\n    after: []        // operations to do after everything is done\n  };\n\n  module.exports = flow;\n\n  var runOptions = {\n    limit: 10,         // concurrent connections\n    iterations: 1000,  // number of iterations to perform\n    prealloc: 100      // only preallocate up to 100 before starting\n  };\n  var errors = [];\n  benchrest(flow, runOptions)\n    .on('error', function (err, ctxName) { console.error('Failed in %s with err: ', ctxName, err); })\n    .on('progress', function (stats, percent, concurrent, ips) {\n      console.log('Progress: %s complete', percent);\n    })\n    .on('end', function (stats, errorCount) {\n      console.log('error count: ', errorCount);\n      console.log('stats', stats);\n    });\n```\n\n\u003ca name=\"returns\"/\u003e\n\n### Returns EventEmitter\n\nThe main function from `require('bench-rest')` will return a node.js EventEmitter instance when called with the `flow` and `runOptions`. This event emitter will emit the following events:\n\n - `error` - emitted as an error occurs during a run. It emits parameters `err` and `ctxName` matching where the error occurred (`main`, `before`, `beforeMain`, `after`, `afterMain`)\n - `progress` - emitted periodically as iterations complete. It emits parameters `stats`, `percentComplete`, `concurrent`, `ips`. The `stats` is the current `measured` stats (discussed below). The `concurrent` param is the concurrent connection count at that point in time. The `ips` is the calculated current iterations per second rate at which the iterations are executing. The interval at which progress is output is controlled by the runOption.progress in milliseconds.\n - `end` - emitted when the benchmark run has finished (successfully or otherwise). It emits parameters `stats` and `errorCount` (discussed below).\n\n\n\u003ca name=\"stats\"/\u003e\n\n#### Stats (metrics) and errorCount benchmark results\n\nThe `stats` is a `measured` data object and the `errorCount` is an count of the errors encountered. Time is reported in milliseconds. See `measured` for complete description of all the properties. https://github.com/felixge/node-measured\n\n`stats.totalElapsed` is the elapsed time in milliseconds for the entire run including all setup and teardown operations\n\nThe `stats.main` will be the meter data for the main benchmark flow operations (not including the beforeMain and afterMain operations).\n\nA couple key metrics to be aware of:\n\n - `stats.main.meter.mean` - average iterations / sec\n - `stats.main.meter.count` - iterations completed\n - `stats.main.meter.currentRate` - iterations / sec at this moment (mainly useful when monitoring progress)\n - `stats.main.1MinuteRate` - iterations / sec for the last minute (only relevant if more than 1 minute has passed)\n - `stats.main.histogram.min` - the minimum time any iteration took (milliseconds)\n - `stats.main.histogram.max` - the maximum time any iteration took (milliseconds)\n - `stats.main.histogram.mean` - the average time any iteration took (milliseconds)\n - `stats.main.histogram.p95` - the amount of time that 95% of all iterations completed within (milliseconds)\n\nThe output of the above run will look something like:\n\n```javascript\nerror count:  0\nstats {\n  totalElapsed: 151,\n  main:\n   { meter:\n      { mean: 1190.4761904761904,\n        count: 100,\n        currentRate: 1190.4761904761904,\n        '1MinuteRate': 0,\n        '5MinuteRate': 0,\n        '15MinuteRate': 0 },\n     histogram:\n      { min: 3,\n        max: 66,\n        sum: 985,\n        variance: 43.502525252525245,\n        mean: 9.85,\n        stddev: 6.595644415258091,\n        count: 100,\n        median: 8.5,\n        p75: 11,\n        p95: 17,\n        p99: 65.53999999999976,\n        p999: 66 } } }\n```\n\n\u003ca name=\"shortcuts\"/\u003e\n\n### Shortcuts for expressing flow\n\nIf you have very simple flow that does not need setup and teardown, then there are a few shortcuts for expressing the flow.\n\n - pass flow as just a string URL - it will perform a GET on this URL as the main flow, ex: `var flow = 'http://localhost:8000/';`\n - pass flow as just a single REST operation, ex: `var flow = { head: 'http://localhost:8000/' };`\n - pass flow as array of REST operations\n\n```javascript\n// passing as array implies no setup/teardown and these are the main operations\nvar flow = [\n  { put: 'http://localhost:8000/foo', json: 'mydata' },\n  { get: 'http://localhost:8000/foo' }\n];\n```\n\n\u003ca name=\"run-options\"/\u003e\n\n### Run options\n\nThe runOptions object can have the following properties which govern the benchmark run:\n\n - `limit` - required number of concurrent operations to limit at any given time\n - `iterations` - required number of flow iterations to perform on the `main` flow (as well as `beforeMain` and `afterMain` setup/teardown operations)\n - `prealloc` - optional max number of iterations to preallocate before starting, defaults to lesser of 100K and `iterations`. When using large number of iterations or large payload per iteration, it can be necessary to adjust this for optimal memory use.\n - `user` - optional user to be used for basic authentication\n - `password` - optional password to be used for basic authentication\n - `progress` - optional, if non-zero number is provided it enables the output of progress events each time this number of milliseconds has passed\n\n\u003ca name=\"rest-flow\"/\u003e\n\n### REST Operations in the flow\n\nThe REST operations that need to be performed in either as part of the main flow or for setup and teardown are configured using the following flow properties.\n\nEach array of opertions will be performed in series one after another unless an error is hit. The afterMain and after operations will be performed regardless of any errors encountered in the flow.\n\n```javascript\n  var flow = {\n    before: [],      // REST operations to perform before anything starts\n    beforeMain: [],  // REST operations to perform before each iteration\n    main: [],        // REST operations to perform for each iteration\n    afterMain: [],   // REST operations to perform after each iteration\n    after: []        // REST operations to perform after everything is finished\n  };\n```\n\nEach operation can have the following properties:\n\n - one of these common REST properties `get`, `head`, `put`, `post`, `patch`, `del` (using del rather than delete since delete is a JS reserved word) with a value pointing to the URI, ex: `{ get: 'http://localhost:8000/foo' }`\n - alternatively can specify `method` (use uppercase) and `uri` directly, ex: `{ method: 'GET', uri: 'http://localhost:8000/foo' }`\n - `json` optionally provide data which will be JSON stringified and provided as body also setting content type to application/json, ex: `{ put: 'http://localhost:8000/foo', json: { foo: 10 } }`\n - `headers` - optional headers to set, ex: `{ get: 'http://localhost:8000/foo', headers: { 'Accept-Encoding': 'gzip'}`\n - any other properties/options which are valid for `mikeal/request` - see https://github.com/mikeal/request\n - pre/post processing - optional array as `beforeHooks` and `afterHooks` which can perform processing before and/or after an operation. See \u003ca href=\"#pre-post\"\u003ePre/post operation processing\u003c/a\u003e section below for details.\n\n\n\u003ca name=\"tokens\"/\u003e\n\n### Token substitution for iteration operations\n\nTo make REST flows that are independent of each other, one often wants unique URLs and unique data, so one way to make this easy is to include special tokens in the `uri`, `json`, or `data`.\n\nCurrently the token(s) replaced in the `uri`, `json`, or `body` are:\n\n - `#{INDEX}` - replaced with the zero based counter/index of the iteration\n\nNote: for the `json` property the `json` object is JSON.stringified, tokens substituted, then JSON.parsed back to an object so that tokens will be substituted anywhere in the structure. If subsitution is not needed (no `#{INDEX}` in the structure, then no copy (stringify/parse) will be performed.\n\n\u003ca name=\"pre-post\"/\u003e\n\n### Pre/post operation processing\n\nIf an array of hooks is specified in an operation as `beforeHooks` and/or `afterHooks` then these synchronous operations will be done before/after the REST operation.\n\nBuilt-in processing filters can be referred to by name using a string, while custom filters can be provided as a function, ex:\n\n```javascript\n// This causes the HEAD operation to use a previously saved etag if found for this URI\n// setting the If-None-Match header with it, and then if the HEAD request returns a failing\n// status code\n{ head: 'http://localhost:8000', beforeHooks: ['useEtag'], afterHooks: ['ignoreStatus'] }\n```\n\nThe list of current built-in beforeHooks:\n\n- `useEtag` - if an etag had been previously saved for this URI with `saveEtag` afterHook, then set the appropriate header (for GET/HEAD, `If-None-Match`, otherwise `If-Match`). If was not previously saved or empty then no header is set.\n\nThe list of current built-in afterHooks:\n\n - `saveEtag` - afterHook which causes an etag to be saved into an object cache specific to this iteration. Stored by URI. If the etag was the result of a POST operation and a `Location` header was provided, then the URI at the `Location` will be used.\n - `ignoreStatus` - afterHookif an operation could possibly return an error code that you want to ignore and always continue anyway. Failing status codes are those that are greater than or equal to 400. Normal operation would be to terminate an iteration if there is a failure status code in any `before`, `beforeMain`, or `main` operation.\n - `verify2XX` - afterHook which fails if an operation's status code was not in 200-299 range. If you don't want a redirect followed, be sure to add the request option `followRedirect: false`. Note: by default errors are verified (greater than or equal to 400), so this would just be used when you want to make sure it is not a 3xx either.\n - `startStepTimer` - used in beforeHooks to start a timer for this step named step_OPIDX where OPIDX is the zero based index of the step in the flow. Be sure to call `endStepTimer` in afterHooks to end it. Provides detailed stats for an individual step in a flow.\n - `endStepTimer` - used in afterHooks to end a timer previously started with `startStepTimer` and included in the stats displayed at the end of the run.\n\n\nTo create custom beforeHook or afterHook the synchronous function needs to accept an `all` object and return the same or possibly modified object. To exit the flow, an exception can be thrown which will be caught and emitted. Using these beforeHooks you can modify the next request, and using the afterHooks can verify the response and/or store data for future actions.\n\nOne way to keep state for each iteration (without using external variables) is to use the all.iterCtx object which is an empty object provided for each iteration. See `examples/hook.js` and `test/hooks-iter-ctx.mocha.js`\n\nSo a verification function could be written as such\n\n```javascript\nfunction verifyData(all) {\n  if (all.err) return all; // errored so just return and it will error as normal\n  assert.equal(all.response.statusCode, 200);\n  assert(all.body, 'foobarbaz'); // if throws, err is caught and counted\n  return all; // always return all if you want it to continue\n}\n```\n\nPostprocess function example:\n\n```javascript\nfunction postProcess(all) {\n  // all.iterCtx obj is where you can keep data for an iteration\n  all.iterCtx.location = all.response.headers.location;\n  all.iterCtx.body = all.body;\n  return all; // always return all if you want it to continue\n}\n```\n\nPreprocess function example:\n\n```javascript\nfunction preProcess(all) {\n  // all.iterCtx object is where you can keep data private for an iteration\n  // all.requestOptions will be used for the request, modify as needed\n  all.requestOptions.uri = 'http://localhost:8000' + all.iterCtx.location;\n  return all; // always return all if you want it to continue\n}\n```\n\n\nThe properties available on the `all` object are:\n\n - all.env.index - the zero based counter for this iteration, same as what is used for #{INDEX}\n - all.env.jar - the cookie jar\n - all.env.user - basic auth user if provided\n - all.env.password - basic auth password if provided\n - all.env.etags - object of etags saved by URI\n - all.env.stats - measured stats collection containing `totalElapsed` and `main`. If `startStepTimer` and `endStepTimer` hooks are added to individual steps then additional timers step_OPINDEX will be created for steps that have the hooks.\n - all.iterCtx - empty object created for each iteration, can be used for your private storage from beforeHooks and afterHooks\n - all.opIndex - zero based index for the operation in the array of operations, ie: first operation in the main flow will have opIndex of 0\n - all.requestOptions - the options that will be used for the request (see mikeal/request)\n - all.requestOptions.uri - the URL that will be used for the request\n - all.requestOptions.method - the method that will be used for the request\n - all.response - the response obj (only for afterHooks)\n - all.body - the response body (only for afterHooks)\n - all.err - not empty if an error has occurred\n - all.cb - the cb that will be called when done\n\n\n\u003ca name=\"why\"/\u003e\n\n## Why create this project?\n\nIt is important to understand how well your architecture performs and with each change to the system how performance is impacted. The best way to know this is to benchmark your system with each major change.\n\nBenchmarking also lets you:\n\n - understand how your system will act under load\n - how and whether multiple servers or processes will help you scale\n - whether a feature added improved or hurt performance\n - predict the need add instances or throttle load before your server reaches overload\n\nAfter attempting to use the variety of load testing clients and modules for benchmarking, none really met all of my desired goals. Most clients are only able to benchmark a single operation, not a whole flow and not one with setup and teardown.\n\nBuilding your own is certainly an option but it gets tedious to make all the necessary setup and error handling to achieve a simple flow and thus this project was born.\n\n\u003ca name=\"tuning\"/\u003e\n\n## Tuning OS\n\nEach OS may need some tweaking of the configuration to be able to generate or receive a large number of concurrent connections.\n\n\u003ca name=\"tuning-mac\"/\u003e\n\n### Mac OS X\n\nThe Mac OS X can be tweaked using the following parameters. The configuration allowed about 8K concurrent connections for a single process.\n\n```bash\nsysctl -a | grep maxfiles  # display maxfiles and maxfilesperproc  defaults 12288 and 10240\nsudo sysctl -w kern.maxfiles=25000\nsudo sysctl -w kern.maxfilesperproc=24500\nsysctl -a | grep somax # display max socket setting, default 128\nsudo sysctl -w kern.ipc.somaxconn=20000  # set\nulimit -S -n       # display soft max open files, default 256\nulimit -H -n       # display hard max open files, default unlimited\nulimit -S -n 20000  # set soft max open files\n```\n\n\u003ca name=\"modules\"/\u003e\n\n## Key modules leveraged\n\n - request - https://github.com/mikeal/request - for http/https operations with cookies, redirects\n - async - https://github.com/caolan/async - for limiting concurrency\n - measured - https://github.com/felixge/node-measured - for metrics\n\n\u003ca name=\"get-involved\"/\u003e\n\n## Tested on Node versions\n\n - 4\n - 5\n - 6\n\n## Get involved\n\nIf you have input or ideas or would like to get involved, you may:\n\n - contact me via twitter @jeffbski  - \u003chttp://twitter.com/jeffbski\u003e\n - open an issue on github to begin a discussion - \u003chttps://github.com/jeffbski/bench-rest/issues\u003e\n - fork the repo and send a pull request (ideally with tests) - \u003chttps://github.com/jeffbski/bench-rest\u003e\n\n## Developer Notes\n\nWe use semver for this package so any breaking changes will update the major version number.\n\n```bash\nnpm test # runs tests\n```\n\n### Versioning and publish\n\n```bash\nnpm version patch # increment version and create git tag, or minor, major\ngit push origin master --tags # upload changes and tags to github\nnpm publish # publish latest version to npm\n```\n\n\u003ca name=\"license\"/\u003e\n\n## License - MIT\n\n - [MIT license](http://github.com/jeffbski/bench-rest/raw/master/LICENSE)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjeffbski%2Fbench-rest","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fjeffbski%2Fbench-rest","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjeffbski%2Fbench-rest/lists"}