Comment nous avons divisé les démarrages à froid par deux

Un démarrage à froid, c’est le temps entre l’arrivée d’une requête et le moment où une nouvelle instance est prête à y répondre. Pour les services qui descendent à zéro instance, c’est la première chose que ressentent les utilisateurs. Au cours du dernier trimestre, nous avons fait passer le démarrage à froid médian d’un service Node de 410 ms à 190 ms. Cet article présente les trois changements qui ont fait l’essentiel du travail.

Mesurer avant de changer quoi que ce soit

Nous avons découpé chaque démarrage à froid en quatre phases, chacune journalisée : planification, téléchargement de l’image, démarrage du runtime et initialisation de l’application. Le résultat nous a surpris. Les images étaient déjà en cache sur la plupart des hôtes, mais l’initialisation de l’application, le temps que votre propre code passe à importer des modules et à ouvrir des connexions, représentait presque la moitié du total.

répartition du démarrage à froid (p50, avant)
planification     38 ms
image             52 ms
runtime          131 ms
application      189 ms
total            410 ms

Prendre un instantané du runtime après le démarrage

Le démarrage du runtime refait le même travail pour chaque instance d’un même build. Nous prenons désormais un instantané mémoire juste après ce démarrage, nous le stockons avec le build, puis nous le restaurons au démarrage à froid suivant. La restauration prend environ 30 ms, contre 131 ms pour un démarrage complet.

Les instantanés sont pris par build : un nouveau déploiement ne restaure jamais l’état d’un ancien.

Charger les modules à leur première utilisation

Beaucoup de services importent tous leurs gestionnaires de routes au démarrage, alors qu’une requête n’en utilise qu’un seul. Le runtime prend maintenant en charge les modules de route différés : le routeur sait quel fichier gère chaque chemin, et ne l’importe qu’à la première requête sur ce chemin.

server.ts
import { createServer } from "@cloud/runtime";

createServer({
  routes: {
    "/api/invoices": () => import("./routes/invoices"),
    "/api/reports": () => import("./routes/reports"),
  },
});

Envoyer la première requête à une instance déjà chaude

Le dernier changement concerne le routeur. Pendant qu’une nouvelle instance démarre, la première requête part vers une instance chaude qui a de la capacité, et la nouvelle instance ne reçoit du trafic qu’une fois prête. Les utilisateurs n’attendent plus de démarrage à froid lors d’une montée en charge, seulement lors d’un départ de zéro.

Résultats

Le démarrage à froid médian est maintenant de 190 ms, et le p95 de 420 ms, contre 980 ms auparavant. Les trois changements sont actifs par défaut. Les modules de route différés nécessitent le runtime 2.4 ou plus récent.

Partager :

Écrit par

Daniel Okafor

Ingénieur principal, Runtime

Daniel travaille sur le runtime et la couche base de données. Daniel écrit sur les démarrages à froid, les migrations et la mesure des performances en production.

Newsletter

Nos notes d'ingénierie, une fois par mois

Les nouveautés marquantes, des analyses de l'équipe plateforme et des guides utilisables le jour même. Sans spam.

gats-fo-lex

Désinscription à tout moment. Consultez notre politique de confidentialité.

Offre gratuite · Sans carte bancaire

Poussez votre code aujourd'hui. En ligne avant que votre café refroidisse.

Connectez un dépôt, choisissez une région et obtenez une URL de production avec HTTPS, logs et autoscaling déjà activés.

$ git push origin main

  1. Build34 s
  2. Déploiement12 s
  3. Contrôles de santé3 s

your-app.example.com

Buy NowTheme Details