ENbext.dev

Traduction automatique depuis l’anglais. Consulter l’original.

Sur cette page

Planificateur de tâches

bext comprend un planificateur de tâches intégré qui élimine le besoin de cron externes, de minuteries systemd ou de files d'attente de tâches tierces. Les tâches s'exécutent sur une tâche tokio dédiée au sein du processus serveur avec suivi de l'état persistant, prévention des chevauchements et surveillance des pannes.

Configuration#

Définir les tâches dans bext.config.toml :

toml
[[tasks]]
name = "cache_gc"
schedule = "*/5 * * * *"   # Every 5 minutes
handler = "cache::gc"

[[tasks]]
name = "warm_popular"
schedule = "10m"            # Every 10 minutes
handler = "cache::warm"
config = { routes = ["/", "/products", "/pricing"] }

[[tasks]]
name = "health_check"
schedule = "30s"            # Every 30 seconds
handler = "health::self_check"

[[tasks]]
name = "daily_report"
schedule = "@daily"         # Once per day
handler = "report::generate"
enabled = true

[[tasks]]
name = "cleanup_sessions"
schedule = "1h"             # Every hour
handler = "sessions::cleanup"
config = { max_age_hours = 24 }

Syntaxe du calendrier#

Le planificateur prend en charge trois formats de planification :

Intervalles simples#

Le format le plus courant : un nombre suivi d'une unité de temps :

Format Signification Exemple
Ns Toutes les N secondes 30s
Nm Toutes les N minutes 5m
Nh Toutes les N heures 2h

Expressions de type Cron#

Un sous-ensemble de la syntaxe cron standard est pris en charge :

Expression Signification
*/5 * * * * Toutes les 5 minutes
0 */3 * * * Toutes les 3 heures

Le planificateur analyse le modèle */N dans le champ des minutes (première position) et le modèle 0 */N dans le champ des heures (deuxième position).

Raccourcis#

Raccourci Équivalent
@hourly Toutes les 1 heure
@daily Toutes les 24 heures

Tous les intervalles doivent être supérieurs à zéro. Une planification de 0s ou 0m est rejetée au démarrage avec une erreur de configuration.

Gestionnaires intégrés#

Le bext est livré avec trois gestionnaires de tâches intégrés :

cache::gc#

Expulse les entrées expirées du cache ISR. S'exécute toutes les 5 minutes par défaut même sans configuration explicite.

toml
[[tasks]]
name = "cache_gc"
schedule = "*/5 * * * *"
handler = "cache::gc"

cache::warm#

Pré-rend les routes spécifiées dans le cache ISR, garantissant des réponses rapides pour les pages populaires après un déploiement ou un redémarrage.

toml
[[tasks]]
name = "warm_popular"
schedule = "10m"
handler = "cache::warm"
config = { routes = ["/", "/products", "/blog"] }

health::self_check#

Accède au point de terminaison /health du serveur et enregistre un avertissement si la réponse n'est pas 200. Utile pour détecter les états dégradés.

toml
[[tasks]]
name = "health_check"
schedule = "30s"
handler = "health::self_check"

Gestionnaires personnalisés#

Pour les tâches spécifiques à l'application, utilisez un nom de gestionnaire personnalisé. Les gestionnaires personnalisés sont résolus via le système de plugins ou l'API SDK :

toml
[[tasks]]
name = "sync_inventory"
schedule = "15m"
handler = "inventory::sync"
config = { source = "https://erp.internal/api/stock", timeout_ms = 30000 }

Enregistrez le gestionnaire dans votre plugin ou votre code côté serveur :

ts
import { registerTaskHandler } from "bext/tasks";

registerTaskHandler("inventory::sync", async (config) => {
  const response = await fetch(config.source, {
    signal: AbortSignal.timeout(config.timeout_ms),
  });
  const stock = await response.json();
  await updateInventoryCache(stock);
});

Prévention des chevauchements#

Le planificateur garantit qu'une tâche ne s'exécute jamais en même temps qu'elle-même. Si une exécution précédente est toujours en cours lorsque le tick suivant se déclenche, la tâche est ignorée pour ce tick. Cela évite les conflits de ressources et la corruption des données dans les tâches qui peuvent parfois s'exécuter plus longtemps que leur intervalle.

Le planificateur vérifie les tâches dues toutes les 30 secondes (configurable via tick_interval). Au premier tick après le démarrage, toutes les tâches activées qui n'ont jamais été exécutées sont immédiatement exécutées.

Gestion des échecs#

Lorsqu'un gestionnaire de tâches renvoie une erreur ou renvoie un résultat d'échec, le planificateur :

  1. Incrémente la métrique error_count de la tâche
  2. Enregistre l'erreur avec le nom de la tâche et le gestionnaire
  3. Marque l'exécution comme terminée (la tâche sera réessayée lors de son prochain intervalle planifié)

Pour les tâches qui nécessitent une logique de nouvelle tentative plus sophistiquée (interruption exponentielle, coupure de circuit), utilisez un flux durable au lieu d'une simple tâche planifiée.

Désactivation des tâches#

Définissez enabled = false pour désactiver une tâche sans supprimer sa configuration :

toml
[[tasks]]
name = "experimental_sync"
schedule = "1h"
handler = "experimental::sync"
enabled = false  # Paused — won't run until re-enabled

Les tâches désactivées n'apparaissent pas dans la liste des tâches dues et ne consomment aucune ressource.

Surveillance de l'état#

L'état de la tâche est disponible sur /__bext/admin/api/scheduler :

json
{
  "is_running": true,
  "tasks": [
    {
      "name": "cache_gc",
      "handler": "cache::gc",
      "enabled": true,
      "run_count": 288,
      "error_count": 0,
      "is_running": false,
      "interval_secs": 300
    },
    {
      "name": "warm_popular",
      "handler": "cache::warm",
      "enabled": true,
      "run_count": 144,
      "error_count": 2,
      "is_running": true,
      "interval_secs": 600
    }
  ]
}

Ces métriques sont également exportées au format Prometheus sous /__bext/metrics sous l'espace de noms bext_task_*.

Intégration avec des flux durables#

Pour les opérations complexes en plusieurs étapes, utilisez le planificateur de tâches comme déclencheur de flux durables :

toml
[[tasks]]
name = "monthly_billing"
schedule = "0 */24 * * *"    # Daily
handler = "flow::trigger"
config = { flow_id = "billing-cycle", input = { type = "monthly" } }

Cela combine le minuteur fiable du planificateur avec la récupération après incident, les politiques de nouvelle tentative et la logique de compensation du moteur de flux. Le planificateur déclenche le déclencheur et le flux durable gère indépendamment l'exécution en plusieurs étapes.

Tâches ponctuelles#

Pour les tâches qui doivent s'exécuter une seule fois au démarrage (migrations de bases de données, réchauffement initial du cache), définissez la planification sur un intervalle très long et comptez sur le comportement « exécuter immédiatement au premier tick » :

toml
[[tasks]]
name = "startup_migration"
schedule = "8760h"           # 1 year — effectively one-shot
handler = "db::migrate"

Vous pouvez également utiliser le hook de démarrage de votre plugin bext pour une initialisation véritablement unique.

Arrêt progressif#

Lorsque le serveur reçoit un signal d'arrêt ( SIGTERM ou SIGINT ), le planificateur cesse d'accepter de nouvelles exécutions de tâches. Les tâches en cours d'exécution bénéficient d'un délai de grâce pour être terminées avant la fin du processus. L'indicateur d'arrêt est propagé à tous les gestionnaires de tâches afin qu'ils puissent vérifier une terminaison anticipée.

Attention

Le planificateur vérifie les tâches dues toutes les toutes les 30 secondes (le tick_interval ). Une tâche avec un intervalle inférieur à 30 secondes (par exemple 5s ) ne se déclenchera pas plus d'une fois par cycle de tick. Utilisez une route API ou un agent de file d'attente pour les charges de travail de moins de 30 secondes.

Conseil

Pour les tâches qui nécessitent une logique de nouvelle tentative au-delà de « réessayer l'intervalle suivant » (comme une interruption exponentielle ou une compensation en cas d'échec), déclenchez un Flux durable à partir du gestionnaire de tâches au lieu de coder la logique de nouvelle tentative dans le gestionnaire lui-même.

En rapport#

Modifier cette page ↗Besoin d’aide ? ↗
Rechercher dans la documentation

Saisissez un sujet, une commande ou une question.