Principe de la simulation déterministe

celld est un runtime destiné aux Cloudflare Workers et aux Durable Objects, exécutable sur des machines locales. Son unique dépendance externe est un stockage objet compatible S3. Le projet repose sur une simulation déterministe (DST) qui exécute le code de production dans un environnement contrôlé. Le simulateur impose l’ordre d’exécution des événements – requêtes entrantes, réponses de stockage, déclenchements de minuteries – tout en conservant le même code de traitement que la version en production. En fixant la graine du générateur aléatoire, chaque exécution reproduit exactement la même séquence d’actions, d’états intermédiaires et de résultats.

Architecture du simulateur et contrôle des événements

Chaque cellule (cell) possède sa propre base SQLite. Le simulateur sépare le « sélecteur d’événement » du « gestionnaire d’événement ». Cette séparation permet au simulateur de choisir, à chaque pas, quelle action exécuter : délivrer une requête, lancer une tâche asynchrone, avancer le temps simulé ou finaliser une opération de stockage. Les réponses du stockage peuvent être retardées ou échouer volontairement, et le temps simulé peut avancer sans attendre le temps réel, ce qui rend possible le test de comportements dépendants du temps comme les retries ou les alarmes. Le processus de test s’appuie sur le pseudocode suivant :

const simulation = createSimulation({ settings, seed });
for (let step = 0; step < settings.maxActions; step++) {
  const candidates = simulation.availableActions();
  if (candidates.length === 0) {
    checkCompletion(simulation);
    break;
  }
  const action = simulation.choose(candidates);
  simulation.run(action);
  checkInvariants(simulation);
}

Après chaque action, un vérificateur évalue des invariants définis par les développeurs, garantissant que le système reste cohérent tout au long du scénario.

Analyse d’un bug d’alarme et reproduction

L’une des anomalies découvertes concerne la gestion des alarmes. Une alarme planifie l’exécution d’une cellule à une heure précise. Si la cellule est inactif, celld la décharge et la réactive via des « wake entries » stockés dans l’objet S3. Un bug est apparu lorsqu’un wake entry de 10 h 00 était réutilisé après la suppression de l’alarme correspondante et la création d’une nouvelle alarme à 10 h 05. Le simulateur a pu reproduire l’interleaving suivant : suppression de l’alarme 10 h 00, création de l’alarme 10 h 05, puis exécution tardive du nettoyage du wake entry 10 h 00. Cette séquence violait l’invariant qui stipule qu’un wake entry doit rester présent jusqu’à ce que l’alarme associée se déclenche ou soit annulée. Grâce à la graine identique, les développeurs ont pu rejouer exactement le même ordre d’événements et identifier le point de corruption de l’état.

Limites et perspectives

Le simulateur n’est pas encore publié dans le dépôt public de celld, ce qui restreint son accès aux contributeurs internes. De plus, la génération aléatoire d’actions repose sur un nombre maximal d’étapes (settings.maxActions) ; des scénarios plus longs pourraient rester inexplorés. Malgré ces contraintes, DST a déjà permis de détecter des bugs qui n’auraient pas émergé dans des tests classiques, notamment ceux liés aux interleavings de stockage et de temporisation. L’évolution prévue inclut l’exposition du simulateur aux utilisateurs externes et l’enrichissement des invariants pour couvrir davantage de propriétés de cohérence.