inro

Hoja de ruta

Cuatro fases, cada una terminando con mediciones que deciden si merece la pena empezar la siguiente. Ninguna fase posterior empieza sin los números de la anterior — y dos de las cuatro las cerró su propia puerta: las mediciones dijeron que el trabajo no merecía la pena.

Las cifras medidas van marcadas como tales y salen de bancos de pruebas del repositorio. El resto — las fases 3 y 4, y la tabla de más abajo — siguen siendo proyecciones del diseño.

Fase 1 — Núcleo (Hecha · medida)

El formato de fichero, el B+tree copy-on-write, la lista de libres, las transacciones, los índices secundarios, delete_range y el cifrado. Los seis criterios de salida cumplidos y medidos.

Criterios de salida:

  • Pruebas basadas en propiedades e inyección de fallos en verde.

  • 34,14 bytes por entrada de registro, frente a un umbral de 40, medido con un millón de registros sintéticos.

  • Una base recién creada de 1.536 bytes, por debajo de los 2 KB que pedía el diseño.

  • Apertura de coste constante: 4 lecturas físicas a 30 KB, a 14 MB y a 2 GB.

  • Un banco comparativo contra SQLite sobre el esquema real de quota, que inro gana.

  • Cero dependencias de ejecución y cero unsafe en el crate.

Fase 2 — RAM y CPU (Hecha · medida)

Sin tocar el formato en disco. Las mediciones movieron el objetivo en vez de cumplirlo: una base abierta cuesta 19,7 KB de RAM, no los 832 KB que estimaba el diseño, así que casi todo lo que esta fase iba a construir no protegía de nada.

Lo que sí entregó es lo único que el diseño no había visto: la amplificación física de escritura bajó de 335× a 1,71×, y a 1,10× con lotes mayores, guardando las páginas sucias hasta el commit en lugar de escribir cada una al pasar. Para una flota de 2.700 inquilinos son 4,1 GB de escrituras al día en vez de 813.

Una medición contradijo a otra, y se conservan las dos: la curva de caché es plana en búsquedas dispersas pero no sobre un conjunto de trabajo caliente, donde 32 páginas hacen nueve veces menos lecturas que 4. El valor por defecto se queda en 4 porque en el edge se escribe más de lo que se lee.

Fase 3 — Segmentos append-only (No se abre)

Sustituiría el B+tree por segmentos secuenciales y un índice disperso en el espacio de claves de registro. Unas 700 líneas, y la única fase que cambia el formato en disco del motor — por eso era la única especificada con una puerta: medir antes de comprometerse.

La puerta la cerró. Dos de sus tres criterios de salida ya los cumplía el trabajo de las fases 1 y 2: las hojas están al 99,5% de llenado, y podar medio millón de filas cuesta 2,1 ms. El tercero — amplificación de escritura cerca de 1,0× — llevaría una flota de 2.700 inquilinos de 4,1 a 2,4 GB de escrituras al día, en una máquina que ya escribe 9,1 GB diarios por su cuenta.

Fase 4 — Codificación columnar (No se abre)

Delta y bitpacking en las marcas de tiempo, RLE en las columnas de baja cardinalidad, frame-of-reference en latencias y tamaños, diccionario y bitpacking en los identificadores internados, LZ4 sobre el bloque. Objetivo: 13 bytes o menos por entrada de registro, frente a los 34 de hoy.

Necesita los segmentos sellados de la fase 3, y su propio premio no carga con las dos: en 2.700 inquilinos llevaría la flota de 6,06 a 4,35 GB. Son 1,71 GB ahorrados en un disco con 59 GB libres, a cambio de 1.300 líneas de códecs y un LZ4 escrito a mano — el motor prohíbe el crate que usa todo el mundo, porque contiene unsafe.

Qué reabriría las dos: una flota un orden de magnitud mayor, donde 1,71 GB pasan a ser 17. Un tier que guarde los registros crudos meses en vez de días, donde los bytes por entrada vuelvan a importar. O un despliegue donde escribir sea el recurso caro — una tarjeta SD en un nodo IoT de verdad, no un volumen de servidor. Ninguna de las tres es descabellada. Ninguna es cierta hoy.

Qué viene ahora de verdad (En curso)

Con las fases 3 y 4 cerradas por medición, el trabajo que viene no es una fase del motor. El motor está terminado para la carga con la que se construyó; lo que queda es meterlo bajo esa carga, más tres piezas de su superficie que son ausencias deliberadas y con nombre, no descuidos.

  • Bajo el edge de quota. Un fichero por inquilino, con la configuración que fijaron las mediciones: páginas de 512 B en micro, 2 KB en starter, 4 KB en pro y enterprise; caché de 4 páginas; Sync::Commit; y un tope de páginas por tier. El tamaño de página no se puede cambiar en un fichero existente, así que un nodo que asciende de tier conserva la geometría con la que se creó salvo que la reprovisión recree la base.

  • Una reconstrucción de índice explícita. Hoy un desajuste de versión es siempre un error, y a propósito: reconstruir implica releer y reescribir un espacio de claves entero, disparado por el hecho de abrir la base. Lo que falta es el verbo que el llamante invoca a sabiendas.

  • El borrado de rango sobre espacios de claves indexados. Hoy se rechaza, porque la propiedad que hace rápido el borrado de rango — que nunca lee las hojas — es la misma que hace imposible saber qué entradas de índice retirar. La vía existe sobre el papel y necesita su propio diseño, porque una declaración falsa corrompe un índice en silencio.

  • La compactación. reclaim_tail devuelve al sistema de ficheros la cola libre del fichero; no baja las páginas vivas. Un inquilino que baja de tier de forma permanente recupera hoy su cola. Uno cuyas páginas vivas están arriba, no.

Ninguna de las tres piezas del motor tiene fecha. Están escritas para que la ausencia se lea como una decisión con un motivo detrás, que es el único tipo de ausencia alrededor de la que se puede planificar.

Cifras por fase, nodo pro

Fase Disco Amplificación de escritura RAM por base
1 — núcleo (medido) 6,3 MB 1,71× 19,7 KB
2 — RAM y CPU (medido) 6,3 MB 1,10× con lotes mayores 19,7 KB
3 — segmentos 40 MB ~1,0× ~34 KB
4 — columnar ~15 MB ~1,0× ~34 KB

Las dos primeras filas están medidas; las dos últimas son lo que habrían entregado las fases 3 y 4, y son la razón de que no se construya ninguna. El disco es el estado estacionario de un inquilino real a lo largo de 30 días simulados, no un cálculo — por eso la fase 1 marca 6,3 MB y no los 46 MB que esperaba el diseño. Ese hueco, encontrado midiendo, es lo que dejó sin sentido las dos últimas fases.

Un servidor con 4.500 nodos micro o starter y 500 nodos pro: unos 640 MB de RAM según lo proyectaba el diseño, unos 61 MB con todas las bases abiertas a la RAM que este motor cuesta de verdad, y unos 12 MB si el 95% están inactivas y cerradas.