Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

[REQUEST] Detect and report infinite loops / recursive or self-referential data loading #382

Open
@jwatzman

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!

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions

        AltStyle によって変換されたページ (->オリジナル) /