Skip to main content

Command Palette

Search for a command to run...

Time-based vs Event-driven: Por qué no es solo cron vs webhooks

Updated
5 min read
Time-based vs Event-driven: Por qué no es solo cron vs webhooks
A

Platform Engineer + CNCF Ambassador, AWS community builder. I design scalable cloud-native platforms and love making teams faster and safer.

Ayer: qué orquestador usar.
Hoy: cuándo ejecutar ese orquestador.

El setup más común que veo: ( No sean asi, xD quieran a su PE)

# En el orquestador de tu elección
@schedule("0 2 * * *")  # Todos los días 2am
def train_model():
    data = fetch_data()
    model = train(data)
    deploy(model)

Pregunta simple: ¿Por qué 2am? ¿Por qué todos los días?

Respuesta común: Porque... ¿ventana de bajo tráfico?

Pero la pregunta real es:

"¿Qué evento justifica gastar $X en compute para re-entrenar?"

Porque re-entrenar cuesta:

  • Compute (GPUs no son gratis)

  • Tiempo (tu pipeline bloquea recursos)

  • Riesgo (cada deploy es un posible incidente)

Si nada cambió, ¿para qué re-entrenar?


Los 3 triggers legítimos:

1. Datos nuevos

# Llegaron 10K nuevas transacciones
# El modelo PUEDE aprender algo nuevo
if new_data.count >= 10000:
    trigger_training()

2. Degradación detectada

# El modelo está fallando en prod
# El accuracy bajó 5%
if current_accuracy < baseline - 0.05:
    trigger_emergency_training()

3. Código/features cambiaron

# Agregaste un nuevo feature
# Cambiaste hiperparámetros
if code_changed() or features_changed():
    trigger_ci_training()

Si ninguno de estos 3 pasó, probablemente no necesitas re-entrenar.


Pattern 1: Time-based (Batch)

# Airflow, Prefect, Kubeflow, etc.
@schedule("@weekly")
def training_pipeline():
    train_model()

Trade-offs:

ProsContras
Simple de implementarPuede ser desperdicio
PredecibleNo responde a cambios reales
Fácil de debuggearPuede entrenar con datos malos
Conocido por equiposLatencia hasta próximo run

Úsalo cuando:

  • Tus datos llegan en schedule predecible

  • El costo de entrenar es bajo

  • Prefieres simplicidad sobre optimización

No lo uses cuando:

  • Los datos llegan irregularmente

  • Re-entrenar es muy caro

  • Necesitas respuesta inmediata a cambios


Pattern 2: Event-driven

# Trigger por evento externo
@trigger(bucket="s3://data", event="new_file")
def training_pipeline():
    if new_data_meets_threshold():
        train_model()

Trade-offs:

ProsContras
Solo entrenas cuando necesitasMás complejo de implementar
Optimiza cómputoDebugging más difícil
Responde a eventos realesNecesitas infraestructura de eventos
Escala mejorPuede disparar demasiado

Úsalo cuando:

  • Los datos llegan irregularmente

  • Re-entrenar es caro (>$100/run)

  • Tienes infraestructura de eventos (S3, Kafka, etc.)

No lo uses cuando:

  • Tu equipo no tiene experiencia con eventos

  • Los datos llegan consistentemente

  • Prefieres simplicidad


Pattern 3: Metric-driven

@schedule("@hourly")  # Check frecuente
def monitor_and_train():
    metrics = get_production_metrics()

    if metrics.accuracy < THRESHOLD:
        trigger_emergency_retrain()
        alert_team()

Trade-offs:

ProsContras
Responde a problemas realesRequiere monitoring robusto
Optimiza para calidadPuede tener falsos positivos
Proactivo ante driftLatencia entre detección y fix
Justifica cada re-entrenoComplejo de implementar bien

Úsalo cuando:

  • Puedes medir performance en producción

  • El costo de modelo malo > costo de re-entrenar

  • Tienes alerting robusto

No lo uses cuando:

  • No tienes métricas confiables en prod

  • El re-entrenamiento toma demasiado tiempo

  • Tu modelo no degrada predeciblemente


Pattern 4: Hybrid (Real-world)

# Lo que REALMENTE usamos en producción

# Baseline: time-based conservador
@schedule("@weekly")
def baseline_training():
    train_model(priority="normal")

# Emergency: metric-driven
@monitor(metric="accuracy", threshold=0.85)
def emergency_training():
    train_model(priority="high")
    alert_oncall()

# CI/CD: code-driven
@on_merge(branch="main")
def ci_training():
    if features_changed():
        train_model(priority="normal")

Esta es la realidad en prod:

  • 80% de los trainings → Time-based baseline

  • 15% → Triggered por CI/CD

  • 5% → Emergency por métricas


Criterios de decisión:

Frecuencia de datos:

Streaming (segundos) → Event-driven
Batch diario → Daily schedule
Batch semanal → Weekly schedule
Batch mensual → Monthly schedule
Irregular → Event-driven

Costo de training:

<$10/run → Time-based frecuente está OK
$10-100/run → Time-based + basic triggers
$100-1000/run → Event-driven + monitoring
>$1000/run → Solo cuando absolutamente necesario

Criticidad de estar actualizado:

Alta (fraude, spam) → Hybrid con monitoring
Media (recomendaciones) → Time-based + events
Baja (analytics) → Time-based conservador

Ejemplo real de arquitectura:

┌─────────────────────────────────────┐
│     Data Pipeline (Event-driven)     │
│   S3 → Trigger → Validation → ...   │
└──────────────┬──────────────────────┘
               │
               ▼
┌─────────────────────────────────────┐
│  Training Orchestrator (Hybrid)     │
│                                     │
│  1. Baseline: @weekly              │
│  2. On data threshold: >10K rows   │
│  3. On metric drop: <85% accuracy  │
│  4. On code change: features/*     │
└──────────────┬──────────────────────┘
               │
               ▼
┌─────────────────────────────────────┐
│    ML Pipeline (Orquestado)        │
│  ingest → train → evaluate → ...   │
└─────────────────────────────────────┘

Los errores que he visto:

ERROR 1: "Entrenar cada hora por si acaso"

Costo mensual: $432 en compute Beneficio: Modelo 0.1% mejor ROI: Negativo

ERROR 2: "Solo entrenar manualmente"

Resultado: Se olvidan durante semanas El modelo degrada sin que nadie note Cliente se queja primero

ERROR 3: "Re-entrenar en cada commit"

100 commits/día × $10/training = $1000/día 99% de esos trainings no cambian nada

LO CORRECTO:

Baseline predecible + triggers inteligentes Monitoreo que justifica cada training Balance entre frescura y costo

Mi recomendación pragmática:

Fase 1 - MVP:

python

@schedule("@weekly")
def train():
    # Simple, predecible, suficiente para empezar

Fase 2 - Añade monitoring:

python

@schedule("@weekly")
def train():
    train_model()

@alert(metric="accuracy < 0.85")
def notify():
    # Solo alerta, no auto-retrain aún

Fase 3 - Event-driven si lo necesitas:

python

@schedule("@weekly")
def baseline_train():
    pass

@trigger(on_data_threshold)
def event_train():
    pass

No saltes a Fase 3 si Fase 1 es suficiente.


Mañana hablamos de environments:

Dev, staging, producción... pero para ML.

Donde "environment" no es solo código diferente, sino:

  • Datasets diferentes

  • Modelos diferentes

  • Métricas diferentes

  • Infraestructura diferente

Spoiler: if ENV == "prod" no es suficiente.


Pregunta:

  • ¿Qué pattern usas para scheduling?

  • ¿Te has cuestionado si es el correcto?

More from this blog

Alejandro Parra's Blog

16 posts

Time-based vs Event-driven: Por qué no es solo cron vs webhooks