La mayoría de los trabajos de exportación de bases de datos son accidentalmente orientados a filas. Obtienen una fila, asignan un conjunto de valores, convierten esos valores a otra representación y eventualmente los reconstruyen como columnas. Es cómodo de escribir y caro de operar.
postgres_to_parquet parte de una premisa distinta: la extracción es una transformación de stream acotada. El proceso debería moverse a través de una cantidad fija de memoria sin importar el tamaño de la tabla, y los valores deberían permanecer en su representación compatible con el protocolo durante el mayor tiempo posible.
Preserva la forma de los datos
El protocolo binario COPY de PostgreSQL evita el parseo de texto en la ruta crítica. El extractor lee lotes, mapea el esquema una vez y añade los valores directamente a sus escritores de columna de destino. Una fila es solo un envoltorio de transporte, no la abstracción que gobierna el layout de memoria.
while let Some(batch) = copy_reader.next_batch().await? {
parquet_writer.append(batch.columns())?;
if parquet_writer.is_full() {
parquet_writer.flush_row_group()?;
}
}
El propósito de este bucle no es la astucia. Hace visible el contrato de recursos: llegan los lotes, se llenan buffers de columna acotados, un row group se descarga y el proceso continúa.
Un modelo de throughput práctico
La velocidad sostenida está limitada por la etapa más estrecha del pipeline:
$$T_{pipeline}=\min(T_{postgres},T_{network},T_{encode},T_{storage})$$
Optimizar un paso de serialización por encima del techo de red o almacenamiento no aumentará el throughput de extremo a extremo. Instrumentar cada etapa sí revela dónde vale la pena hacer un cambio. Las métricas útiles son bytes leídos, filas decodificadas, row groups descargados, presión de asignación y latencia de escritura.
Zero-copy es una disciplina
Zero-copy no significa que nunca ocurra una asignación. Significa que las copias son deliberadas y se difieren hasta que una frontera de formato las requiere. En esta ruta, eso mantiene ciclos de CPU disponibles para compresión y codificación en lugar de objetos transitorios de string y valor.
El diseño final es sencillo de razonar: un lector ligado al esquema, un lote acotado, un escritor de columnas y backpressure en cada frontera cara. Eso es lo que permite que una exportación siga siendo predecible cuando pasa de una tabla de prueba a una de tamaño de producción.