Skip to main content

batch-cron-jobs

Patrón para fragmentar cron jobs de larga duración en unidades pequeñas con progreso JSON

الانتقال إلى التثبيت

معلومات المصدر

المستودع
Ntizar/NtizarBrainMasterMind
آخر نشاط في المصدر
١١ يوليو ٢٠٢٦ في ٠٣:٣٩
لغة SKILL.md المكتشفة
الإسبانية
النجوم
٢
التفرعات
٠

خيارات التثبيت

يُحدَّد Prompt الذي يراجع المصدر أولًا بشكل افتراضي. يمكنك التبديل إلى أمر مباشر أو تنزيل نسخة محلية.

مراجعة ملفات المصدر

اقرأ SKILL.md وأي ملفات مرافقة يعرضها SkillsMP قبل أن تقرر التثبيت.

عرض SKILL.md

SKILL.md
تعليمات المصدر · معاينة للقراءة فقط
name
batch-cron-jobs
description
Patrón para fragmentar cron jobs de larga duración en unidades pequeñas con progreso JSON
# Batch Cron Jobs — Patrón de procesamiento fragmentado ## Cuándo aplicar Un cron job falla por timeout (horas corriendo, 9009, OOMKill) porque ejecuta un script de larga duración que procesa TODO de golpe. Señales: - Cron con `schedule: "once in 30m"` ejecutando un script de scrapers/batch downloads - Error "el script lleva ~7 horas corriendo", timeout, OOMKill - Script que itera sobre una lista grande (países, años, ciudades, archivos) ## Patrón: Wrapper grupo por ejecución ### Paso 1: Crear wrapper en shell ```bash #!/usr/bin/env bash # wrapper-por-grupo.sh — Procesa un grupo por ejecución de cron set -euo pipefail DATA_DIR="/ruta/al/proyecto/Data" SCRIPT_PRINCIPAL="/ruta/al/script.py" PROGRESS_FILE="$DATA_DIR/progress.json" # Colores RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[1;33m' CYAN='\033[0;36m' NC='\033[0m' # --- Listar grupos --- GRUPOS=() for d in "$DATA_DIR"/*/; do nombre=$(basename "$d") [[ "$nombre" == *progress* ]] && continue GRUPOS+=("$nombre") done # --- Cargar progreso --- CURRENT_INDEX=0 COMPLETADO=False if [ -f "$PROGRESS_FILE" ]; then CURRENT_INDEX=$(python3 -c "import json; d=json.load(open('$PROGRESS_FILE')); print(d.get('actual', 0))" 2>/dev/null || echo 0) COMPLETADO=$(python3 -c "import json; d=json.load(open('$PROGRESS_FILE')); print(str(d.get('completado', False)).lower())" 2>/dev/null || echo "false") fi # --- Si completado, salir --- if [ "$COMPLETADO" = "true" ]; then echo -e "${GREEN}✅ Completado. ${#GRUPOS[@]} grupos procesados.${NC}" exit 0 fi # --- Determinar grupo actual --- if [ "$CURRENT_INDEX" -ge "${#GRUPOS[@]}" ]; then CURRENT_INDEX=0 fi GRUPO_ACTUAL="${GRUPOS[$CURRENT_INDEX]}" SIGUIENTE=$((CURRENT_INDEX + 1)) echo -e "${GREEN}▶️ Próximo grupo: $GRUPO_ACTUAL (${SIGUIENTE}/${#GRUPOS[@]})${NC}" # --- Ejecutar con --filtro para ese grupo --- START=$(date +%s) set +e OUTPUT=$(python3 "$SCRIPT_PRINCIPAL" --filtro "$GRUPO_ACTUAL" 2>&1) EC=$? set -e ELAPSED=$(( $(date +%s) - START )) echo "$OUTPUT" # --- Guardar progreso --- cat > "$PROGRESS_FILE" <<EOF { "grupos": [$(printf '"%s",' "${GRUPOS[@]}" | sed 's/,$//')], "actual": $SIGUIENTE, "completado": $([ "$SIGUIENTE" -ge "${#GRUPOS[@]}" ] && echo "true" || echo "false"), "ultima_ejecucion": "$(date -u '+%Y-%m-%d %H:%M UTC')", "grupo_procesado": "$GRUPO_ACTUAL", "status": "$([ $EC -eq 0 ] && echo SUCCESS || echo ERROR)", "duracion_seg": $ELAPSED } EOF # --- Resumen para cron --- if [ $SIGUIENTE -lt "${#GRUPOS[@]}" ]; then echo -e "🔄 Siguiente: ${GRUPOS[$SIGUIENTE]}" else echo -e "${GREEN}🎉 ¡Todos completados!${NC}" fi ``` ### Paso 2: Cron configuration ``` job_id: batch-procesamiento no_agent: true ← CRUCIAL: sin LLM, ejecuta directo script: wrapper-por-grupo.sh schedule: every 1h ← cada hora procesa 1 grupo repeat: N ← número de grupos + margen (ej: 20 grupos → 25) deliver: local ← guarda resultado en archivos enabled_toolsets: [] ← sin toolsets, solo filesystem ``` ### Paso 3: Script principal con soporte de filtro El script original necesita un flag de filtro (ej: `--pais`, `--año`, `--grupo`). Si no lo tiene, añadirlo. Ejemplo: ```python # En el script original: if "--pais" in sys.argv and i + 1 < len(sys.argv): SOLO_PAIS = sys.argv[i + 1] ``` ## Estructura de archivos de progreso ```json { "grupos": ["AT", "BE", "BG", "CH", "CZ", "DE"], "actual": 3, "completado": false, "ultima_ejecucion": "2026-07-08 14:00 UTC", "grupo_procesado": "BG", "status": "SUCCESS", "duracion_seg": 247 } ``` ## Pitfalls - **Sin `no_agent: true`** → el cron pasa por LLM cada ejecución → lento, caro, innecesario - **Repeat mayor que grupos** → no pasa nada, al llegar a "completado: true" el wrapper sale 0 - **Script sin soporte de filtro** → añadir el parámetro de filtro al script original - **Progreso en `completed=true`** → el wrapper debe detectar esto y salir sin hacer nada - **Bash arrays con nombres con espacios** → usar `IFS= read -r` en loops - **🔥 Script path resolution** → el cron solo encuentra scripts en `~/.hermes/scripts/`. Si el script está en `/hermes-home/scripts/`, crear symlink: `ln -sf /hermes-home/scripts/foo.sh ~/.hermes/scripts/foo.sh` - **🔥 JSON booleans en bash** → `True`/`False` (Python) generan JSON inválido. Usar `true`/`false` (minúsculas). Ejemplo: `echo "true"` NO `echo "True"`. - **🔥 `pipefail` + `find` en directorio inexistente** → Con `set -euo pipefail`, `find` en directorio que no existe da exit code 1. `2>/dev/null` suprime el error pero NO el código de salida. Con `pipefail` se propaga por `| wc -l` y mata el script. Fix: `find "$DIR" -name "*.pdf" 2>/dev/null | wc -l || true`. Clásico en batch downloads donde el primer ítem aún no tiene carpeta. ## Comparación: cron antiguo vs cron con wrapper | Aspecto | Cron antiguo | Cron con wrapper | |---------|-------------|-----------------| | Ejecución | 1 → todo el lote | N → 1 grupo por ejecución | | Duración | 7+ horas, timeout | 5-30 minutos | | Reinicio tras crash | Pierde todo, empieza de 0 | Reanuda desde progreso | | Coste LLM | Sí (si no es no_agent) | No (no_agent: true) | | Visibilidad | Un fallo, no sabes dónde | Logs por grupo |
عرض على GitHub