Mi Experiencia Migrando de On-Premise a Google Cloud
Lecciones aprendidas (y errores costosos) al mover una infraestructura de datos legada a GCP, sin interrumpir producción.
Migrar una infraestructura de datos legada a la nube es uno de esos proyectos que siempre dura el triple de lo estimado. Este artículo cuenta lo que hice bien, lo que hice mal, y qué haría diferente hoy.
El contexto
La empresa donde trabajé tenía todo on-premise: un data warehouse PostgreSQL con ~2 TB de datos históricos, scripts ETL en cron jobs de Linux, y dashboards en Metabase apuntando directamente a producción. La decisión de migrar a GCP fue técnica y económica — los costos de mantenimiento del hardware eran insostenibles.
Fase 1: Inventario (lo que más subestimé)
Antes de mover una sola tabla, dediqué tres semanas solo a documentar:
- ¿Qué tablas tiene cada esquema?
- ¿Quién las consulta y con qué frecuencia?
- ¿Cuáles tienen dependencias circulares?
- ¿Qué scripts ETL están activos vs zombies?
-- Query para identificar tablas más consultadas en los últimos 90 días
SELECT
schemaname,
tablename,
n_live_tup as filas_estimadas,
last_analyze,
seq_scan as escaneos_secuenciales
FROM pg_stat_user_tables
ORDER BY seq_scan DESC;
Este inventario salvó el proyecto. Descubrimos que el 40% de las tablas no tenían queries en los últimos 6 meses — migrarlas habría sido tiempo desperdiciado.
Fase 2: La estrategia de migración
Elegí el patrón Strangler Fig: en lugar de reescribir todo de golpe, fui migrando tabla por tabla mientras producción seguía funcionando en on-premise.
On-Premise (PostgreSQL)
↓
├── Tablas activas → BigQuery (migración progresiva)
└── Dashboards → apuntan a BigQuery cuando la tabla está lista
Duración: 4 meses
Sin downtime
Errores costosos (para que no los repitas)
Error 1: No considerar los tipos de datos
PostgreSQL y BigQuery no son 100% compatibles. El campo TIMESTAMP WITH TIME ZONE se convierte en DATETIME en BigQuery, perdiendo la información de zona horaria.
# El problema: BigQuery no tiene TIMESTAMPTZ nativo
# La solución: normalizar todo a UTC antes de cargar
def normalizar_timestamp(ts: datetime, tz_origen: str) -> datetime:
tz = pytz.timezone(tz_origen)
return tz.localize(ts).astimezone(pytz.UTC).replace(tzinfo=None)
Error 2: Ignorar el costo de las queries durante el desarrollo
BigQuery cobra por datos escaneados. Durante la fase de desarrollo, sin LIMIT ni particionamiento, quemé ~$300 USD en una semana de pruebas.
Regla que aplico ahora: Toda tabla nueva en BigQuery va particionada por fecha desde el día uno.
Error 3: No preparar a los usuarios finales
Los analistas acostumbrados a SQL de PostgreSQL se perdieron con las diferencias de BigQuery (sin ILIKE, DATE_TRUNC diferente, etc.). Habría ahorrado mucho tiempo con un taller de 2 horas al inicio.
Lo que funcionó muy bien
Cloud Composer (Airflow managed): Reemplazar los cron jobs por DAGs de Airflow fue la mejor decisión. Visibilidad completa, reintentos automáticos, y alertas por email cuando algo falla.
dbt para transformaciones: Versionar las transformaciones SQL en Git cambió completamente la dinámica del equipo. De "quién tocó esta vista y por qué" a "el PR está en revisión".
Resultado final
- Costos de infraestructura: -60% vs on-premise
- Tiempo de carga del dashboard principal: de 45s a 8s (particionamiento + clustering en BigQuery)
- Incidentes de datos en producción: de ~4/mes a ~0.5/mes
La migración no fue perfecta, pero valió cada hora invertida.