Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Create inflightRequestThrottle plugin #1431

Merged
merged 7 commits into from
Aug 7, 2017
Merged

Conversation

retrohacker
Copy link
Member

Plugin for limiting inflight requests

@@ -153,6 +153,13 @@ event, e.g., `server.on('after', plugins.metrics());`:
* `res` {Object} the response obj
* `route` {Object} the route obj that serviced the request

The module includes the following plugins to be used with restify's `pre` event:
* `inflightRequestThrottle(options)` - limits the max number of inflight requests
* `options.limit` {Number} the maximum number of simultaneous connections the server will handle before returning an error
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

options vs opts?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Second this -- let's keep it consistent :)

```js
const opts = { limit: 600, server: server };
opts.error = new Error('Resource Exhausted');
error.statuscode = 420;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should that be camelCase statusCode? Also, I might suggest adding an example using restify-errors constructors.


```js
const opts = { limit: 600, server: server };
opts.error = new Error('Resource Exhausted');
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it opts.error or opts.resp?

plugin: 'inflightRequestThrottle',
inflightRequests: inflightRequests,
limit: self._limit,
message: 'maximum inflight requests exceeded, rejecting request'
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Whoops, sorry, I should have been clearer about my last comment on this. I meant using the logger API itself, so something like this:

req.log.trace({
  plugin: 'inflightRequestThrottle',
  inflightRequests: inflightRequests,
  limit: self._limit
}, 'maximum inflight requests exceeded, rejecting request');

@retrohacker
Copy link
Member Author

@DonutEspresso one more time?

@@ -153,6 +153,13 @@ event, e.g., `server.on('after', plugins.metrics());`:
* `res` {Object} the response obj
* `route` {Object} the route obj that serviced the request

The module includes the following plugins to be used with restify's `pre` event:
* `inflightRequestThrottle(options)` - limits the max number of inflight requests
* `options.limit` {Number} the maximum number of simultaneous connections the server will handle before returning an error
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

s/simultaneous connections/inflight requests.

The module includes the following plugins to be used with restify's `pre` event:
* `inflightRequestThrottle(options)` - limits the max number of inflight requests
* `options.limit` {Number} the maximum number of simultaneous connections the server will handle before returning an error
* `options.resp` {Error} An error that will be passed to `res.send` when the limit is reached.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what does resp stand for? Response? If so can use the convention of res?

## Inflight Request Throttling

```js
const options = { limit: 600, server: server };
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The server property is not in docs. Do we need to pass in server or should we be only passing in the specific properties that the throttle plugin needs?


```js
const options = { limit: 600, server: server };
options.resp = new require('restify-errors').InternalServerError();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since people copy paste snippets all the time, we should hoist the require up to the top and then call new on the constructor. Additionally, I thought this was a response? Why is it now an error object?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Errors get serialized by res.__send

@@ -25,6 +25,7 @@ module.exports = {
serveStatic: require('./static'),
throttle: require('./throttle'),
urlEncodedBodyParser: require('./formBodyParser'),
inflightRequestThrottle: require('./inflightRequestThrottle'),
Copy link
Member

@yunong yunong Aug 3, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We generally sort these alphabetically -- so please do that. And yes it'd be awesome if we had a lint rule for this, but we don't yet :(

* server.
* @returns {Function} middleware to be registered on server.pre
*/
function inflightRequestThrottle (opts) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this class need a constructor? Generally, the pattern for plugins in restify has been to use a closure, and obviating the need for a constructor. Can we keep it consistent and do that here? We can of course, debate closure vs constructor trade offs at another point in time. Here's an example: https://github.com/restify/node-restify/blob/5.x/lib/plugins/metrics.js#L23

@retrohacker
Copy link
Member Author

@yunong one more time?

The module includes the following plugins to be used with restify's `pre` event:
* `inflightRequestThrottle(options)` - limits the max number of inflight requests
* `options.limit` {Number} the maximum number of inflight requests the server will handle before returning an error
* `options.res` {Error} An error that will be passed to `res.send` when the limit is reached.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry to go one more time on this -- now that I understand this is an actual error, can we just name it options.err instead? That makes it a lot more clear, at least to me. Thoughts? And totally my bad for not realizing this in the first place.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed, options.res doesn't feel intuitive.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yup, I totally agree. This grew organically since the original implementation expected this to be an array of parameters that would be passed to res.send.apply. Now that we expect a restify error, its an awkward fit.

var assert = require('chai').assert;
var restify = require('../../lib/index.js');
var restifyClients = require('restify-clients');
var P = restify.plugins.inflightRequestThrottle;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why P?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Line length in the tests, it stands for Plugin. I will try something else though, single letter variables feel wrong.

* `inflightRequestThrottle(options)` - limits the max number of inflight requests
* `options.limit` {Number} the maximum number of inflight requests the server will handle before returning an error
* `options.res` {Error} An error that will be passed to `res.send` when the limit is reached.
* `options.res.statusCode` {Number} The status code to return when the limit is reached.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In terms of StatusCode here -- I almost think we should have the user just pass in a restify error of their choosing, and eliminate having to side channel the status code here. If they want to override the status code, they can just pass in an error they construct which contains the statuscode.

var defaultResponse = new ServiceUnavailableError('resource exhausted');

/**
* inflightRequestThrottle
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You may want to specify both here and in docs that users should register this plugin first as order matters and you want the throttle check to occur as early as possible.

@retrohacker
Copy link
Member Author

@yunong one more time?

Copy link
Member

@yunong yunong left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good, nice work 👍

@retrohacker
Copy link
Member Author

❤️

@retrohacker retrohacker merged commit 285faf4 into 5.x Aug 7, 2017
@retrohacker retrohacker deleted the inflightRequestThrottle branch August 17, 2017 00:27
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants