-
Notifications
You must be signed in to change notification settings - Fork 517
Description
If you accidentally create a self-referential DataLoader
load, the loader silently creates a loop of promises which never resolve. This is obviously a programmer error, but it can be pretty hard to diagnose when things just silently don't work, if the recursive load isn't obvious across a complex codepath.
This code demonstrates the issue:
import DataLoader from 'dataloader';
const dumbDataLoader = new DataLoader(
async nums => await Promise.all(nums.map(async n => await dumbDataLoader.load(n)))
);
console.log(await dumbDataLoader.load(3))
I'd expect to get some sort of warning or error from DataLoader to try to pin down the issue, but instead I only get this warning from Node (which in practise won't pop up for a more complex server):
Warning: Detected unsettled top-level await at index.js:7
console.log(await doubleDataLoader.load(3))
Worse, a variation of this can pop up even when the loads are technically not infinitely recursive, since you can have one batch waiting on a result from the same batch. Consider this code:
import DataLoader from 'dataloader';
const factDataLoader = new DataLoader(
async nums => await Promise.all(nums.map(async n => {
if (n == 0) return 1;
return n * (await factDataLoader.load(n - 1));
}))
);
// This works:
console.log(await factDataLoader.load(0));
console.log(await factDataLoader.load(1));
factDataLoader.clearAll();
// This does not:
console.log(await Promise.all([
factDataLoader.load(0),
factDataLoader.load(1),
]));
That isn't actually infinitely recursive, but because 0
and 1
end up in the same batch, and 1
depends on 0
, and the result of 0
can't get to the computation around 1
because they're waiting on the same batch... this ends up hanging too. This is basically the same issue, but it's really hard to debug!
This example might look contrived, but this hit me in real-world code. My data loaders do data fetching and complex privacy checks. I have arbitrarily-nested posts, and you can only see a post if you can load the post's parent. If you try to load a post and its parent in the same batch of a data loader, it hangs without any error or warning!