Files
dashboard-leads/OPTIMIZACION_CACHE.md
2026-08-18 11:48:08 -05:00

3.5 KiB

OPTIMIZACIÓN — Caché de descargas pesadas (reutilizable en Ventas, Comisiones, etc.)

El problema (síntoma)

Al cambiar un filtro (mes, sede, etc.) el dashboard se demora 15-130 segundos, aunque los datos "ya deberían estar cargados". Se siente como si cada filtro volviera a descargar todo desde cero.

La causa raíz

Una función que descarga datos de una fuente externa (Supabase, SQL Server, Postgre, API) se llama SIN caché. Cada vez que el usuario filtra, esa función vuelve a descargar TODO desde internet.

En LEADS el culpable fue traer_cartera_rows() (bajaba 86,000 filas de Supabase de 1000 en 1000 = 86 pedidos en serie ≈ 52-130s) y se re-descargaba en CADA cambio de mes, porque matriz_web_formulario la llamaba directo:

# ANTES (malo): re-descarga en cada llamada
def _load():
    rows = get_dm().traer_cartera_rows()   # baja 86k filas CADA vez
    ...

Cómo diagnosticarlo (script de tiempos)

Medir cada parte por separado para encontrar QUÉ tarda. Clave: medir un cambio de mes CON los insumos ya cargados (2a vez). Si sigue lento, algo se re-descarga.

import time, services as S
def cron(label, fn):
    t0 = time.time(); fn(); print(f"  {label:38} {time.time()-t0:6.1f}s")

cron("dashboard MES1 (1a vez)", lambda: S.mi_funcion("2026","1",...))
cron("dashboard MES2 (2a vez)", lambda: S.mi_funcion("2026","2",...))  # <-- debe ser rapido
# Si MES2 sale lento, desglosar: medir cada traer_* y cada matriz por separado
# hasta ver cual linea se lleva los segundos (casi siempre un traer_* sin cache).

La solución (2 líneas, NO toca lógica ni resultados)

Envolver la descarga pesada en cache_get_or_set con clave GLOBAL, y que TODAS las funciones usen ese helper cacheado en vez de descargar directo.

# 1) Helper cacheado (se baja UNA vez, se reutiliza)
def _cartera_rows_cache():
    return cache_get_or_set("cartera_rows", ("GLOBAL",),
                            lambda: get_dm().traer_cartera_rows())

# 2) Reemplazar TODAS las llamadas directas:
#    get_dm().traer_cartera_rows()   ->   _cartera_rows_cache()

Resultado en LEADS:

  • Cambiar de mes: de 69-133s -> 0.4s (solo filtra lo que ya está en memoria)
  • La descarga pesada solo se paga 1 vez (en la precarga de arranque, en background).

Regla general para CUALQUIER dashboard (Ventas, Comisiones, Rentabilidad...)

  1. Toda función traer_* / consulta a SQL/Supabase/API que se use en varias vistas o en cada filtro, debe estar cacheada con cache_get_or_set.
  2. NUNCA llamar get_dm().traer_xxx() directo dentro de un _load que depende de filtros; usar el helper cacheado.
  3. El filtro (mes/sede/programa) debe operar sobre datos YA en memoria, no re-descargar. Filtrar en memoria = milisegundos.
  4. Precargar en segundo plano (hilo daemon al startup) el mes actual/anterior para que el usuario no espere la 1a carga.
  5. El refresco periódico (cada 15 min) invalida caché -> considerar bajar datos que cambian 1 vez al día (ej. cartera) con menos frecuencia, o recargar en background sin dejar hueco.

Mejora opcional pendiente (no aplicada aún)

La 1a descarga de la cartera es lenta (100-155s) porque pagina de 1000 en 1000. Subir el tamaño de lote (ej. limit=10000) reduce los viajes: de ~155s a ~10-15s. Aplica a cualquier tabla grande que se baje paginada.

Verificación (que no cambien números)

Antes de optimizar, guardar una "línea base" con los totales actuales (diag_base.json). Tras optimizar, comparar: deben ser IDÉNTICOS. La optimización solo cambia CUÁNDO/CÓMO se descarga, nunca el cálculo.