I recently found that because I have asynchronous calls being made in some of my shield rules, it's causing my dataloader batch functions to be called multiple times, when they should only be called once, which leaves me with the N+1 problem.
I believe this is due to how the dataloader
library requires that all of the batch function calls occur during the same event loop "tick" (source), and the asynchronous calls made in my shield rules are preventing this.
Here's the useFactory
function that I pass to GraphQLModule
in my app:
useFactory(dataloaderService: DataloaderService, usersService: UsersService) {
return {
context: async ({ req }: { req: Request }) => {
const loaders = dataloaderService.getLoaders();
const services = { usersService };
return { loaders, services };
},
transformSchema: (schema: GraphQLSchema) => {
schema = applyMiddleware(schema, shieldPermissions);
return schema;
},
};
},
Is there any way to use both dataloaders and an authorization library like GraphQL Shield at the same time? Is there anything wrong with my current configuration that could be causing this?
Passing an option for batchScheduleFn
resolved the issue:
private _createGroupsLoader() {
return this._getDataLoader<number, Group>(
async (groupIds) =>
this.groupsService.getGroupsBatch(groupIds as number[]),
{
batchScheduleFn: (callback) => setTimeout(callback, 5),
}
);
}
Without passing batchScheduleFn
, dataloader uses it's own scheduler internally that either uses process.nextTick
or setImmediate
.
Reason for using 5 ms:
Our current heuristic is to yield execution back to the main thread every 5ms. 5ms isn't a magic number, the important part is that it's smaller than a single frame even on 120fps devices, so it won't block animations.
Source: https://dev.to/tsirlucas/integrating-dataloader-with-concurrent-react-53h1