FLUJOS/INFO/DOCS/CONTEXT/PIPELINE_MAESTRO.md
CAPITANSITO 778db90d78 fix: módulos faltantes wikipedia scraper + docs actualizados
- WIKIPEDIA/main.py: import buscar_articulos y obtener_contenido_wikipedia
- myenv: instalados wikipedia, wikipedia-api, deep-translator
- PIPELINE_MAESTRO.md: tabla de errores conocidos, Nice/CPUQuota, timer 2d
- SEGURIDAD.md: tabla de fixes aplicados en producción

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-22 00:33:50 +02:00

7.9 KiB
Raw Blame History

Pipeline Maestro — Contexto técnico FLUJOS

Fecha: 2026-04-21
Archivo: FLUJOS_DATOS/pipeline_maestro.py
Scheduler: systemd timer (semanal, domingos 3:00 AM)


Visión general

El pipeline maestro orquesta las tres fases del sistema en orden estricto:

FASE 1 — SCRAPING
    ├── Wikipedia (main.py)             → colección wikipedia
    ├── Noticias (main_noticias.py)     → colección noticias
    └── Imágenes Wikipedia (wikipedia_image_scraper.py) → colección imagenes_wiki

FASE 2 — ANÁLISIS
    ├── Qwen3-VL imágenes (pipeline_imagenes.py --analizar) → colección imagenes
    └── Tokenización texto (pipeline_mongolo.py)            → colección noticias/wikipedia (actualiza)

FASE 3 — COMPARACIÓN
    └── Similitud entre documentos (pipeline_completo.py)   → colección comparaciones

Ejecución

# Ejecución completa (las 3 fases)
/var/www/theflows.net/flujos/FLUJOS_DATOS/myenv/bin/python3 pipeline_maestro.py

# Solo una fase
python pipeline_maestro.py --solo-fase scraping
python pipeline_maestro.py --solo-fase analisis
python pipeline_maestro.py --solo-fase comparacion

# Forzar re-ejecución ignorando cooldown de 20h
python pipeline_maestro.py --forzar
python pipeline_maestro.py --solo-fase scraping --forzar

Control de estado — MongoDB pipeline_log

Cada fase registra inicio, fin y stats en la colección pipeline_log:

{
  "fase": "scraping",
  "estado": "completado" | "en_progreso" | "error",
  "inicio": ISODate("2026-04-20T03:00:00Z"),
  "fin": ISODate("2026-04-20T04:15:00Z"),
  "stats": {
    "wikipedia_nuevos": 45,
    "noticias_nuevos": 312,
    "imagenes_wiki_nuevas": 60
  }
}

Lógica de "¿necesita correr?"

def fase_necesita_correr(db, fase, forzar=False):
    if forzar: return True
    ultimo = db['pipeline_log'].find_one({'fase': fase}, sort=[('inicio', -1)])
    if not ultimo: return True                             # nunca corrió
    if ultimo['estado'] != 'completado': return True      # última ejecución falló
    if utcnow() - ultimo['fin'] < timedelta(hours=20):
        return False                                       # cooldown activo
    return True

Lockfile — prevención de instancias simultáneas

LOCK_FILE = Path("/tmp/flujos_pipeline.lock")

class PipelineLock:
    def __enter__(self):
        self._f = open(LOCK_FILE, "w")
        fcntl.flock(self._f, fcntl.LOCK_EX | fcntl.LOCK_NB)  # non-blocking
        # Si ya hay lock → BlockingIOError → log.error + sys.exit(1)

Si el pipeline muere abruptamente, el lock se libera automáticamente cuando el proceso termina (el SO libera todos los file locks del proceso muerto).


Índices de deduplicación (idempotente)

def setup_indices(db):
    indices = {
        "wikipedia":     [("archivo", ASCENDING)],
        "noticias":      [("archivo", ASCENDING)],
        "imagenes":      [("archivo", ASCENDING)],
        "imagenes_wiki": [("archivo", ASCENDING)],
        # comparaciones: SIN índice único (52M docs con posibles duplicados)
    }
    for col, keys in indices.items():
        db[col].create_index(keys, unique=True, background=True)

Se ejecuta al inicio de cada pipeline. create_index es idempotente — no falla si el índice ya existe.


Fase 3 — Modo incremental

La comparación usa el flag --desde para solo comparar documentos nuevos:

ultima = ultima_ejecucion_completada(db, "comparacion")
if ultima:
    args = ["--desde", ultima.strftime("%Y-%m-%d")]
    # pipeline_completo.py solo procesa docs con fecha >= ultima ejecución

Esto evita re-comparar los 52M pares existentes en cada ejecución.


Systemd — configuración

Archivos instalados en /etc/systemd/system/:

flujos-pipeline.service

[Unit]
Description=FLUJOS Pipeline Maestro
After=network.target mongod.service
Requires=mongod.service

[Service]
Type=oneshot
User=capitansito
WorkingDirectory=/var/www/theflows.net/flujos/FLUJOS_DATOS
Environment=MONGO_URL=mongodb://localhost:27017
Environment=DB_NAME=FLUJOS_DATOS
ExecStart=/var/www/theflows.net/flujos/FLUJOS_DATOS/myenv/bin/python3 \
          /var/www/theflows.net/flujos/FLUJOS_DATOS/pipeline_maestro.py
TimeoutStartSec=43200
Nice=15
CPUQuota=400%
StandardOutput=journal
StandardError=journal

[Install]
WantedBy=multi-user.target

Nice=15 + CPUQuota=400% limita el pipeline a ~4 cores para no saturar el servidor (Qwen en CPU puede usar 1000%+ por instancia sin esto).

flujos-pipeline.timer

[Unit]
Description=Ejecutar FLUJOS Pipeline cada 2 dias a las 03:00
Requires=flujos-pipeline.service

[Timer]
OnBootSec=2min
OnUnitActiveSec=2d
Persistent=true
Unit=flujos-pipeline.service

[Install]
WantedBy=timers.target

Cambiado de semanal (OnCalendar=Sun) a cada 2 días (OnUnitActiveSec=2d) el 2026-04-22. Persistent=true hace que si el servidor estaba apagado cuando tocaba, el timer se dispara 2 minutos después del siguiente arranque.

Comandos de gestión

# Ver estado del timer
systemctl status flujos-pipeline.timer

# Ver logs del último pipeline
journalctl -u flujos-pipeline.service -n 100

# Lanzar manualmente
systemctl start flujos-pipeline.service

# Habilitar/deshabilitar timer
systemctl enable flujos-pipeline.timer
systemctl disable flujos-pipeline.timer

# Ver cuándo es la próxima ejecución
systemctl list-timers flujos-pipeline.timer

Cooldown y tiempos esperados de ejecución

Fase Cooldown mínimo Tiempo estimado
scraping 20h 26h (depende de medios accesibles)
analisis 20h 14h (Qwen en CPU es lento: ~3 min/imagen)
comparacion 20h Variable (incremental: 30 min / full: varios días)

Resumen de estadísticas al final

Al completar, el pipeline imprime en el log:

Estado MongoDB:
    wikipedia           : 5,234
    noticias            : 21,456
    imagenes            :   150
    imagenes_wiki       :   500
    comparaciones       : 52,100,000

Log del pipeline

FLUJOS_DATOS/pipeline_maestro.log   → log principal (en .gitignore)

También visible en journald:

journalctl -u flujos-pipeline.service --since "2026-04-20"

Errores conocidos y fixes aplicados (2026-04-22)

Script Error Fix
WIKIPEDIA/main.py NameError: buscar_articulos no definida Añadido from wikipedia_utils import buscar_articulos, obtener_contenido_wikipedia
WIKIPEDIA/main.py ModuleNotFoundError: wikipedia pip install wikipedia en myenv
WIKIPEDIA/main.py ModuleNotFoundError: wikipediaapi pip install wikipedia-api en myenv
NOTICIAS/main_noticias.py ModuleNotFoundError: deep_translator pip install deep-translator en myenv

Todos los módulos se instalan dentro del entorno virtual:

/var/www/theflows.net/flujos/FLUJOS_DATOS/myenv/bin/python3 -m pip install <modulo>

No usar pip directamente (el ejecutable pip del venv puede estar roto; usar python3 -m pip).


Dependencias Python

pymongo
bson      # ObjectId (incluido con pymongo)
fcntl     # stdlib Python
argparse  # stdlib Python
subprocess

El pipeline maestro solo llama a los sub-scripts vía subprocess.run(), no importa sus módulos directamente.


Flujo completo diagram

pipeline_maestro.py
│
├── setup_indices(db)                        # crea índices únicos
│
├── [FASE 1] fase_scraping()
│   ├── subprocess: WIKIPEDIA/main.py
│   ├── subprocess: NOTICIAS/main_noticias.py
│   └── subprocess: IMAGENES/wikipedia_image_scraper.py --flujos --max 20 --mongo
│
├── [FASE 2] fase_analisis()
│   ├── subprocess: IMAGENES/pipeline_imagenes.py --analizar --carpeta ... --mongo
│   └── subprocess: COMPARACIONES/pipeline_mongolo.py
│
└── [FASE 3] fase_comparacion()
    └── subprocess: COMPARACIONES/pipeline_completo.py [--desde YYYY-MM-DD]