d9b448a07b
Desglose del sistema de orquestacion propio para reemplazar Dagu: - 0007a: core puro (parse, validate, topo sort) - 0007b: process manager (spawn, wait, kill) - 0007c: execution store (SQLite) - 0007d: scheduler (cron parser, ticker) - 0007e: app CLI que compone todo Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
3.5 KiB
3.5 KiB
0007d — Scheduler: cron parser y ticker
Metadata
| Campo | Valor |
|---|---|
| ID | 0007d |
| Estado | pendiente |
| Prioridad | media |
| Tipo | feature |
Dependencias
| ID | Título | Estado | Requerido |
|---|---|---|---|
| 0007a | Funciones core del DAG engine | pendiente | Si |
| 0007c | Execution store | pendiente | Si |
Bloqueada por: #0007a, #0007c
Desbloquea: #0007e
Objetivo
Funciones para parsear expresiones cron, calcular proximas ejecuciones, y un ticker que dispara DAGs segun su schedule. Es lo que reemplaza el scheduler de Dagu.
Contexto
- Las expresiones cron de Dagu son estandar (5 campos: min hour dom mon dow)
- El ticker es un loop infinito que cada minuto evalua que DAGs deben lanzarse
- Funciones puras para parseo y calculo, impura solo el ticker
Arquitectura
functions/core/
├── cron_parse.go — NEW: string → CronExpression
├── cron_parse.md
├── cron_next.go — NEW: CronExpression + time → proxima ejecucion
├── cron_next.md
├── cron_match.go — NEW: CronExpression + time → bool (coincide?)
├── cron_match.md
functions/infra/
├── dag_ticker.go — NEW: loop que evalua schedules y lanza DAGs
├── dag_ticker.md
types/core/
├── cron_expression.md — NEW: minute, hour, dom, month, dow (cada uno []int o wildcard)
Patron pure core / impure shell
core/—cron_parse,cron_next,cron_matchson purasinfra/—dag_tickeres impuro (time.Sleep, lanza ejecuciones)
Tareas
Fase 1: Tipos
- 1.1 Definir
CronExpression— campos parseados con soporte para , ranges (1-5), lists (1,3,5), intervals (/5)
Fase 2: Funciones puras
- 2.1
cron_parse— "0 9 * * *" → CronExpression. Soportar: *, N, N-M, N/M, listas - 2.2
cron_next— dada una CronExpression y un time.Time, retorna el proximo time.Time que coincide - 2.3
cron_match— dada una CronExpression y un time.Time, retorna true si coincide (para el ticker) - 2.4 Tests exhaustivos: wildcards, ranges, listas, intervalos, edge cases (fin de mes, febrero)
Fase 3: Ticker
- 3.1
dag_ticker— recibe lista de (DagDefinition, path), cada minuto evalua cron_match para cada uno, lanza los que coinciden - 3.2 Soporte para cancelacion (context.Context) y graceful shutdown
Fase 4: Cleanup
fn indexy verificar IDs
Ejemplo de uso
// Puro
expr, _ := cron_parse("*/5 9-17 * * 1-5") // cada 5 min, 9-17h, lun-vie
next := cron_next(expr, time.Now()) // proxima ejecucion
matches := cron_match(expr, time.Now()) // true si ahora coincide
// Impuro (el ticker)
ctx, cancel := context.WithCancel(context.Background())
dag_ticker(ctx, dags, executor) // loop infinito hasta cancel
Decisiones de diseno
- No usar libreria cron externa: las expresiones son simples, implementar desde cero es ~100 lineas y evita dependencias
- Separar parse/next/match: parse es costoso, match es barato — parsear una vez, match cada minuto
- Ticker como funcion, no como goroutine: el caller decide como lanzarlo
Criterios de aceptacion
- Parsea todas las expresiones cron de los DAGs existentes en
~/dagu/dags/ cron_nextcalcula correctamente la proxima ejecucioncron_matchcoincide correctamente para el minuto actual- Ticker lanza DAGs en el momento correcto
- Tests pasan
- Indexado en registry.db