feat: add DAG engine issues (0007a-e) and feature flag
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>
This commit is contained in:
@@ -0,0 +1,109 @@
|
||||
# 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_match` son puras
|
||||
- `infra/` — `dag_ticker` es 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 index` y verificar IDs
|
||||
|
||||
---
|
||||
|
||||
## Ejemplo de uso
|
||||
|
||||
```go
|
||||
// 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_next` calcula correctamente la proxima ejecucion
|
||||
- [ ] `cron_match` coincide correctamente para el minuto actual
|
||||
- [ ] Ticker lanza DAGs en el momento correcto
|
||||
- [ ] Tests pasan
|
||||
- [ ] Indexado en registry.db
|
||||
Reference in New Issue
Block a user