> ## Documentation Index
> Fetch the complete documentation index at: https://help.treble.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Sincronización Incremental

> Mantén una copia de tus datos de Treble en tus propios sistemas, de forma confiable, usando el watermark synced_at.

# Sincronizar el warehouse hacia tus propios sistemas

Si te conectas vía JDBC/ODBC para replicar los datos del warehouse en tu propia base de datos o lake, **no necesitas re-descargas completas periódicas**. Todas las tablas exponen `synced_at` — el momento en que cada fila fue escrita o corregida por última vez — diseñado exactamente para la extracción incremental.

## El patrón

Mantén un watermark (`T_prev`) por tabla en tu sistema, y en cada ciclo de sincronización:

```text theme={null}
1. Congela el corte:    T = max(synced_at) de la tabla   (una consulta, usa NUESTRO reloj)
2. Trae el delta:       WHERE synced_at > {T_prev} AND synced_at <= {T}
3. UPSERT por clave:    inserta filas nuevas, reemplaza las existentes por su id
4. Refresca la ventana caliente: vuelve a traer los últimos 3 días por fecha de evento
                        y reemplaza ese segmento completo en tu copia
5. Avanza:              T_prev = T   (solo después de que los pasos 2–4 hayan tenido éxito)
```

### Por qué importa cada paso

* **Congela `T` una sola vez (paso 1).** Usar `now()` dentro de tus consultas hace que cada página de una extracción larga vea un instante distinto. Congelar un solo timestamp convierte toda la extracción en un snapshot consistente.
* **UPSERT, nunca INSERT ciego (paso 3).** Cuando el warehouse corrige una fila (llega un estado de entrega, aterriza un id de CRM tardío), reemite la **misma clave de negocio** con un `synced_at` nuevo. Un upsert reemplaza tu versión desactualizada; un insert ciego la duplicaría. La clave de cada tabla es su columna `*_id` (`session_id`, `message_id`, `deployment_id`, …).
* **Reemplaza la ventana caliente (paso 4).** En casos raros una fila puede *desaparecer* de una tabla (por ejemplo, un duplicado creado por una corrección interna que se limpia). Una consulta puede devolver lo que existe — no puede decirte qué dejó de existir. Volver a traer una ventana reciente corta por fecha de evento (`created_at` / `scheduled_at`) y reemplazar ese segmento en tu copia garantiza que tus datos coincidan con los nuestros, incluidas las eliminaciones. Tres días es una ventana cómoda; todas las correcciones ocurren dentro de 48 horas.

### Ciclo de referencia (ejemplo: envíos de campaña)

```sql theme={null}
-- paso 1
SELECT max(synced_at) FROM fact_campaign_sends;   -- => {T}

-- paso 2 (luego haz UPSERT por deployment_id de tu lado)
SELECT *
FROM fact_campaign_sends
WHERE synced_at > {T_prev} AND synced_at <= {T};

-- paso 4 (reemplaza este segmento completo en tu copia)
SELECT *
FROM fact_campaign_sends
WHERE scheduled_at >= today() - 3 AND synced_at <= {T};
```

## Carga inicial

La primera sincronización es el mismo mecanismo con `T_prev` en cero: trae todo con `synced_at <= T`, en bloques acotados por fecha si la tabla es grande, y luego empieza a ciclar. Sin casos especiales.

## Qué no hacer

* **No re-descargues la historia de forma programada** — los datos consolidados (de más de 48 horas) son inmutables; volver a traerlos es puro costo.
* **No construyas cortes con tu propio reloj** — compara siempre contra valores de `synced_at` leídos del warehouse.
* **No te saltes el upsert** — las correcciones son una característica, no una anomalía; tu copia debe absorberlas.

## Elegir una cadencia

Cualquier cadencia funciona — el watermark hace que los ciclos sean independientes. Cada 15–60 minutos acompaña la frescura propia del warehouse; cada hora o cada día está bien para copias de reporting. El costo de cada ciclo es proporcional a lo que cambió, no al tamaño de tu historia.
