Tâches Cron et Planification
IMPORTANT : Avant de faire quoi que ce soit, vous DEVEZ lire BASE_SKILL.md dans le répertoire de cette compétence. Il contient des conseils essentiels sur le débogage, la gestion des erreurs, la gestion d'état, le déploiement et la configuration du projet. Ces règles et patterns s'appliquent à tout travail RivetKit. Tout ce qui suit suppose que vous ayez déjà lu et compris ce document.
Exemples Fonctionnels
Si vous avez besoin d'une implémentation de référence, consultez le code d'exemple fonctionnel brut dans ces modèles :
Patterns pour exécuter des tâches cron durables et des tâches planifiées sur les Actors Rivet. Les planifications d'Actor sont des minuteurs persistants possédés par le moteur, donc une tâche conserve son délai limite à travers la mise en veille, les redémarrages, les mises à niveau, les déploiements et les crashs des actors.
Code de Démarrage
Commencez par l'exemple Scheduling fonctionnel sur GitHub. Il implémente un service de rappels avec des minuteurs uniques, un frontend React et des événements de déclenchement en direct.
L'API Scheduling
L'API complète est documentée dans Scheduling. Il y a deux méthodes, toutes deux disponibles sur le contexte de l'actor :
| Méthode | Comportement |
|---|---|
c.schedule.after(duration, actionName, ...args) |
Exécute l'action nommée après duration millisecondes. |
c.schedule.at(timestamp, actionName, ...args) |
Exécute l'action nommée à un timestamp d'époque exact en millisecondes. |
Propriétés clés :
- Durable : La planification est persistée par le moteur et le minuteur survit à la mise en veille, redémarrage, mise à niveau et crash de l'actor. Si l'actor est en veille à la limite, le moteur le réveille pour exécuter l'action. Voir Lifecycle.
- Actions ordinaires comme callbacks : Le callback planifié est une action ordinaire sur le même actor, invoquée par nom. Les arguments après le nom de l'action sont transmis positivement, par exemple
c.schedule.after(delayMs, "triggerReminder", reminder.id). - Pas d'API d'annulation : Rivet ne supporte pas actuellement l'annulation d'une action planifiée. Le pattern est une garde tombstone : supprimez l'entrée de l'état et faites que l'action planifiée ne fasse rien quand elle ne peut pas trouver son entrée. Les actions
cancelReminderettriggerReminderde l'exemple implémentent exactement cela.
Tâches Récurrentes Via Réarmement
c.schedule est unaire, donc les tâches récurrentes sont construites en ayant l'action planifiée se réarmer à la fin de chaque exécution :
import { actor } from "rivetkit";
const DAY_MS = 24 * 60 * 60 * 1000;
export const dailyReport = actor({
state: { lastRunAt: 0 },
actions: {
runReport: (c) => {
// Do the job's work, then record the run.
c.state.lastRunAt = Date.now();
// Re-arm the next run before returning.
c.schedule.after(DAY_MS, "runReport");
},
},
});
Armez la première exécution à partir de onCreate ou d'une action de configuration ; après cela, l'action maintient la chaîne vivante en se replanifiant elle-même.
Se réarmer avec after mesure la prochaine exécution à partir de la fin de la courante, donc la cadence dérive plus tard par le temps d'exécution du travail à chaque cycle. Si les exécutions doivent rester alignées sur une cadence fixe, réarmez plutôt avec c.schedule.at(c.state.lastRunAt + DAY_MS, "runReport").
L'exemple Scheduling lui-même n'utilise que des rappels uniques. Une implémentation réelle de réarmement se trouve dans l'actor monde inactif, où l'action collectProduction crédite la production, met à jour lastCollectedAt et appelle un helper scheduleCollection qui réarme avec c.schedule.after(delayMs, "collectProduction", { buildingId }). Il montre aussi la gestion du rattrapage : si l'action s'exécute tard, elle calcule combien d'intervalles entiers se sont écoulés depuis la dernière exécution et les crédite en un seul lot avant de réarmer.
Pour les tâches multi-étapes qui nécessitent des tentatives et un suivi de la progression dans une seule exécution, considérez les Workflows au lieu de chaîner les planifications.
Comparaison de Durabilité
| Approche | Durabilité du Minuteur | Mise à l'Échelle Horizontale | Déploiements et Redémarrages |
|---|---|---|---|
setTimeout / setInterval |
Mémoire en processus uniquement | Chaque réplica arme son propre minuteur, donc les tâches s'exécutent une fois par instance | Tous les minuteurs en attente sont perdus au redémarrage ou au crash |
node-cron et bibliothèques similaires |
Mémoire en processus uniquement | Chaque instance exécute la tâche sauf si vous ajoutez un verrouillage externe | La planification se réinitialise au déploiement ; les exécutions manquées pendant la panne sont ignorées |
| Service cron externe | Vit en dehors de votre app | Nécessite un endpoint HTTP public plus son propre état de dédupliquage et de tentative | Survit à vos déploiements mais est une infrastructure séparée à exploiter |
| Planification d'Actor Rivet | Persiste par le moteur en tant que minuteur durable | Exactement un actor par clé, donc le minuteur est armé une fois au lieu d'une fois par réplica | Survit à la mise en veille, redémarrage, mise à niveau et crash de l'actor |
Idempotence
Une action planifiée peut se déclencher plus que prévu : un crash entre la réalisation du travail et le réarmement peut faire que l'action s'exécute à nouveau, et parce que les planifications ne peuvent pas être annulées, une action peut se déclencher pour une entrée qui a déjà été supprimée. Concevez les handlers pour qu'un déclenchement dupliqué soit inoffensif :
- Stocker un marqueur d'exécution en état : Gardez un timestamp
lastRunAtou un numéro de séquence en état d'actor et mettez-le à jour dans l'action. À chaque déclenchement, calculez le temps écoulé depuis le marqueur et ignorez ou traitez par lot en conséquence. L'actioncollectProductionde l'actor monde inactif fait cela aveclastCollectedAtet le traitement par lot d'intervalles entiers. - Garde tombstone pour les entrées annulées : La
triggerReminderde l'exemple recherche le rappel dansc.state.remindersen premier et retourne avec un avertissement s'il a disparu, donc un déclenchement après annulation est une no-op sûre. - Garder le travail et les mises à jour de marqueur dans la même action : Les écritures d'état d'actor sont persistées avec l'action, donc mettre à jour le marqueur dans le même handler qui fait le travail maintient les deux cohérentes.
Topologie
| Topologie | À Utiliser Quand | Clé d'Exemple |
|---|---|---|
| Actor de tâche singleton | Une tâche globale comme un rapport nocturne ou un passage de nettoyage | job["daily-report"] |
| Actor par entité planifiée | Minuteurs par utilisateur ou par ressource tels que rappels, essais ou périodes de facturation | reminder[userId] |
L'exemple Scheduling utilise une clé reminderActor["main"] partagée unique pour la simplicité de la démo. Pour les systèmes de rappels de production, préférez un actor par utilisateur pour que les minuteurs, l'état et la charge soient isolés par entité. Voir Keys.
Exemple de Service de Rappels
| Sujet | Résumé |
|---|---|
| Planification | Minuteurs uniques armés avec c.schedule.after(delayMs, "triggerReminder", reminder.id) ou c.schedule.at(timestamp, "triggerReminder", reminder.id). |
| État | État JSON contenant reminders et completedCount ; l'action planifiée mute l'état quand elle se déclenche. |
| Événements | triggerReminder diffuse un événement reminderTriggered à tous les clients connectés. Voir Events. |
| Annulation | cancelReminder ne supprime que le rappel de l'état ; l'action planifiée peut encore se déclencher et faire une no-op via une garde de recherche d'état. |
Actors
- Clé :
reminderActor["main"] - Responsabilité : Stocke les rappels en état persistant, arme une future auto-action par rappel via
c.schedule, marque les rappels comme complétés quand l'action planifiée se déclenche, et diffusereminderTriggeredaux clients connectés. - Actions
scheduleReminderscheduleReminderAttriggerRemindergetReminderscancelRemindergetStats
- Queues
- Aucune
- État
- JSON
reminderscompletedCount
Lifecycle
sequenceDiagram
participant C as Client
participant R as reminderActor
participant E as Engine
C->>R: scheduleReminder(message, delayMs)
R->>E: schedule.after(delayMs, triggerReminder, id)
Note over E: schedule persisted + alarm armed
R-->>C: reminder
Note over R: actor sleeps
E->>R: alarm fires, actor wakes
Note over R: triggerReminder(id) runs
R-->>C: reminderTriggered event
Note over R: recurring jobs re-arm here with schedule.after
Liste de Sécurité
L'exemple est intentionnellement ouvert : tout client peut se connecter à la clé partagée ["main"] et planifier ou annuler n'importe quoi. Traitez tout ce qui suit comme des extensions requises pour la production :
- Valider les entrées de planification : Limitez
delayMsettimestampdes clients. Rejetez les délais négatifs, les timestamps dans le passé et les délais limite absurdement lointains, et limitez les tailles de message ou de payload. - Ne jamais planifier des actions choisies par le client : Exposez des actions spécifiques comme
scheduleReminderqui arment en interne un callback fixe. Ne passez pas un nom d'action fourni par le client ou des args non vérifiés dansc.schedule. - Authentifier et définir la portée des clés : Ajoutez l'authentification de connexion et utilisez des clés d'actor par utilisateur au lieu d'une clé globale unique, pour que les utilisateurs ne puissent pas lire ou annuler les planifications les uns des autres.
- Élaguer les entrées complétées : Le tableau
remindersde l'exemple croît sans limite. Supprimez ou archivez les entrées complétées pour que l'état reste petit. - Utiliser des IDs stables : Générez des IDs d'entrée avec
crypto.randomUUID()plutôt que des chaînes timestamp-plus-aléatoire.
Carte de Référence
Actors
- Contrôle d'Accès
- Actions
- Clés d'Actor
- Planification d'Actor
- Statuts d'Actor
- Actors Rivet Générés par IA et par Utilisateur
- Authentification
- Communication Entre Actors
- Connexions
- Onglets Inspecteur Personnalisés
- Débogage
- Patterns de Conception
- Destruction d'Actors
- Erreurs
- Gestionnaire Fetch et WebSocket
- Types d'Aide
- Icônes et Noms
- Paramètres d'Entrée
- Cycle de Vie
- Limites
- Gestionnaire de Requête HTTP Bas Niveau
- Stockage KV Bas Niveau
- Gestionnaire WebSocket Bas Niveau
- Métadonnées
- Démarrage Rapide Next.js
- Démarrage Rapide Node.js & Bun
- Queues & Boucles d'Exécution
- Démarrage Rapide React
- Temps Réel
- Démarrage Rapide Rust (Aperçu)
- Actor Sandbox
- Mise à l'Échelle & Concurrence
- Partage et Jointure d'État
- SQLite
- SQLite + Drizzle
- État & Stockage
- Test
- Dépannage
- Types
- API HTTP Vanille
- Versions & Mises à Niveau
- Workflows
Agent Os
- Communication Agent à Agent
- agentOS vs Sandbox
- Authentification
- Benchmarks
- Configuration
- Package Principal
- Tâches Cron
- Déploiement
- Passerelle LLM Intégrée
- Événements
- Système de Fichiers
- Limitations
- Identifiants LLM
- Multijoueur
- Mise en Réseau & Aperçus
- Aperçu
- Permissions
- Persistance & Mise en Veille
- Pi
- Processus & Shell
- Queues
- Démarrage Rapide
- Montage Sandbox
- Sécurité & Authentification
- Modèle de Sécurité
- Sessions
- Logiciel
- SQLite
- Prompt Système
- Outils
- Webhooks
- Automatisation de Workflow
Clients
Connect
- Déployer vers Amazon Web Services Lambda
- Déploiement vers AWS ECS
- Déploiement vers Cloudflare Workers
- Déploiement vers Freestyle
- Déploiement vers Google Cloud Run
- Déploiement vers Hetzner
- Déploiement vers Kubernetes
- Déploiement vers Railway
- Déploiement vers Rivet Compute
- Déploiement vers Supabase Functions
- Déploiement vers Vercel
- Déploiement vers VMs & Bare Metal
Cookbook
- Agent IA
- Espaces de Travail Agent IA
- Salon de Chat
- Éditeur de Texte Collaboratif
- Tâches Cron et Planification
- Base de Données par Locataire
- Déploiement de Rivet dans un VPC ou un Réseau Air-Gapped
- Curseurs et Présence en Direct
- Jeu Multijoueur
Général
- Configuration d'Actor
- Architecture
- Partage de Ressources Inter-Origines
- Documentation pour LLMs & IA
- Mise en Réseau Edge
- Endpoints
- Variables d'Environnement
- Serveur HTTP
- Journalisation
- Configuration de Pool
- Liste de Contrôle de Production
- Configuration de Registre
- Modes d'Exécution