Skip to content

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

@jwatzman

Description

@jwatzman

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