Saltar a contenido

Arquitectura — Visión general

Diagrama de componentes

graph TD
    subgraph MySQL["MySQL (Rosgan DB)"]
        HAC_CERT[HAC_CERTIFICADO]
        ATRIB_VAL[ATRIBUTOS_VALORES]
        ESTAB[REG_CUENTA_ESTABLECIMIENTO]
        OFERTAS[HAC_OFERTAS]
        MOV[HAC_CERTIFICADO_MOV]
        DETALLE[HAC_CERT_DETALLE]
        PRE_ORDEN[HAC_PRE_ORDEN]
        ATRIBUTOS[ATRIBUTOS]
        QUEUE[(meilisearch_sync_queue)]
    end

    subgraph Worker["sync_worker.py (Docker)"]
        CHECK_LOTES[run_lotes_cycle]
        CHECK_ATTR[check_atributos_changed]
        SYNC_ATTR[sync_atributos]
        TRANSFORM[limpiar_certificado]
    end

    subgraph Meili["Meilisearch"]
        INDEX[(índice: certificados)]
        SETTINGS[settings <br/> filterable <br/> searchable <br/> sortable]
    end

    HAC_CERT -->|trigger| QUEUE
    ATRIB_VAL -->|trigger| QUEUE
    ESTAB -->|trigger| QUEUE
    OFERTAS -->|trigger| QUEUE
    MOV -->|trigger| QUEUE
    DETALLE -->|trigger| QUEUE
    PRE_ORDEN -->|trigger| QUEUE

    QUEUE -->|SELECT + DELETE| CHECK_LOTES
    CHECK_LOTES -->|query JOIN 12 tablas| TRANSFORM
    TRANSFORM -->|POST documents| INDEX

    ATRIBUTOS -->|updated_at >= NOW - 1min| CHECK_ATTR
    CHECK_ATTR -->|sí hay cambios| SYNC_ATTR
    SYNC_ATTR -->|PUT settings| SETTINGS

Decisiones de diseño

Cola con PK = certificado_id

La tabla meilisearch_sync_queue tiene certificado_id como Primary Key. Esto hace que INSERT IGNORE sea suficiente para deduplicar: si un lote se modifica 30 veces antes de que el worker corra, se indexa exactamente una vez con el estado final.

Triggers en MySQL, no polling

Las alternativas consideradas fueron:

Opción Pros Contras
Triggers + cola Captura cualquier cambio, deduplicado, atómico con el UPDATE Requiere permisos TRIGGER en MySQL
Polling con updated_at Sin triggers Solo funciona en tablas directas, no captura cambios en tablas relacionadas (establecimiento, atributos, etc.)
Binlog (Debezium/Maxwell) Captura todo sin tocar el schema Overhead operacional alto, requiere replication habilitado

is_delete en la cola

Cuando se hace DELETE en HAC_CERTIFICADO el trigger usa ON DUPLICATE KEY UPDATE is_delete = 1 en lugar de INSERT IGNORE. Esto garantiza que si el lote estaba encolado para upsert y se elimina antes de que el worker corra, el worker lo eliminará de Meilisearch en lugar de intentar reindexarlo.

Atributos por ventana de tiempo

Los atributos dinámicos (ATRIBUTOS) se detectan por updated_at >= NOW() - INTERVAL 1 MINUTE sin guardar estado. Es simple, funciona, y el costo de 6 queries por minuto a una tabla pequeña es despreciable.

Python + asyncio

El worker usa aiomysql y httpx de forma asíncrona. En la práctica las operaciones son secuenciales (leer cola → query → upsert → borrar cola), pero asyncio permite que el sleep entre ciclos no bloquee recursos y que en el futuro se puedan paralelizar batches sin reescribir el core.