Skip to content

Commit 42acd25

Browse files
authored
Merge pull request #351 from Yelp/add_maxBatchSize_config_option
Add max batch size config option
2 parents f1ff4c4 + 732f246 commit 42acd25

File tree

4 files changed

+127
-2
lines changed

4 files changed

+127
-2
lines changed

API_DOCS.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -188,6 +188,7 @@ Describes the shape and behaviour of the resources object you will pass to `getL
188188
| `isBatchKeyASet` | (Optional) Set to true if the interface of the resource takes the batch key as a set (rather than an array). For example, when using a generated clientlib based on swagger where `uniqueItems: true` is set for the batchKey parameter. Default: false. |
189189
| `propertyBatchKey` | (Optional) The argument to the resource that represents the optional properties we want to fetch. (e.g. usually 'properties' or 'features'). |
190190
| `responseKey` | (Non-optional when propertyBatchKey is used) The key in the response objects corresponds to `batchKey`. This should be the only field that are marked as required in your swagger endpoint response, except nestedPath. |
191+
| `maxBatchSize` | (Optional) Limits the number of items that can be batched together in a single request. When more items are requested than this limit, multiple requests will be made. This can help prevent hitting URI length limits or timeouts for large batches. |
191192
192193
### `typings`
193194

__tests__/implementation.test.js

Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1246,6 +1246,127 @@ test('bail if errorHandler does not return an error', async () => {
12461246
});
12471247
});
12481248

1249+
test('batch endpoint with maxBatchSize', async () => {
1250+
const config = {
1251+
resources: {
1252+
foo: {
1253+
isBatchResource: true,
1254+
docsLink: 'example.com/docs/bar',
1255+
batchKey: 'foo_ids',
1256+
newKey: 'foo_id',
1257+
responseKey: 'id',
1258+
maxBatchSize: 3,
1259+
},
1260+
},
1261+
};
1262+
1263+
// Track each batch of IDs that the resource function receives
1264+
const receivedBatches = [];
1265+
1266+
const resources = {
1267+
foo: ({ foo_ids, properties }) => {
1268+
receivedBatches.push([...foo_ids]);
1269+
return Promise.resolve(
1270+
foo_ids.map(id => ({
1271+
id,
1272+
name: `name-${id}`,
1273+
rating: id + 1
1274+
}))
1275+
);
1276+
},
1277+
};
1278+
1279+
await createDataLoaders(config, async (getLoaders) => {
1280+
const loaders = getLoaders(resources);
1281+
1282+
// Request 5 items at once, which should be split by maxBatchSize later in the test.
1283+
const results = await Promise.all([
1284+
loaders.foo.load({ foo_id: 1, properties: ['name', 'rating'] }),
1285+
loaders.foo.load({ foo_id: 2, properties: ['name', 'rating'] }),
1286+
loaders.foo.load({ foo_id: 3, properties: ['name', 'rating'] }),
1287+
loaders.foo.load({ foo_id: 4, properties: ['name', 'rating'] }),
1288+
loaders.foo.load({ foo_id: 5, properties: ['name', 'rating'] }),
1289+
]);
1290+
1291+
// Verify that all results were returned correctly
1292+
expect(results).toEqual([
1293+
{ id: 1, name: 'name-1', rating: 2 },
1294+
{ id: 2, name: 'name-2', rating: 3 },
1295+
{ id: 3, name: 'name-3', rating: 4 },
1296+
{ id: 4, name: 'name-4', rating: 5 },
1297+
{ id: 5, name: 'name-5', rating: 6 },
1298+
]);
1299+
1300+
// Verify that the requests were batched correctly
1301+
expect(receivedBatches.map(batch => batch.length)).toEqual([3, 2]);
1302+
1303+
// Verify that all IDs were requested
1304+
const allRequestedIds = receivedBatches.flat().sort();
1305+
expect(allRequestedIds).toEqual([1, 2, 3, 4, 5]);
1306+
});
1307+
});
1308+
1309+
test('batch endpoint with propertyBatchKey and maxBatchSize', async () => {
1310+
const config = {
1311+
resources: {
1312+
foo: {
1313+
isBatchResource: true,
1314+
docsLink: 'example.com/docs/bar',
1315+
batchKey: 'foo_ids',
1316+
newKey: 'foo_id',
1317+
propertyBatchKey: 'properties',
1318+
responseKey: 'id',
1319+
maxBatchSize: 2,
1320+
},
1321+
},
1322+
};
1323+
1324+
// Track each batch of IDs that the resource function receives
1325+
const receivedBatches = [];
1326+
1327+
const resources = {
1328+
foo: ({ foo_ids, properties }) => {
1329+
receivedBatches.push([...foo_ids]);
1330+
return Promise.resolve(
1331+
foo_ids.map(id => ({
1332+
id,
1333+
name: `name-${id}`,
1334+
rating: id + 1
1335+
}))
1336+
);
1337+
},
1338+
};
1339+
1340+
await createDataLoaders(config, async (getLoaders) => {
1341+
const loaders = getLoaders(resources);
1342+
1343+
// Request 5 items at once, which should be split by maxBatchSize later in the test.
1344+
const results = await Promise.all([
1345+
loaders.foo.load({ foo_id: 1, properties: ['name', 'rating'] }),
1346+
loaders.foo.load({ foo_id: 2, properties: ['name', 'rating'] }),
1347+
loaders.foo.load({ foo_id: 3, properties: ['name', 'rating'] }),
1348+
loaders.foo.load({ foo_id: 4, properties: ['name', 'rating'] }),
1349+
loaders.foo.load({ foo_id: 5, properties: ['name', 'rating'] }),
1350+
]);
1351+
1352+
// Verify that all results were returned correctly
1353+
expect(results).toEqual([
1354+
{ id: 1, name: 'name-1', rating: 2 },
1355+
{ id: 2, name: 'name-2', rating: 3 },
1356+
{ id: 3, name: 'name-3', rating: 4 },
1357+
{ id: 4, name: 'name-4', rating: 5 },
1358+
{ id: 5, name: 'name-5', rating: 6 },
1359+
]);
1360+
1361+
// Verify that the requests were batched correctly
1362+
expect(receivedBatches.map(batch => batch.length)).toEqual([2, 2, 1]);
1363+
1364+
// Verify that all IDs were requested
1365+
const allRequestedIds = receivedBatches.flat().sort();
1366+
expect(allRequestedIds).toEqual([1, 2, 3, 4, 5]);
1367+
});
1368+
});
1369+
12491370
test('batch endpoint (multiple requests) with propertyBatchKey', async () => {
12501371
const config = {
12511372
resources: {

src/config.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ export interface BatchResourceConfig {
2828
isBatchKeyASet?: boolean;
2929
propertyBatchKey?: string;
3030
responseKey?: string;
31+
maxBatchSize?: number;
3132
}
3233

3334
export interface NonBatchResourceConfig {

src/implementation.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -416,7 +416,8 @@ function getBatchLoader(resourceConfig: BatchResourceConfig, resourcePath: Reado
416416
* flow errors :(
417417
*/ ''
418418
}
419-
...cacheKeyOptions
419+
...cacheKeyOptions,
420+
${resourceConfig.maxBatchSize ? `maxBatchSize: ${resourceConfig.maxBatchSize},` : ''}
420421
}
421422
)`;
422423
}
@@ -514,7 +515,8 @@ function getPropertyBatchLoader(resourceConfig: BatchResourceConfig, resourcePat
514515
* flow errors :(
515516
*/ ''
516517
}
517-
...cacheKeyOptions
518+
...cacheKeyOptions,
519+
${resourceConfig.maxBatchSize ? `maxBatchSize: ${resourceConfig.maxBatchSize},` : ''}
518520
}
519521
)`;
520522
}

0 commit comments

Comments
 (0)