Migracion de Datos

Procesos ETL, patrones de Change Data Capture (CDC), mapping de datos entre plataformas, EBCDIC vs UTF-8, packed decimal, y estrategias de migracion.

Panorama de migracion de datos

La migracion de datos desde o hacia IBM i es uno de los desafios mas subestimados en proyectos de modernizacion. Los datos en IBM i tienen particularidades unicas: encoding EBCDIC, tipos packed decimal, estructuras de archivos fisicos con multiples miembros, y relaciones implicitas que no estan definidas como foreign keys.

La migracion puede ser de IBM i a otra plataforma (ej: migrar datos a PostgreSQL), de otra plataforma a IBM i (ej: consolidar datos), o entre IBM i (ej: migrar a PowerVS o consolidar LPARs).

Migracion unica (one-time)

Se migran todos los datos de una vez. Requiere ventana de downtime. Simple pero riesgoso para volumenes grandes.

Migracion incremental (trickle)

Se migra un lote inicial y luego se sincronizan los cambios con CDC. Minimiza downtime. Mas complejo de implementar.

Replicacion continua

Los datos se replican en tiempo real de forma permanente. No hay cutover, ambos sistemas coexisten indefinidamente.

ETL: Extract, Transform, Load

El patron ETL es la base de la mayoria de las migraciones. Se extraen datos de IBM i, se transforman al formato del destino, y se cargan en la plataforma target. Cada fase tiene sus propias herramientas y consideraciones.

Extract: sacar datos de IBM i

SQL — Extraccion de datos
-- Exportar tabla completa a CSV via SQL
-- (usando CPYTOIMPF desde CL o query directo)

-- Metodo 1: Query directo via JDBC/ODBC
SELECT
  CUSID,
  TRIM(CUSNAM) AS customer_name,
  TRIM(CUSADR) AS address,
  CUSBAL AS balance,
  CHAR(OPNDAT, ISO) AS open_date,
  CASE CUSTS
    WHEN 'A' THEN 'Active'
    WHEN 'I' THEN 'Inactive'
    WHEN 'C' THEN 'Closed'
  END AS status
FROM PRODLIB.CUSTMAST
WHERE CUSTS <> 'C'
ORDER BY CUSID;

-- Metodo 2: Unload a archivo delimitado
CREATE TABLE QTEMP.EXPORT_CUSTOMERS AS (
  SELECT CUSID, CUSNAM, CUSADR, CUSBAL
  FROM PRODLIB.CUSTMAST
) WITH DATA;
CL — Exportar datos a CSV
-- Exportar con CPYTOIMPF (metodo CL nativo)
CPYTOIMPF FROMFILE(PRODLIB/CUSTMAST)
          TOSTMF('/home/exports/custmast.csv')
          MBROPT(*REPLACE)
          STMFCCSID(1208)        -- UTF-8
          RCDDLM(*CRLF)
          DTAFMT(*DLM)
          STRDLM(*DBLQ)
          FLDDLM(',')

-- Exportar con SQL
RUNSQLSTM SRCSTMF('/home/scripts/export_cust.sql')
          COMMIT(*NONE)
          NAMING(*SQL)

Transform: convertir datos

La transformacion aborda las diferencias fundamentales entre IBM i y las plataformas modernas: encoding (EBCDIC a UTF-8), tipos de datos (packed decimal a numeric), formatos de fecha, y limpieza de campos con trailing blanks.

Python — Transformacion de datos IBM i
import pyodbc
import pandas as pd

# Conectar y extraer
conn = pyodbc.connect('DSN=IBMi_PROD')
df = pd.read_sql("""
    SELECT CUSID, CUSNAM, CUSADR, CUSBAL,
           OPNDAT, CUSTS
    FROM PRODLIB.CUSTMAST
""", conn)

# Transformaciones
df['customer_name'] = df['CUSNAM'].str.strip()
df['address'] = df['CUSADR'].str.strip()
df['balance'] = df['CUSBAL'].astype(float)
df['open_date'] = pd.to_datetime(
    df['OPNDAT'], format='%Y%m%d'
)
df['status'] = df['CUSTS'].map({
    'A': 'Active', 'I': 'Inactive', 'C': 'Closed'
})

# Validar
assert df['CUSID'].is_unique, "IDs duplicados!"
assert df['balance'].notna().all(), "Balances nulos!"

# Cargar en PostgreSQL
from sqlalchemy import create_engine
pg_engine = create_engine(
    'postgresql://user:pass@host/db'
)
df.to_sql('customers', pg_engine,
          if_exists='replace', index=False)

Change Data Capture (CDC)

CDC captura los cambios incrementales en los datos (inserts, updates, deletes) y los replica al sistema destino. En IBM i, la fuente natural de CDC son los journals, que registran cada operacion de escritura a nivel de registro.

ETL batch (full extract)

  • Extrae todos los datos cada vez
  • Simple de implementar
  • Alto consumo de recursos en cada ejecucion
  • Latencia: horas (generalmente nocturno)
  • No captura deletes intermedios
  • Adecuado para volumenes pequenos

CDC (incremental)

  • Solo captura cambios desde la ultima vez
  • Mas complejo de implementar
  • Bajo consumo: solo lee journal entries
  • Latencia: segundos a minutos
  • Captura inserts, updates Y deletes
  • Escala a cualquier volumen
SQL — CDC manual con journals
-- Leer cambios de las ultimas 24 horas
SELECT
  jrn.JOURNAL_ENTRY_TYPE AS operation,
  jrn.ENTRY_TIMESTAMP AS change_time,
  jrn.CUSID,
  jrn.CUSNAM,
  jrn.CUSBAL
FROM TABLE(
  QSYS2.DISPLAY_JOURNAL(
    JOURNAL_LIBRARY => 'MYLIB',
    JOURNAL_NAME => 'MYJRN',
    STARTING_TIMESTAMP =>
      CURRENT_TIMESTAMP - 24 HOURS,
    JOURNAL_ENTRY_TYPES => 'PT,UP,DL',
    JOURNAL_CODES => 'R',
    FILE_NAME => 'CUSTMAST'
  )
) AS jrn
ORDER BY jrn.ENTRY_TIMESTAMP;

-- Tipos de operacion:
-- PT = Put (INSERT)
-- UP = Update
-- DL = Delete
-- Los campos contienen el AFTER image
-- (con IMAGES(*BOTH) tambien se obtiene el BEFORE)

Precisely Connect CDC

Herramienta enterprise para CDC en IBM i. Lee journals en tiempo real y publica a Kafka, MQ, archivos, o bases de datos. Soporta transformacion inline y multiples targets.

Debezium + custom connector

Open source. Requiere un conector custom para leer journals de IBM i. Comunidad activa pero soporte limitado para IBM i especificamente.

Data mapping entre plataformas

Los tipos de datos de IBM i (DDS y SQL) no mapean directamente a tipos de otras bases de datos. Esta tabla muestra las equivalencias mas comunes.

IBM i (DDS)IBM i (SQL)PostgreSQLNotas
A (Alpha)CHAR / VARCHARVARCHAR / TEXTEBCDIC a UTF-8, strip trailing blanks
P (Packed)DECIMAL / NUMERICNUMERIC(p,s)Packed decimal, 2 digitos por byte
S (Zoned)NUMERICNUMERIC(p,s)Zoned decimal, 1 digito por byte
B (Binary)INTEGER / SMALLINTINTEGER / SMALLINT2 o 4 bytes
L (Date)DATEDATEVerificar formato (ISO, USA, EUR)
T (Time)TIMETIMEFormato HH.MM.SS
Z (Timestamp)TIMESTAMPTIMESTAMPCon o sin timezone
H (DBCS)GRAPHIC / VARGRAPHICTEXTCaracteres double-byte (CJK)
O (Object)BLOBBYTEADatos binarios
6 (Date campo numerico)DECIMAL(8,0)DATEFormato YYYYMMDD como numero
Atencion con fechas: Muchos programas RPG legacy almacenan fechas como campos numericos (DECIMAL 8,0 con formato YYYYMMDD o DECIMAL 6,0 con YYMMDD). Estos no son campos DATE reales y requieren conversion explicita durante la migracion.

EBCDIC vs ASCII/UTF-8

IBM i usa EBCDIC (Extended Binary Coded Decimal Interchange Code) como encoding nativo, mientras que el mundo moderno usa ASCII/UTF-8. Esta diferencia es la fuente de muchos problemas de migracion, especialmente con caracteres especiales, acentos y simbolos de moneda.

EBCDIC (CCSID 37 / 284 / 500)

  • Encoding de 1 byte, 256 caracteres
  • Orden de clasificacion diferente a ASCII
  • Letras NO son contiguas (A-I, J-R, S-Z)
  • Variantes por idioma: 37 (USA), 284 (Spain), 500 (International)
  • Numeros y letras NO en el mismo orden que ASCII

UTF-8 (CCSID 1208)

  • Encoding variable (1-4 bytes)
  • Compatible con ASCII para los primeros 128 chars
  • Orden de clasificacion estandar
  • Soporta todos los idiomas del mundo
  • Estandar de facto en sistemas modernos
SQL — Conversion de CCSID
-- Ver el CCSID de una tabla
SELECT TABLE_SCHEMA, TABLE_NAME, COLUMN_NAME,
       CCSID, DATA_TYPE, CHARACTER_MAXIMUM_LENGTH
FROM QSYS2.SYSCOLUMNS
WHERE TABLE_SCHEMA = 'MYLIB'
  AND TABLE_NAME = 'CUSTMAST';

-- Convertir datos al extraer
SELECT
  CAST(CUSNAM AS VARCHAR(40) CCSID 1208) AS name_utf8,
  CAST(CUSADR AS VARCHAR(60) CCSID 1208) AS addr_utf8
FROM MYLIB.CUSTMAST;

-- Cambiar CCSID de un archivo IFS
CHGATR OBJ('/home/exports/data.csv')
       ATR(*CCSID)
       VALUE(1208)
Problema comun: Caracteres como ñ, acentos (á é í), y simbolos (€ £) se corrompen si no se especifica correctamente el CCSID en la extraccion. Siempre usar CCSID 1208 (UTF-8) al exportar.

Datos packed decimal y zoned decimal

IBM i usa dos formatos numericos que no existen en la mayoria de las plataformas modernas: packed decimal y zoned decimal. Entender estos formatos es critico cuando se migran datos a nivel de bytes (archivos planos, save files).

Packed Decimal (tipo P)

Almacena 2 digitos por byte (excepto el ultimo que tiene 1 digito + signo). Un campo PACKED(7,2) como 12345.67 se almacena como: 01 23 45 67 0F(F = positivo, D = negativo). Ocupa ceil((precision+1)/2) bytes = 4 bytes para P(7,2).

Zoned Decimal (tipo S)

Almacena 1 digito por byte. El signo va en la zona del ultimo byte. Un campo ZONED(7,2) como 12345.67 se almacena como: F1 F2 F3 F4 F5 F6 F7(ultimo byte: F7 positivo, D7 negativo). Ocupa exactamente precision bytes.

Python — Decodificar packed decimal
def decode_packed_decimal(data: bytes, decimals: int) -> float:
    """Decodifica packed decimal de IBM i."""
    result = 0
    for i, byte in enumerate(data):
        if i < len(data) - 1:
            # Cada byte tiene 2 digitos
            high = (byte >> 4) & 0x0F
            low = byte & 0x0F
            result = result * 100 + high * 10 + low
        else:
            # Ultimo byte: 1 digito + signo
            high = (byte >> 4) & 0x0F
            sign = byte & 0x0F
            result = result * 10 + high
            if sign in (0x0D, 0x0B):  # D o B = negativo
                result = -result

    return result / (10 ** decimals)

# Ejemplo: b'\x01\x23\x45\x67\x0F' = 12345.67
value = decode_packed_decimal(
    b'\x01\x23\x45\x67\x0F', decimals=2
)
print(value)  # 12345.67
Recomendacion: Siempre que sea posible, extraer datos via SQL o JDBC/ODBC en lugar de archivos planos. SQL convierte automaticamente packed/zoned decimal a tipos numericos estandar, evitando la necesidad de decodificacion manual.

Herramientas de migracion

HerramientaTipoUso principalCosto
CPYF / CPYTOIMPFCL nativoExport CSV, copiar entre archivosIncluido
SQL INSERT/SELECTSQL nativoCopiar datos entre tablas/schemasIncluido
FTP / SFTPProtocolo nativoTransferir archivos al IFSIncluido
JDBC/ODBC bulkDriverETL desde aplicacion externaIncluido
Precisely Connect CDCEnterpriseCDC en tiempo real, replicacionLicencia
IBM InfoSphere DataStageEnterpriseETL visual enterpriseLicencia
Talend / InformaticaEnterpriseETL multi-plataformaLicencia
Apache NifiOpen sourceData pipeline visualGratuito
Python + pandasScriptETL custom, transformaciones complejasGratuito
dbtOpen sourceTransformaciones SQL, documentacionGratuito

Validacion de datos migrados

La validacion es la fase mas critica y a menudo la mas descuidada. Un error en la migracion de datos puede pasar desapercibido durante meses si no se valida sistematicamente.

SQL — Queries de validacion
-- 1. Validar conteos
-- En IBM i (origen):
SELECT COUNT(*) AS total_origin FROM PRODLIB.CUSTMAST;
-- En PostgreSQL (destino):
SELECT COUNT(*) AS total_dest FROM public.customers;
-- Deben coincidir!

-- 2. Validar sumas de control
-- En IBM i:
SELECT
  COUNT(*) AS rows,
  SUM(CUSBAL) AS sum_balance,
  MIN(CUSBAL) AS min_balance,
  MAX(CUSBAL) AS max_balance,
  COUNT(DISTINCT CUSTS) AS distinct_status
FROM PRODLIB.CUSTMAST;

-- En destino: misma query, comparar resultados

-- 3. Validar datos especificos (sampling)
-- Comparar 100 registros aleatorios
SELECT CUSID, CUSNAM, CUSBAL
FROM PRODLIB.CUSTMAST
ORDER BY RAND()
FETCH FIRST 100 ROWS ONLY;

-- 4. Detectar truncamientos
SELECT CUSID, CUSNAM
FROM PRODLIB.CUSTMAST
WHERE LENGTH(TRIM(CUSNAM)) > 40;  -- limite del destino

-- 5. Detectar caracteres problematicos
SELECT CUSID, CUSNAM, HEX(CUSNAM) AS hex_value
FROM PRODLIB.CUSTMAST
WHERE CUSNAM LIKE '%?%'  -- caracteres no convertidos

Validaciones obligatorias

  • Conteo de registros (source vs target)
  • Checksums y sumas de control en campos numericos
  • Integridad referencial (FKs implicitas)
  • Caracteres especiales (acentos, ñ, simbolos)
  • Fechas limites y valores nulos
  • Precision decimal (packed 7,2 = numeric 7,2)

Validaciones recomendadas

  • Sampling aleatorio manual (50-100 registros)
  • Tests de regresion de aplicacion
  • Performance del destino con datos reales
  • Orden de clasificacion (EBCDIC sort != UTF-8 sort)
  • Reportes de reconciliacion automatizados
  • Validacion por usuarios de negocio clave

Estrategias de migracion

Big Bang (cutover unico)

  • Toda la data se migra en una ventana
  • Simple de planificar y ejecutar
  • Requiere downtime significativo (horas/dias)
  • Rollback = restaurar backup completo
  • Riesgo alto si algo falla
  • Adecuado para bases pequenas (<50 GB)

Trickle (incremental + CDC)

  • Carga inicial + sincronizacion continua
  • Mas complejo de implementar
  • Downtime minimo en el cutover final
  • Rollback = seguir usando origen
  • Riesgo bajo: ambos sistemas en sync
  • Necesario para bases grandes (>50 GB)

Migracion trickle: timeline tipico

Semana 1-2Setup de CDC, configuracion de journaling en tablas fuente
Semana 3-4Carga inicial completa (full extract + load)
Semana 5-8CDC en marcha: sincronizacion continua, validacion diaria
Semana 9-10Testing de aplicacion contra datos migrados
Semana 11Dress rehearsal: simulacion de cutover completo
Semana 12Cutover real: detener apps, sync final, switch, validar
Regla de oro: Siempre hacer un dress rehearsal (ensayo general) completo antes del cutover real. Migrar toda la data, ejecutar todas las validaciones, y hacer rollback. Esto revela problemas que no se ven en tests parciales y calibra los tiempos reales de la migracion.

Checklist de migracion

Pre-migracion

Inventario completo de tablas, vistas, indices y triggers
Documentar relaciones implicitas (no hay FKs? documentar la logica)
Identificar campos con formatos especiales (fechas como numeros, packed decimal)
Mapear CCSIDs de todas las tablas fuente
Estimar volumenes (filas, GB) y tasa de cambio diaria
Definir criterios de exito (conteos, checksums, sampling)
Establecer rollback plan documentado y probado

Durante la migracion

Ejecutar validaciones de conteo en cada tabla migrada
Verificar checksums de campos numericos
Validar caracteres especiales (muestreo manual)
Monitorear logs de error del proceso ETL/CDC
Documentar cada problema encontrado y su solucion
Mantener comunicacion continua con stakeholders

Post-migracion

Reporte de reconciliacion completo (source vs target)
Tests de regresion de aplicacion con datos migrados
Validacion por usuarios de negocio (UAT)
Performance testing en destino
Verificar backups del nuevo sistema
Documentar proceso completo para futuras migraciones
Mantener acceso al origen por 30-90 dias (rollback window)