Skip to content
This repository has been archived by the owner on Apr 20, 2019. It is now read-only.

Add optional "forceSequential" flag to batch request payload. #86

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -57,3 +57,14 @@ Optionally you can assign the query as a third property rather than placing it d
```

If an error occurs as a result of one the requests to an endpoint it will be included in the response in the same location in the array as the request causing the issue. The error object will include an error property that you can interrogate. At this time the response is a 200 even when a request in the batch returns a different code.

By default, requests in the `"requests"` array will be run concurrently, with the exception of pipelined requests which will be run sequentially.
To force all batched requests to run sequentially regardless of pipelining, pass in the `forceSequential: true` flag:

```json
{ "forceSequential": true,
"requests": [
{"method": "get", "path": "/users/1"},
{"method": "get", "path": "/users/2"}
] }
```
10 changes: 6 additions & 4 deletions lib/batch.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ module.exports.config = function (settings) {
results: [],
resultsMap: []
};
const { forceSequential } = request.payload;

let errorMessage = null;

Expand Down Expand Up @@ -71,7 +72,7 @@ module.exports.config = function (settings) {
}

try {
await internals.process(request, requests, payloads, resultsData);
await internals.process(request, requests, payloads, resultsData, forceSequential);
}
catch (err) {
// console.log("ERROR ", err);
Expand All @@ -89,23 +90,24 @@ module.exports.config = function (settings) {
path: Joi.string().required(),
query: [Joi.object().unknown().allow(null),Joi.string()],
payload: [Joi.object().unknown().allow(null),Joi.string()]
}).label('BatchRequest')).min(1).required()
}).label('BatchRequest')).min(1).required(),
forceSequential: Joi.bool().label('ForceSequential').optional().default(false)
}).required().label('BatchRequestPayload')
},
auth: settings.auth,
tags: settings.tags
};
};

internals.process = async function (request, requests, payloads, resultsData) {
internals.process = async function (request, requests, payloads, resultsData, forceSequential) {

const fnsParallel = [];
const fnsSerial = [];

requests.forEach((requestParts, idx) => {

const payloadParts = payloads[idx];
if (internals.hasRefPart(requestParts) || payloadParts.length) {
if (forceSequential || internals.hasRefPart(requestParts) || payloadParts.length) {
return fnsSerial.push(
async () => await internals.batch(request, resultsData, idx, requestParts, payloadParts)
);
Expand Down
11 changes: 11 additions & 0 deletions test/batch.js
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,17 @@ describe('Batch', () => {
expect(res[1].name).to.equal('Active Item');
});

it('handles sequential requests with forceSequential', async () => {

const res = await Internals.makeRequest(server, '{ "forceSequential": true, "requests": [{"method": "get", "path": "/sequential"}, {"method": "get", "path": "/sequential"}, {"method": "get", "path": "/sequential"}, {"method": "get", "path": "/sequential"}] }');

expect(res.length).to.equal(4);
expect(res[0]).to.equal(1);
expect(res[1]).to.equal(2);
expect(res[2]).to.equal(3);
expect(res[3]).to.equal(4);
});

it('supports piping a response into the next request', async () => {

const res = await Internals.makeRequest(server, '{ "requests": [ {"method": "get", "path": "/item"}, {"method": "get", "path": "/item/$0.id"}] }');
Expand Down
26 changes: 25 additions & 1 deletion test/internals.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,14 @@
const Hapi = require('hapi');
const Bassmaster = require('../');

const awaitDelay = function (ms) {

return new Promise((resolve) => {

return setTimeout(resolve, ms);
});
};

const profileHandler = function (request, h) {

const id = request.query.id || 'fa0dbda9b1b';
Expand Down Expand Up @@ -171,6 +179,21 @@ const echoHandler = function (request, h) {
return request.payload;
};

const sequentialHandler = async function (request, h) {

if (!sequentialHandler.callCount) {
sequentialHandler.callCount = 1;

await awaitDelay(500);

return sequentialHandler.callCount;
}

await awaitDelay(Math.floor(Math.random() * 100));
Copy link

Choose a reason for hiding this comment

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

I think in a perfect world the sequential handlers would count down: the first 40ms, the second 30ms, etc.

Copy link
Author

Choose a reason for hiding this comment

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

Yeah, that's a good idea. I just wanted to make sure the first definitely was by far the longest, and added some response time jitter just to keep it interesting :P

The main portion was the first -> second calls since .callCount would definitely 100% be incremented by then. I'll go ahead and do the delay decrement though. It's a good idea 👍

Copy link

Choose a reason for hiding this comment

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

Oh!! I actually missed that finer point. I think ultimately we're good either way :)


return ++sequentialHandler.callCount;
};

module.exports.setupServer = async function () {

const server = new Hapi.Server();
Expand Down Expand Up @@ -205,7 +228,8 @@ module.exports.setupServer = async function () {
]
}
},
{ method: 'GET', path: '/redirect', handler: redirectHandler }
{ method: 'GET', path: '/redirect', handler: redirectHandler },
{ method: 'GET', path: '/sequential', handler: sequentialHandler }
]);

await server.register(Bassmaster);
Expand Down