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

Abierto
#382 0 comentarios 0 reacciones 0 asignados Ver en GitHub

Nadie ha tomado este issue todavía.

Evaluación

Dificultad
5/5
Tiempo estimado
Más de una semana
Aptitud para principiantes
25/100
Tipo de issue
Nueva funcionalidad
Claridad
Bastante claro
Estado de actividad
Estancado
Stack tecnológico
javascript, node.js
Área
backend

Línea de trabajo

Comienza con el flujo de DataLoader.load y el comportamiento de la función de batch que se muestran en los dos ejemplos. Reproduce tanto el load autorreferencial como el caso de dependencia dentro del mismo batch y, a continuación, determina cómo podrían notificarse sin cambiar el comportamiento normal de carga. Se considera terminado cuando estos bloqueos producen una advertencia o un error útil que ayude a identificar la relación de load problemática.

Escrito por el modelo de indexación a partir del texto del issue.

Descripción

enhancement

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!

Lenguaje dominante
JavaScript
Estrellas
13.4k
Forks
522
Métricas de merge de PR
Sin PR fusionados en 30 d

Preparar el entorno

Primeros pasos

  1. Lee el issue completo y luego la guía de contribución del proyecto.
  2. Comenta en el issue que vas a ocuparte — evita que dos personas hagan lo mismo.
  3. Haz un fork del repositorio y trabaja en una rama.
  4. Abre un pull request que haga referencia al número del issue.

Más de graphql/dataloader

Todos los issues de graphql/dataloader

Issues similares

Más issues de JavaScript

Recibe los nuevos issues en tu correo

Un resumen breve de issues de GitHub para principiantes.