Aviso desde el principio: trabajo para croit, que ha escrito esto. Todo lo que sigue está leído del código fuente y la documentación publicados de los dos proyectos, enlazados al pie de la página, y donde la documentación de croit contradice el código de croit, lo digo.

El OSD de Ceph, reescrito desde cero

croit ha publicado ceph-rust, un OSD de Ceph escrito desde la nada en Rust, con un almacén de objetos propio llamado FlashStore en lugar de BlueStore1. Entró en vista previa pública el 9 de noviembre de 2026, con la disponibilidad general prevista para el primer trimestre de 20271. No es un fork. La regla del propio proyecto es que el C++ de Ceph se lee como referencia, se redacta como especificación con palabras de croit y luego se implementa a partir de la especificación2.

Lo que importa a quien opera Ceph es la promesa de sustitución directa. Tus monitores, tus managers, la CLI ceph y tus clientes se quedan como están. Un OSD FlashStore entra en los mismos pools y los mismos placement groups que los OSD clásicos que ya están ahí, y cambias un disco cada vez1.

Qué se cambiaceph-mon, ceph-mgr21.2.0 clásicoclienteslibrados, librbd, krbdun pool, los mismos placement groupsceph-osdBlueStoreRocksDB sobre BlueFSC++, de Cephcroit-flashstore-osdFlashStoreCOW B+tree + WALRust, de croit
Solo cambia el OSD. Monitores, managers y clientes siguen siendo Ceph clásico, y un mismo placement group puede tener una copia en cada tipo.

Así que la pregunta para un operador es práctica. Si cambias un OSD BlueStore por uno FlashStore, ¿qué es distinto por debajo? No en abstracto, y no frente a un BlueStore de una charla de congreso de hace años. Frente al BlueStore que de verdad ejecutas hoy si estás en Umbrella, Ceph 21.2.0, que es la versión contra la que se compila y se prueba FlashStore1.

Esa es la comparación aquí, y solo esa. El BlueStore de Umbrella tal como lo describen su código y su documentación, frente a FlashStore tal como lo describen su código y sus registros de decisiones.

Afecta a cualquiera con clústeres Ceph sobre NVMe, y a cualquiera que haya mirado cuántos núcleos le cuesta un OSD. A los clústeres de HDD no les afecta en absoluto, y el motivo sale más abajo.

Qué hay en el repositorio

El repositorio es una instantánea de solo lectura del árbol de desarrollo privado de croit. Sin gestor de incidencias, sin pull requests; las actualizaciones llegan como nuevos commits de instantánea1. Eso corresponde a la fase de preview, que existe para recoger las opiniones de la comunidad mientras croit pasa internamente sus casos de prueba de producción antes de marcarlo como seguro para su uso. El desarrollo se abre tras la primera versión de producción, como en la mayoría de los proyectos de código abierto respaldados por una empresa. Esta tiene fecha del 10 de noviembre de 2026 y lleva unas 700.000 líneas de Rust y más de 450 registros de decisiones de arquitectura, los ADR, que son los documentos breves donde queda escrito por qué se tomó cada decisión de diseño.

Los ADR son la parte útil para una comparación como esta. Son directos, llevan mediciones y recogen lo que se probó y se tiró además de lo que se quedó.

ComponenteQué sustituyeVista previaGA (prevista)
FlashStore OSDceph-osd + BlueStorenov. 2026T1 2027
librustyradoslibradosnov. 2026T1 2027
librustyrbdlibrbdnov. 2026T1 2027
Transporte RDMA (RoCEv2)nada en Umbrellanov. 2026T1 2027
CephFS2, RGW en RustMDS, RGWT1 2027T2 2027
ceph-mon, ceph-mgrno se sustituyenn/dn/d

Conviene conocer las licencias antes de construir nada encima. El daemon y las herramientas son LGPL-3.0-only, las bibliotecas cliente son MPL-2.0 y la documentación es CC-BY-SA-4.03. Las bibliotecas cliente pasaron a MPL para que un programa en Rust pueda enlazarlas estáticamente sin las obligaciones de reenlazado que trae la LGPL, lo cual es una decisión razonada y está escrita como tal3.

Vista previa significa lo que dice. El propio README de croit dice que es para pruebas y evaluación y «not yet recommended for production data»1. Yo tampoco pondría datos de clientes encima todavía, y nada en el resto del artículo cambia eso.

Dos almacenes, uno al lado del otro

Un OSD tiene dos mitades. La mitad de arriba habla el protocolo de Ceph, es dueña de los placement groups, hace peering, recupera y hace scrub. La mitad de abajo, el almacén de objetos, convierte «escribe estos bytes en este objeto» en bloques en un disco y promete que sobreviven a un corte de corriente.

BlueStore es la mitad de abajo de Umbrella. Su documentación dice que «reads and writes to the devices directly in a ‘raw’ fashion»4, y que consume «raw block devices or partitions»5. Los datos de los objetos van directos al dispositivo de bloques. Todo lo demás, los metadatos de los objetos, el OMAP y su propia contabilidad, va a una base de datos clave/valor RocksDB embebida, y RocksDB se apoya en BlueFS, un pequeño sistema de ficheros que BlueStore lleva para ella56.

FlashStore se quita todo eso de encima. Un único B+tree copy-on-write por shard del almacén lo guarda todo: colecciones, metadatos de objetos, atributos extendidos, mapas de extents, OMAP, el PG log, contadores de referencias y hasta el propio bitmap del asignador7. A su lado hay un anillo de registro de escritura anticipada (WAL) de tipo redo y la región de datos. No hay RocksDB, no hay BlueFS y no hay una segunda base de datos que afinar.

Dos pilas bajo un OSDBlueStore de UmbrellaFlashStoreceph-osd8 shards de op × 2 hiloscapa de objetos BlueStoreasignador hybriddatos de objetosbloque raw, aioRocksDBonodes, omap, diferidasBlueFSblock o block.dbcapa de bloques del kernelvaciado antes del commitHDD, SSD o NVMe, cualquier particióncroit-flashstore-osd1 hilo fijado por núcleoshard del almacén por núcleoasignador propio, sin locksdatos de objetosextents nuevos de 4 KiBB+tree COWmeta, omap, PG loganillo de WALlog redo, por shardSPDK (vfio) o io_uringsin vaciado en la ruta calienteNVMe con PLP, 4 KiB, namespace entero
El BlueStore de Umbrella separa datos y metadatos arriba y manda los metadatos por RocksDB y BlueFS. FlashStore separa por núcleo, y dentro de cada shard un solo árbol guarda todo lo que no son datos de objetos.

La otra gran diferencia es el propio disco. BlueStore pasa por la capa de bloques del kernel hacia lo que le des. FlashStore habla con el NVMe o bien con SPDK, un driver en espacio de usuario que le quita el dispositivo al kernel mediante vfio, o bien a través del kernel con io_uring89. SPDK es el backend por defecto. El nivel io_uring es el que funciona donde vfio no, y como tal es en el que acabé en el montaje en Proxmox.

Dónde viven los metadatos

Empieza por aquí. Casi todo lo demás sale de esto.

BlueStore de UmbrellaFlashStore
Metadatos de objetosclaves de RocksDBB+tree COW por shard
OMAPclaves de RocksDBel mismo B+tree por defecto; LSM o RocksDB opcional en prepare
PG logclaves de RocksDBel mismo B+tree
Estado del asignadorasignador hybrid, freelist en RocksDBbitmap por shard, dentro del árbol
Dispositivo de metadatosblock.db y block.wal opcionalesninguno, el mismo NVMe
Checksums de datoscrc32c por cada 4 KiB por defectocrc32c por cada 4 KiB, en el mapa de extents
Almacenes por OSDunouno por núcleo

Los valores por defecto del lado de BlueStore salen directamente del fichero de opciones de Umbrella: RocksDB como backend clave/valor, el asignador hybrid y checksums crc32c61011. El checksum de metadatos de BlueStore «is handled by RocksDB and uses the crc32c algorithm», y los datos le cuestan «a checksum value (usually 4 bytes) for every 4 KB block of data»4. En checksums los dos almacenes coinciden. FlashStore guarda un crc32c por cada bloque de 4 KiB en el mapa de extents y lo verifica al leer7.

Donde se separan es en la estructura. El RocksDB de BlueStore es un árbol LSM, así que escribe y luego compacta en segundo plano, reescribiendo datos que ya había escrito. El árbol de FlashStore se actualiza por copy-on-write y se vuelca a disco en checkpoints: cada cinco segundos mientras haya algo sin persistir, o cuando el anillo del WAL llega a la mitad7.

Otra consecuencia es que FlashStore no tiene dispositivo rápido para metadatos, porque no lo necesita. En Umbrella puedes poner block.db en un disco más rápido, y BlueStore pondrá «as much metadata as it can on the DB device»4. FlashStore da por hecho que todos los discos ya son NVMe rápidos, y guarda sus metadatos junto a los datos. Es una suposición razonable para el hardware al que apunta. También es exactamente la razón de que no haya nada para HDD.

El OMAP es el único sitio donde FlashStore se cubre las espaldas. El árbol es lo predeterminado, pero un almacén se puede preparar con un motor LSM propio de croit, o con RocksDB, detrás de la misma interfaz12. El motor de OMAP se elige al crear el almacén. No se cambia en un OSD en marcha.

Una escritura de 4 KiB, de dos maneras

Esta es la diferencia que notarías, si es que notas alguna.

Toma una escritura de 4 KiB en un objeto en Umbrella con la configuración por defecto sobre flash. bluestore_min_alloc_size_ssd es 4 KiB y bluestore_prefer_deferred_size_ssd es 0, así que una escritura entera de 4 KiB va a 4 KiB recién asignados1314. BlueStore envía esa escritura de datos, y la transacción se queda en STATE_AIO_WAIT hasta que el disco dice que ha terminado15. Solo entonces se encolan los metadatos para RocksDB, y el hilo de sincronización de RocksDB vacía el dispositivo de bloques antes de hacer commit16. Después el cliente recibe su confirmación.

Dos viajes al dispositivo, en serie.

FlashStore envía los dos a la vez. Los datos van a un extent nuevo de 4 KiB y la unidad de WAL va al anillo del log en paralelo. El registro del WAL lleva el crc32c de cada bloque de datos nuevo, así que el replay tras un fallo puede saber si los datos llegaron7. La confirmación sale, en orden del log, cuando los datos, la unidad de WAL y todas las transacciones anteriores son duraderos7. La justificación del propio ADR para el diseño es «parallel commit gives one device latency per ack»7.

Una escritura de 4 KiB, contra el relojBlueStoreescritura de datos4 KiB nuevos, aio, esperacommit de RocksDBlog de BlueFS + vaciadoack2 viajesFlashStoredatos, 4 KiB nuevosunidad WAL, crc datosack1 viajetiempo
La misma escritura de 4 KiB. BlueStore espera a los datos antes de hacer commit en RocksDB; FlashStore escribe los datos y su registro de log a la vez.

Dos cosas mantienen esto en su sitio. En un OSD con carga los dos almacenes agrupan commits, así que bajo carga la diferencia es menor que una latencia de dispositivo por escritura. Y un viaje al dispositivo en NVMe son unas pocas decenas de microsegundos. Que lo notes depende de si tus clientes esperan por latencia con poca profundidad de cola, como suelen hacer las bases de datos sobre RBD, o empujan rendimiento, donde se diluye.

croit intentó ir más lejos, a una sola escritura de dispositivo por transacción, al estilo ZFS. Lo construyeron, lo midieron y lo tiraron: la ganancia solo aparecía con profundidad de cola 1 y la complejidad no compensaba17. Eso lo valoro más que cualquier gráfico de benchmark. Es un registro de decisión que dice «lo construimos y no merecía la pena», en público.

La escritura de menos de 4 KiB

La escritura más pequeña es donde los dos se separan de verdad.

Pongamos que un cliente sobrescribe 512 bytes en medio de un bloque que ya tiene datos. Umbrella sigue usando por defecto su ruta de escritura original; bluestore_write_v2, la reescritura, viene desactivada18. En esa ruta, una escritura más pequeña que min_alloc_size en un bloque que ya está asignado se convierte en una escritura diferida19. Los datos viajan dentro de la transacción de RocksDB como una clave con el prefijo L, y solo van a su sitio real después de que al cliente se le haya dicho que están a salvo2021.

Una escritura de menos de 4 KiBBlueStore, diferida1 leer el restorellenar a 4 KiB2 datos en clave Llog de RocksDB3 commit, vaciadocliente confirmado4 escribir en sitiomismos 4 KiB otra vez5 borrar clave Lotro commitFlashStore4 KiB nuevos enteros + unidad WALen paralelo, nunca en su sitiocliente confirmadoel viejo, libre luego
La ruta diferida de Umbrella para una escritura de menos de 4 KiB en un bloque usado, frente al extent nuevo de FlashStore. El rosa marca cada vez que se escriben los propios datos.

Así que los datos llegan al disco dos veces. La documentación de Umbrella no lo esconde: «writes smaller than min_alloc_size must first pass through the BlueStore WAL»4.

FlashStore nunca sobrescribe datos vivos en su sitio, y punto7. Los objetos de hasta 2 KiB van más allá y viven enteros en el árbol, así que una escritura de WAL es todo el commit7.

Eso también significa que FlashStore no tiene cola de escrituras diferidas que vaciar, ni replay diferido al montar, ni claves L que limpiar. Lo paga en otro sitio: los bloques liberados se retienen hasta el siguiente checkpoint, así que el espacio liberado va con hasta un intervalo de checkpoint de retraso7.

Hilos frente a núcleos

La mitad de arriba del OSD difiere igual de mucho.

El ceph-osd de Umbrella es un servidor de hilos clásico, y cada número de la mitad izquierda del diagrama de abajo es uno de sus valores por defecto sobre flash22. Una operación cruza varios de esos pools por el camino. Comparten estado detrás de locks.

FlashStore mantiene todo el recorrido en un único núcleo fijado que hace polling, desde la tarjeta de red hasta el disco y vuelta23. Cada placement group pertenece exactamente a un núcleo, y también todo lo que toca el PG: «no locks, no atomics, no shared mutable state»24. El tráfico entre núcleos va por anillos de un solo productor, y la propiedad del mensaje viaja con él.

Hilos frente a núcleosceph-osd de Umbrella3 hilos de messengercola de ops mClock8 shards × 2 hilosbstore_kv_sync, bstore_kv_finalRocksDB, BlueFScroit-flashstore-osdnúcleo 0núcleo 1núcleo ncola NICmsgr2, PGsshardcola NVMecola NICmsgr2, PGsshardcola NVMecola NICmsgr2, PGsshardcola NVMeanillos SPSC entre núcleos, sin locks
Umbrella va pasando una operación entre pools de hilos. FlashStore la mantiene en un núcleo desde la red hasta el disco, y solo cruza núcleos mediante anillos.

El almacén sigue el mismo reparto. flashstore_store_shards usa por defecto un shard por núcleo, cada uno con su propio anillo de WAL, árbol y asignador, y un PG vive en exactamente un shard257. BlueStore es un almacén por OSD con un RocksDB.

Tiene un precio, y el ADR lo dice: «idle cores burn CPU (accepted; optional adaptive sleep later)»23. Un trabajo posterior añadió reposo adaptativo y esperas temporizadas al dispositivo para que un núcleo no gire durante toda la latencia del dispositivo con poca profundidad de cola8, pero el principio sigue en pie, y flashstore_cores vale 8 por defecto cuando no fijas un rango25. A FlashStore le das núcleos enteros.

En un nodo de almacenamiento dedicado, no hay problema. En una máquina hiperconvergente donde esos núcleos también corren VM, es la primera cuenta que hay que hacer.

El disco tiene que cumplir su promesa

BlueStore está escrito para apañarse con un disco que miente sobre cuándo están a salvo los datos. FlashStore se niega a tratar con ese disco.

BlueStore de UmbrellaFlashStore
Qué hace duradera una escrituravaciado del dispositivo de bloques antes del commit de RocksDBque la escritura termine en un disco con PLP
Vaciado en la ruta calientesíno
Disco con caché de escritura volátilse usa, y se vacíamkfs lo rechaza; comprobación del host FAIL
Forzarlono hace faltasolo laboratorio: bit FUA en cada escritura duradera

El lado de BlueStore es el hilo de sincronización kv ya citado16. El lado de FlashStore es el ADR de durabilidad y la comprobación del host: PLP significa que el disco no declara caché de escritura volátil, así que una finalización significa que los datos están en un medio estable2426.

Para su objetivo es la decisión correcta, pero ten claro lo que deja fuera, porque casi todo el NVMe de consumo tiene caché volátil y no tiene PLP, y lo mismo pasa con bastante material «de centro de datos» que se vende por precio. Sin PLP, no hay FlashStore. Pienses lo que pienses de lo demás.

En qué no funciona FlashStore

Pon los requisitos uno al lado del otro y el mercado al que apunta está claro.

BlueStore de UmbrellaFlashStore
MediosHDD, SSD, NVMesolo NVMe
Protección ante cortes de corrienteno obligatoriaobligatoria, mkfs se niega sin ella
Formato de sector512 B o 4 KiBse espera 4 KiB, avisa si no
Disposiciónparticiones, LVM vía ceph-volumenamespaces enteros, sin particiones
Dispositivo DB/WAL apartesoportadono existe
Cifrado en reposo--dmcrypt vía ceph-volumefuera de alcance
Clientes msgr1escucha por defectorechazados, solo msgr2
Cache tieringobsoleto, aún presenterechazado, el pool pasa a pasivo
Requisitos de kernelcualquiera soportadoio_uring: Linux 5.19+, o el 5.14 de RHEL 9 con backports; hugepages para SPDK

La columna de BlueStore es de Umbrella: ceph-volume cifra volúmenes lógicos «using dmcrypt by specifying the --dmcrypt flag»27, y ms_bind_msgr1 sigue valiendo true por defecto28. La columna de FlashStore sale de su referencia de hardware y de su decisión de alcance2629.

Dos de esas filas merecen más que una línea de tabla.

El cifrado en reposo queda fuera, con un motivo: SPDK se salta dm-crypt, y no está previsto cifrar dentro del almacén. La respuesta del ADR son discos NVMe autocifrados, o cifrar por encima del almacén, en el cliente o en RGW29. Si tienes un requisito de cumplimiento que nombra dm-crypt en el OSD, hoy eso te para en seco.

msgr1 queda fuera como riesgo aceptado. Los clientes de kernel anteriores a 5.11 y los librados anteriores a Nautilus no pueden hablar con un OSD FlashStore en absoluto29. La mayoría de las instalaciones hace tiempo que pasaron eso. Algunas no, y es la vieja máquina olvidada del rincón la que se entera.

Le dice al monitor que es BlueStore

Esta es la que va a pillar a la gente.

Por defecto, un OSD FlashStore arranca y les dice a los monitores osd_objectstore=bluestore30. No se esconde. Añade al lado sus propias claves flashstore_version y flashstore_build30. El motivo es la compatibilidad: un monitor de Umbrella solo permite funciones como las sobrescrituras EC en un pool cuando todos los OSD del pool declaran BlueStore, y los módulos del manager como Prometheus y telemetry toman decisiones según ese mismo valor30. El ADR deja claro que bluestore aquí «is a compat value for mon/mgr feature gates, not a product name»30.

Hay un interruptor, flashstore_report_objectstore=flashstore, que lo renombra todo: los metadatos, las claves bluestore_bdev_*, el perf logger, los comandos tell y los códigos de salud BLUESTORE_*. Necesita monitores parcheados, y el valor por defecto sigue siendo bluestore2530. Las compilaciones de Ceph de la propia croit llevan esos parches, así que en ellas FlashStore se declara flashstore y nada de lo que sigue aplica. El nombre bluestore está para los monitores y managers estándar que hoy corren en producción.

Lo que se le dice al monitorOSD FlashStorevalor por defectoosd_objectstore=bluestoreflashstore_version=…mon, mgr: BlueStoresobrescritura EC síOSD FlashStorereport=flashstoreosd_objectstore=flashstoreflashstore_bdev_*mon clásico: no es BlueStorerequiere mon parcheadoEl almacén es el mismo en las dos filas. Solo cambia el nombre que da.
El mismo almacén, dos nombres. El valor por defecto mantiene contentos a los monitores y managers de Umbrella, a costa de que los OSD FlashStore cuenten como BlueStore mires donde mires.

Entiendo por qué. Un control del monitor basado en una cadena es diseño de Ceph, no de croit, y negarse a decir bluestore desactivaría funciones en un clúster sin parchear. Ahí está la trampa en un clúster estándar. Cada dashboard, cada script de inventario y cada ceph osd count-metadata osd_objectstore que ejecutes contará los OSD FlashStore como BlueStore. Durante una migración, justo cuando más necesitas saber qué OSD es cuál, el campo obvio no te dice nada.

Así que mira flashstore_version en ceph osd metadata, no osd_objectstore. Ponlo en tu runbook antes de que entre el primer OSD FlashStore.

Los mismos mandos, otro comportamiento

FlashStore lee muchas de las opciones que Ceph ya tiene, y eso es sobre todo una buena noticia. No todas las opciones significan lo que significaban en Umbrella, y las que no son donde un clúster afinado se lleva la sorpresa.

OpciónBlueStore de UmbrellaFlashStore
compression_mode, por poolnone, passive, aggressive, forcelos mismos cuatro, el mismo significado
compression_algorithmsnappy por defectosnappy, zlib, zstd, lz4; nivel de zstd fijo en 1
bluestore_compression_max_blob_size_ssd64 KiBtrozos de 64 KiB
osd_memory_target4 GiB, cachés autoajustadas en vivose lee una vez al arrancar, dimensiona las cachés del almacén, sin autoajuste
osd_op_queuemclock_scheduleraceptada e ignorada
osd_mclock_*activasaceptadas e ignoradas
csum_typecrc32c por defecto, variantes xxhash disponiblescrc32c fijo; cualquier otro valor deja un aviso en el log31

La compresión coincide. El modo por defecto de BlueStore es none y su algoritmo por defecto es snappy3233; FlashStore implementa los mismos cuatro modos, las mismas opciones de pool y los mismos trozos de 64 KiB, y el ADR señala que un trozo de 64 KiB comprimible al 50% ocupa lo mismo en los dos34. No siempre iba a estar. El fichero del ADR todavía se llama no-store-compression, por la decisión original, revocada cuando croit fijó como mandato la paridad funcional completa con el Ceph clásico34.

La memoria no tiene paridad. Umbrella autoajusta las cachés de BlueStore contra osd_memory_target mientras funciona3536. FlashStore lee el objetivo una vez al arrancar, lo reparte entre sus shards, y un cambio hecho con ceph config set se aplica en el siguiente reinicio37. Tampoco mantiene el proceso dentro del objetivo: hugepages, buffers de red y estado de los PG quedan fuera37. Así que osd_memory_target dimensiona las cachés de FlashStore. No limita la memoria del OSD como puede que estés acostumbrado.

Y mClock se ignora sin más37. No se rebaja. Se ignora. No hay planificador de operaciones que configurar, porque cada núcleo es dueño de sus PG. La recuperación y el backfill se frenan en su lugar con osd_max_backfills, osd_recovery_max_active y las opciones de sleep37. Si afinaste perfiles de mClock para una reconstrucción en Umbrella, esos ajustes no hacen nada en un OSD FlashStore, y nada te va a avisar.

No hay conversión, en ningún sentido

No puedes convertir un OSD BlueStore en uno FlashStore. La guía de migración de croit lo dice claramente: «There is no in-place conversion of a BlueStore device»38. Ofrece dos órdenes en su lugar, y los dos son backfill38:

Añadir y luego quitarLiberar y luego añadir
Necesitauna ranura NVMe librenada libre
Paso 1preparar FlashStore en el disco nuevoceph osd out, esperar a clean
Paso 2esperar a active+cleanparar y purgar el OSD clásico
Paso 3quitar el OSD clásicopreparar FlashStore en el disco liberado
Riesgo mientras duraninguno más allá del backfill normaluna copia menos hasta que acabe el backfill

Y no se vuelve más fácil después. El formato en disco de FlashStore tiene bits de características, y un montaje rechaza un bit desconocido: «Every format change means mkfs; unknown bits refuse mount (no downgrade)»7. Así que un cambio de formato en una versión posterior de FlashStore es el mismo vaciar y rellenar que la primera migración.

El único caminoOSD BlueStoreosd.N en nvme0OSD FlashStoremkfs nuevosin conversión, en ningún sentidoel clústerbackfill desde copiasout, purgebackfill entrante
El backfill es el único puente entre los dos almacenes, y también es el puente entre dos formatos de FlashStore.

Hay una cosa que la documentación tiene mal. La lista de comprobación de la guía de migración dice que te asegures de que «the pools do not use erasure coding on croit-flashstore-osd OSDs»38. La guía de erasure coding de la misma instantánea dice que FlashStore sirve por defecto los pools EC heredados, y el valor por defecto del propio código para EC optimizado es all3940. Una de las dos está desactualizada, y por el código es la guía de migración. Aun así me leería entera la guía de EC antes de mezclar pools EC entre los dos tipos de OSD, porque el fallo que describe es un PG que se degrada y un backfill que se atasca39.

Un almacén hecho para los discos que de verdad compramos

BlueStore se hizo para funcionar en todo. Discos que giran, SSD baratos, NVMe, particiones, LVM, volúmenes cifrados, un dispositivo DB rápido delante de un dispositivo de datos lento. Esa amplitud es la razón de que se convirtiera en el predeterminado, y es la razón de que lleve una ruta de escritura diferida, un vaciado antes de cada commit, una base de datos clave/valor sobre un sistema de ficheros propio y un pool de hilos para que todo eso siga moviéndose. Cada una de esas piezas existe porque algún disco en algún sitio la necesitó.

FlashStore empieza por el otro extremo. Da por hecho que el disco es NVMe, que tiene protección ante cortes de corriente y que es lo bastante rápido como para que ahora el cuello de botella sea el software. Luego tira todo lo que solo existía por los otros discos.

¿Es la apuesta correcta? Para clústeres nuevos todo flash, montados con NVMe empresarial de verdad, sí. Ahí es hacia donde van los clústeres Ceph, y un OSD que gasta sus núcleos en locks, traspasos y una base de datos compactando a su espalda está pagando, cada segundo de cada día, por una flexibilidad que un clúster así no va a usar nunca.

Pero la apuesta tiene un coste, y prefiero que lo veas antes de entrar en ella. Renuncias a HDD, dm-crypt, msgr1, el ajuste de memoria en vivo y mClock. En un clúster estándar te llevas un OSD que les dice a tus monitores que es BlueStore, porque los monitores y managers actuales no le dejan otra; las compilaciones de croit no lo necesitarán. Te llevas un formato al que no puedes convertir, y entre cuyas versiones tampoco puedes convertir. Y te lo llevas en vista previa, de un único proveedor, que resulta que me tiene en plantilla.

Así que toma el código fuente como la prueba, no este artículo ni el informe de benchmarks de croit. Los registros de decisiones son públicos y dicen qué se midió y qué se tiró. Léelos, monta un clúster de pruebas con el material que de verdad desplegarías y mide tu propia carga. Si hace en tus discos lo que dicen los ADR, lo sabrás. Si no lo hace, también lo sabrás, y te habrás enterado en un clúster de pruebas.


  1. croit ceph-rust, README en la instantánea 48f9014 — componentes, estado y hoja de ruta, «not yet recommended for production data», compatible en el protocolo con v21.2.0 y v20.2.4. ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎

  2. ADR 0003, desde cero, sin fork de Crimson — «No fork, no linking of Ceph code»; el código de Ceph se lee como referencia, especificaciones con palabras propias. ↩︎

  3. ADR 0164, licencias — daemon y herramientas LGPL-3.0-only, bibliotecas cliente MPL-2.0, documentación CC-BY-SA-4.0, y el motivo de la MPL. ↩︎ ↩︎

  4. Ceph Umbrella, BlueStore configuration reference — acceso directo al dispositivo, dispositivo DB, «writes smaller than min_alloc_size must first pass through the BlueStore WAL», checksums. ↩︎ ↩︎ ↩︎ ↩︎

  5. Ceph Umbrella, storage devices — BlueStore «consumes raw block devices or partitions»; RocksDB embebido para los metadatos internos. ↩︎ ↩︎

  6. Ceph v21.2.0, bluestore_kvbackend — por defecto rocksdb. ↩︎ ↩︎

  7. ADR 0021, motor de almacenamiento — B+tree COW por shard, anillo de WAL, commit en paralelo, checkpoints, datos en línea, liberaciones retenidas, «Every format change means mkfs». ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎

  8. ADR 0004, E/S de almacenamiento — SPDK sobre vfio, ampliado a dos niveles con io_uring, reposo temporizado al dispositivo. ↩︎ ↩︎

  9. ceph-rust docs, prepare and activate — spdk es el backend por defecto, kernel y kernel-cmd dejan el NVMe en el driver del kernel. ↩︎

  10. Ceph v21.2.0, bluestore_allocator — por defecto hybrid. ↩︎

  11. Ceph v21.2.0, bluestore_csum_type — por defecto crc32c. ↩︎

  12. ceph-rust, uso de croit-flashstore-tool — «fresh prepare/mkfs –omap-engine btree|lsm|rocksdb». ↩︎

  13. Ceph v21.2.0, bluestore_min_alloc_size_ssd — por defecto 4 KiB. ↩︎

  14. Ceph v21.2.0, bluestore_prefer_deferred_size_ssd — por defecto 0. ↩︎

  15. Ceph v21.2.0, BlueStore.cc, máquina de estados de la transacción — las aios pendientes llevan la transacción a STATE_AIO_WAIT antes de encolarla para el commit clave/valor. ↩︎

  16. Ceph v21.2.0, BlueStore.cc, hilo kv sync — «flush/barrier on block device» antes del submit a RocksDB. ↩︎ ↩︎

  17. ADR 0027, commit atómico de una sola E/S, rechazado — «gain only at QD1», construido y retirado. ↩︎

  18. Ceph v21.2.0, bluestore_write_v2 — «Use faster write path», por defecto false. ↩︎

  19. Ceph v21.2.0, BlueStore.cc, _do_write_small — lectura de cabeza y cola, luego «chunk-aligned deferred overwrite». ↩︎

  20. Ceph v21.2.0, BlueStore.cc, «journal deferred items» — la transacción diferida se guarda como clave en la misma transacción de RocksDB. ↩︎

  21. Ceph v21.2.0, BlueStore.cc, prefijos de clave — PREFIX_DEFERRED = "L". ↩︎

  22. Valores por defecto de hilos en Ceph v21.2.0 — osd_op_num_shards_ssd 8; osd_op_num_threads_per_shard_ssd 2; osd_op_queue mclock_scheduler; ms_async_op_threads 3; creación de hilos en BlueStore.cc, bstore_kv_sync y bstore_kv_final. ↩︎

  23. ADR 0006, runtime de un hilo por núcleo — un hilo fijado con busy-polling por núcleo, PG propiedad de un núcleo, «idle cores burn CPU (accepted …)». ↩︎ ↩︎

  24. ADR 0008, concurrencia y durabilidad — «no locks, no atomics, no shared mutable state»; PLP obligatorio, mkfs rechaza discos sin PLP, FUA forzado para laboratorio. ↩︎ ↩︎

  25. ceph-rust docs, referencia de configuración — flashstore_cores por defecto 8, flashstore_store_shards auto, flashstore_report_objectstore. ↩︎ ↩︎ ↩︎

  26. ceph-rust docs, requisitos de hardware y del host — caché de escritura volátil FAIL, LBA de 4 KiB, sin particiones, requisitos de kernel e io_uring. ↩︎ ↩︎

  27. Ceph Umbrella, cifrado en ceph-volume — «Logical volumes can be encrypted using dmcrypt by specifying the --dmcrypt flag». ↩︎

  28. Ceph v21.2.0, ms_bind_msgr1 — por defecto true. ↩︎

  29. ADR 0168, cifrado en reposo, msgr1 y cache tiering fuera de alcance — «SPDK bypasses dm-crypt»; los clientes solo v1 «cannot reach croit-flashstore-osd». ↩︎ ↩︎ ↩︎

  30. ADR 0019, declarar osd_objectstore=bluestore — los controles de funciones del monitor, las claves flashstore_* adicionales y la opción de declararse flashstore. ↩︎ ↩︎ ↩︎ ↩︎ ↩︎

  31. ceph-rust, pgs.rs — «csum_type {v} unsupported; using fixed crc32c per 4096-byte block». ↩︎

  32. Ceph v21.2.0, bluestore_compression_mode — por defecto none. ↩︎

  33. Ceph v21.2.0, bluestore_compression_algorithm — por defecto snappy. ↩︎

  34. ADR 0059, compresión del almacén por pool — los mismos modos y opciones que BlueStore, nivel 1 de zstd fijo, espacio con 50% comprimible «identical to BlueStore». ↩︎ ↩︎

  35. Ceph v21.2.0, osd_memory_target — por defecto 4 GiB. ↩︎

  36. Ceph v21.2.0, bluestore_cache_autotune — por defecto true. ↩︎

  37. ADR 0062, objetivo de memoria y mClock — objetivo leído una vez, sin autoajuste, RSS no limitado; opciones de mClock «ACCEPTED and IGNORED». ↩︎ ↩︎ ↩︎ ↩︎

  38. ceph-rust docs, migrar desde el clásico — «There is no in-place conversion of a BlueStore device»; los dos órdenes; la línea de EC de la lista de comprobación. ↩︎ ↩︎ ↩︎

  39. ceph-rust docs, pools con erasure coding — ec_enable_pools por defecto all, ec_enable_optimized por defecto all, y cómo se ve un pool rechazado. ↩︎ ↩︎

  40. ceph-rust, valores por defecto de configuración del OSD — ("ec_enable_optimized", "all"). ↩︎