ISR imbriqué (couplage des TTL)

ISR de page (TTL 5 s) enveloppant un fragment <ISR ttl=30s>. L'externe se renouvelle toutes les 5 secondes ; le cache interne survit à plusieurs cycles externes car son TTL est plus long. Rechargez plusieurs fois : l'horodatage externe s'actualise toutes les ~5 s, l'interne reste figé ~30 s.

Astuce
La règle d'or : le TTL du fragment interne doit toujours être <strong>supérieur ou égal</strong> à celui de la page externe. Si l'interne est plus court, il expirera avant chaque miss de page — le cache interne ne fera jamais de HIT et ne servira à rien. Vérifiez vos cadences avec les en-têtes <code>x-bext-cache</code> et <code>x-bext-render-count</code> en mode dev.

Résultat

Externe (ISR page ttl=5s): 2026-06-21T08:46:01.759Z

Inner (fragment ISR ttl=30s): 2026-06-21T08:46:01.860Z

Lecture des cadences

  • 0 s : démarrage à froid — les deux horodatages sont capturés frais.
  • 0–4 s : chaque rechargement touche le cache de page ; les deux horodatages sont figés.
  • 5 s : le cache de page expire → re-rendu. L'horodatage externe se met à jour. Le fragment ISR est consulté : âge = 5 s, TTL = 30 s → HIT ; le HTML mis en cache à t=0 est réutilisé. La page externe est re-stockée.
  • 10 s, 15 s, 20 s, 25 s : même schéma — l'externe s'actualise, l'interne reste à t=0.
  • 30 s : l'externe a de nouveau expiré, l'interne est consulté à âge=30 s vs TTL=30 s → EXPIRÉ → re-rendu avec un horodatage frais.

Le cache interne rentabilise son existence : entre t=5 s et t=29 s, la fonction enfant lente s'exécute zéro fois alors que la page externe se renouvelle 5 fois.

Anti-pattern : si vous inversiez les TTL (externe=30 s, interne=5 s), l'interne expirerait bien avant le renouvellement de l'externe. Chaque miss externe serait aussi un miss interne, donc le cache interne ne ferait jamais de HIT — un poids mort. La matrice d'interopérabilité dans plan/granular-isr-streaming/ signale ce cas à éviter.

src/app/examples/nested-isr/page.tsxTSX
import { ISR } from "@bext-stack/framework/cache";

// Page-level ISR: 5 second TTL.
export const revalidate = 5;

export default function Page() {
  const outer = new Date().toISOString();
  return (
    <div>
      <p>Outer (page ISR ttl=5s): {outer}</p>
      <ISR cacheKey="nested:inner" ttl={30}>
        {async () => {
          // Slow render — the value of the ISR cache. Skipped on
          // page-cache hit; consulted on page-cache miss; only
          // re-runs when its own 30 s TTL expires.
          await new Promise(r => setTimeout(r, 100));
          return `<p>Inner (fragment ISR ttl=30s): ${new Date().toISOString()}</p>`;
        }}
      </ISR>
    </div>
  );
}