Qué Cambia Cuando Tu Esquema de ClickHouse® Es Código
Si construyes sobre Postgres o MySQL, seguramente llevas años sin escribir una migración a mano. Cambias un fichero de esquema, una herramienta calcula el diff, genera el SQL y te tumba la CI si producción se ha desviado en silencio de lo que dice tu repo. Esa es la versión aburrida y resuelta de la gestión de esquemas. Aburrido es el mayor cumplido que le puedes hacer a lo que muta tu base de datos.
Ahora abre tu proyecto de ClickHouse®. Vuelves a escribir DDL a mano, a leer diffs con los ojos, y a enterarte del drift cuando una query se rompe en producción.
No es que ClickHouse no tenga tooling de migraciones, pero nunca ha sido un valor por defecto, esa cosa obvia a la que un equipo tira igual que el mundo relacional tira de sus herramientas de esquema. La mayoría de lo que hay son SQL runners: aplican los ficheros que escribes y llevan la cuenta de cuáles se ejecutaron, pero el motor de diffs sigues siendo tú. Sigues decidiendo qué cambió, y sigues cruzando los dedos para que dev y prod no se hayan desviado.
Nos cansamos de ser el motor de diffs. Así que construimos CHKit, y lo estamos liberando como open source. Este post no es un tour de features. Va sobre lo que cambia de verdad en tu teclado cuando el esquema de ClickHouse vive en código.
Por qué lo construimos
Corrimos ClickHouse en nuestra empresa anterior, Numia, a una escala cercana al petabyte, alimentando APIs en tiempo real sobre datos de blockchain. El motor en sí rara vez era el problema. El problema era todo lo que lo rodeaba, y la gestión de esquemas estaba muy arriba en la lista.
Cada cambio era DDL escrito a mano, revisado a ojo. La forma real de una tabla era lo que produjera el montón de ficheros de migración. Un ALTER manual lanzado durante un incidente nunca volvía al control de versiones, y nada nos decía que nuestro código y nuestra base de datos ya no coincidían. Escribimos un montón de tooling para tapar esto: health checks, scripts, guardarraíles.
Pero el problema del esquema seguía ahí, y la causa raíz era no tener una historia de schema-as-code como la tiene Postgres. Así que construimos una. Si quieres la versión larga de por qué, y de por qué decidimos abrirlo, hay un post complementario en el blog de CHKit. Aquí me quiero quedar en el lado práctico.
Tu esquema de ClickHouse como código
chkit define tus tablas, vistas y materialized views de ClickHouse como TypeScript (Python en camino). Aquí tienes una tabla real, con las cosas que de verdad configuras en producción:
import { schema, table } from '@chkit/core'
const events = table({
database: 'analytics',
name: 'events',
columns: [
{ name: 'id', type: 'UInt64' },
{ name: 'org_id', type: 'String' },
{ name: 'event', type: 'LowCardinality(String)' },
{ name: 'received_at', type: 'DateTime64(3)' },
{ name: 'payload', type: 'String', codec: [{ kind: 'ZSTD', level: 3 }] },
],
engine: 'MergeTree()',
orderBy: ['org_id', 'received_at'],
primaryKey: ['org_id', 'received_at'],
partitionBy: 'toYYYYMM(received_at)',
ttl: 'received_at + INTERVAL 90 DAY',
})
export default schema(events)
Ese es el estado deseado de tu base de datos, con tipos comprobados y revisable en un pull request. El bucle a su alrededor son tres comandos. chkit generate compara tus definiciones contra el último snapshot y escribe un fichero de migración SQL. chkit migrate --apply ejecuta las migraciones pendientes. chkit drift inspecciona la base de datos viva y te dice en qué se diferencia de tu código. Dos más se ganan su sitio en CI: chkit check tumba el build si hay migraciones pendientes, drift o una migración editada, y chkit codegen genera tipos de fila en TypeScript a partir de las mismas definiciones.
No es un ORM. Sigues escribiendo tu propio SQL para las queries. CHKit se encarga del esquema, las migraciones y los guardarraíles, nada más.
Una cosa por delante: está en beta. La CLI y el DSL de esquema son estables y corren nuestras propias cargas de producción, pero puede que aún hagamos pequeños cambios que rompan cosas antes de la 1.0. La referencia más a fondo vive en la documentación de chkit.
Qué cambia cuando el esquema es código
Aquí está la parte que importa, y la razón por la que esto merece un post.
Dejas de escribir migraciones a mano
Cambias el TypeScript y ejecutas chkit generate. Hace el diff del nuevo estado contra el anterior, calcula el conjunto ordenado de operaciones y te escribe el SQL. Si no cambió nada, no escribe nada.
El fichero de migración deja de ser algo que escribes y pasa a ser algo que revisas. El trabajo mental cambia de "escribir el ALTER correcto para este cambio" a "leer lo que CHKit propone y aprobarlo". En un esquema con mucho movimiento, esa es la diferencia entre una tarde con cuidado y un pull request de treinta segundos.
El drift pasa a ser un gate de CI en vez de una sorpresa a las 3 de la mañana
Este es el que paga la herramienta entera.
El fallo siempre es el mismo. Alguien arregla un incidente a las 3 de la mañana lanzando un ALTER directo contra producción. Funciona. El incidente se cierra. El cambio nunca llega a un fichero de migración, y ahora tu repo y tu base de datos no coinciden, en silencio, hasta que semanas después un deploy hace algo que nadie sabe explicar.
chkit drift lee la base de datos viva y la compara, columna a columna, contra tu código. Pilla la columna que falta, el TTL que cambió y la sorting key que no es la que tu repo cree. Mete chkit check en CI, y esa comparación corre en cada pull request, así que el ALTER de las 3 de la mañana aparece a la mañana siguiente como una diferencia concreta y con nombre, en lugar de una vaga sensación de que algo va raro. La idea no es que la gente deje de tocar producción. No lo va a hacer. La idea es que la base de datos deja de poder guardarte un secreto.
Los cambios destructivos no pueden dispararse por accidente, y los estructurales se anuncian solos
Dentro de "cambio de esquema" se esconden dos problemas distintos, y ClickHouse los trata muy distinto.
Algunos cambios son ediciones de metadatos baratas: añadir una columna, cambiar un TTL, añadir un índice. Otros reescriben la tabla. Cambiar el engine, el ORDER BY, el particionado o la primary key no es una edición en el sitio, para nada. ClickHouse no tiene un ALTER para eso. El único camino es crear una tabla nueva, copiar los datos y cambiarlas. Si alguna vez has cambiado una sorting key en una tabla grande y lo has visto convertirse en una tarde de INSERT SELECT, sabes exactamente de qué hablo. (Nos metimos en por qué el orden de la sorting key importa tanto en nuestro post de optimización de queries.)
CHKit etiqueta cada operación de un plan como safe, caution o danger, y sabe qué cambios son reescrituras estructurales y cuáles ediciones en el sitio. Un DROP COLUMN, un DROP TABLE, cualquier cosa que destruya datos, se etiqueta como danger y se bloquea. En una terminal interactiva, te sale un aviso. En CI sale con código distinto de cero y se niega a correr salvo que pases explícitamente --allow-destructive. Esa verificación también escanea el SQL escrito a mano, no solo las migraciones que generó el propio CHKit, así que no puedes colar un DROP a pelo sin que lo pille.
Los tipos de tu aplicación dejan de desincronizarse de tu esquema
chkit codegen genera tipos de fila en TypeScript a partir de las mismas definiciones de las que salen tus tablas. Corre chkit codegen --check en CI, y el build falla cuando los tipos de tu aplicación y el esquema de tu base de datos se salen de sincronía. Es el mismo problema de drift de antes, un nivel más arriba, cerrado de la misma forma.
¿Qué no hace CHKit?
Un post de lanzamiento que solo lista victorias es marketing, así que aquí están los bordes.
CHKit gestiona el esquema, o sea DDL. No mueve tus datos. Hay un plugin de backfill opcional para copias por ventanas de tiempo con checkpoints, pero es algo a lo que te apuntas, no parte del bucle de migrate del core, y no hace fácil la parte difícil. Reformar una tabla grande sigue siendo una migración de datos de verdad que tienes que planear. Todo el mundo en este espacio sufre con esa.
Además emite DDL de un solo nodo en el camino por defecto. Si corres un clúster replicado con Keeper, esa replicación la gestionas tú (tenemos pensado incluir esta feature en un futuro cercano). Y un par de comodidades, como el uniqueKey gestionado, solo aplican al engine de ObsessionDB. CHKit funciona contra cualquier ClickHouse, pero las features de Cloud son exactamente eso.
Por último, CHKit no puede deshacer una mutación de ClickHouse. Lo que hace es decirte, antes de que ejecutes nada, que el cambio de aspecto inofensivo de tu diff es en realidad una reescritura de tabla entera. Los datos se mueven igual. Solo que dejas de enterarte un viernes por la tarde.
La regla que merece la pena guardar
Si te quedas con una cosa: schema-as-code se gana el sueldo en cuanto más de una persona, o más de un entorno, puede cambiar tus tablas. Un ingeniero en un clúster puede tirar con una carpeta de migraciones y buenos hábitos. Un equipo con staging y prod separados, y una pipeline de CI que puede lanzar un ALTER, no. Ese es el punto en el que el drift deja de ser hipotético.
Y cuando un diff vuelve etiquetado como danger o estructural, para y léelo. Eso es CHKit diciéndote que este cambio mueve datos. Yo planearía el backfill antes de aprobarlo, siempre, en lugar de descubrir la reescritura en producción.
El mundo relacional dejó de escribir migraciones a mano hace mucho. ClickHouse no tiene por qué ser la excepción.
Pruébalo
CHKit tiene licencia MIT y funciona con cualquier ClickHouse, incluidos Cloud, Altinity, autoalojado y ObsessionDB. Puedes montar un proyecto desde un ejemplo que funciona con un solo comando:
npm create chkit@latest
El código está en GitHub. Si corres ClickHouse y sigues gestionando el esquema a mano, construimos esto para que no tengas que hacerlo. Y si quieres ClickHouse gestionado sin el impuesto operativo que empezó toda esta historia, eso es lo que hacemos en ObsessionDB.
Nos gustaría de verdad saber cómo gestionas hoy los cambios de esquema en ClickHouse, sobre todo el drift y los ALTER destructivos. Abre un issue y cuéntanos qué falta.
Seguir Leyendo
Publicado originalmente en obsessionDB. Lee el artículo original aquí.
ClickHouse is a registered trademark of ClickHouse, Inc. https://clickhouse.com