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

Aperta
#382 0 commenti 0 reazioni 0 assegnatari Vedi su GitHub

Nessuno ha ancora preso questa issue.

Valutazione

Difficoltà
5/5
Tempo stimato
Più di una settimana
Idoneità per principianti
25/100
Tipo di issue
Funzionalità
Chiarezza
Abbastanza chiara
Stato di attività
Ferma
Stack tecnologico
javascript, node.js
Ambito
backend

Direzione di ricerca

Inizia con il flusso di DataLoader.load e il comportamento della funzione di batch mostrati nei due esempi. Riproduci sia il load autoreferenziale sia il caso di dipendenza all'interno dello stesso batch, quindi determina come potrebbero essere segnalati senza modificare il normale comportamento di caricamento. Il lavoro è completato quando questi blocchi producono un avviso o un errore utile che aiuti a identificare la relazione di load problematica.

Scritto dal modello di indicizzazione a partire dal testo della issue.

Descrizione

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!

Lingua principale
JavaScript
Stelle
13.4k
Fork
522
Metriche di merge delle PR
Nessuna PR unita negli ultimi 30g

Preparare l'ambiente

Come iniziare

  1. Leggi tutta la issue e poi la guida ai contributi del progetto.
  2. Commenta sulla issue per dire che te ne occupi tu — evita che due persone facciano lo stesso lavoro.
  3. Fai un fork del repository e lavora su un branch.
  4. Apri una pull request che faccia riferimento al numero della issue.

Altre issue di graphql/dataloader

Tutte le issue di graphql/dataloader

Issue simili

Altre issue su JavaScript

Ricevi le nuove issue nella tua casella

Un breve riepilogo di issue GitHub adatte ai principianti.