- Apple usa Cassandra y FoundationDB para iCloud y CloudKit
- Estas bases de datos almacenan miles de millones de bases de datos dentro de una arquitectura de multitenencia extrema
Lecciones reales que trascienden el tiempo
- Tanto Meta como Apple usan procesamiento asíncrono para que las funciones para usuarios operen sin fricción
- Ambas compañías usan una arquitectura stateless para resolver problemas de escalabilidad
- Aíslan lógicamente los recursos para asegurar confiabilidad y disponibilidad
- Manejan distintos requisitos de forma simple
- Construyen capas de abstracción para mejorar la experiencia de desarrolladores
- Conocen a sus usuarios y deciden cada capa, API y diseño en función de eso
Cassandra
- Cassandra es un sistema de gestión de bases de datos NoSQL de columnas anchas
- Originalmente fue desarrollado en Facebook para la función de búsqueda en la bandeja de entrada de Facebook
- Curiosamente, la propia Meta reemplazó la mayor parte de su uso de Cassandra por ZippyDB
- iCloud usa Cassandra en parte, y Apple opera uno de los despliegues de Cassandra más grandes del mundo
- Más de 300 mil instancias/nodos
- Cientos de petabytes de datos
- Más de 2 petabytes por clúster
- Millones de consultas por segundo
- Miles de aplicaciones
- Cassandra sigue mejorándose activamente dentro de Apple
- Sin embargo, CloudKit + Cassandra se topó con límites de escalabilidad y adoptó FoundationDB
FoundationDB
- Apple usa FoundationDB públicamente y la adquirió en 2015
- FoundationDB es un almacén transaccional distribuido de clave-valor, de código abierto, diseñado para manejar datos a gran escala
- Es adecuado tanto para cargas de trabajo de lectura/escritura como para cargas con muchas escrituras
- Apple usa ampliamente FoundationDB Record Layer en CloudKit
- FoundationDB Record Layer ofrece una API de Java para almacenar datos estructurados
- Record Layer soporta multitenencia extrema
Por qué usar Record Layer sobre FoundationDB
- FoundationDB se encarga del sistema distribuido y del control de concurrencia.
- Record Layer cumple el rol de una base de datos relacional que hace más fácil usar FoundationDB.
- CloudKit está en la capa más alta y ofrece funciones y APIs para desarrolladores de aplicaciones.
- A través de Record Layer, Apple soporta multitenencia a gran escala
- Se usa para multitenencia extrema, dando a cada usuario de cada aplicación su propio almacén de registros independiente
- Aloja miles de millones de bases de datos independientes que comparten miles de esquemas
Cómo CloudKit usa FoundationDB y Record Layer
- En CloudKit, las aplicaciones se representan como “contenedores lógicos” que siguen un esquema definido
- Ese esquema describe brevemente los tipos de registro, campos e índices necesarios para búsquedas y consultas eficientes
- Las aplicaciones pueden organizar datos en “zonas” dentro de CloudKit para agrupar lógicamente registros y sincronizarlos opcionalmente con dispositivos cliente
- A cada usuario se le asigna un subespacio único dentro de FoundationDB, y se crea un almacén de registros para cada aplicación con la que interactúa
- En esencia, CloudKit administra una cantidad enorme de bases de datos lógicas equivalente al número de usuarios multiplicado por el número de aplicaciones
- Cada base de datos incluye su propio conjunto de registros, índices y metadatos, llegando a miles de millones de bases de datos
- Cuando CloudKit recibe solicitudes desde dispositivos cliente, las envía mediante balanceo de carga a un proceso disponible del servicio CloudKit
- El proceso interactúa con un almacén de registros específico de Record Layer para procesar la solicitud
- CloudKit convierte el esquema definido de la aplicación en definiciones de metadatos dentro de Record Layer, que se almacenan en un almacén de metadatos separado
- Estos metadatos se complementan con campos de sistema propios de CloudKit que rastrean la creación del registro, la hora de modificación y la zona donde se almacenó el registro
- Para acceder eficientemente a los registros dentro de cada zona, la clave primaria lleva como prefijo el nombre de la zona
- Junto con los índices definidos por usuarios, CloudKit también mantiene “índices de sistema” para usos internos, como gestionar cuotas de almacenamiento, por ejemplo rastreando el tamaño por tipo de registro
Usar FoundationDB junto con Record Layer permite resolver 4 problemas clave de Apple que Cassandra por sí sola no podía solucionar
1. Resolver el problema de la búsqueda de texto completo personalizada
- FoundationDB soporta búsqueda de texto completo personalizada para que los usuarios accedan rápidamente a sus propios datos
- Aprovechando el orden de las claves de FoundationDB, puede buscar rápidamente el inicio del texto (coincidencia por prefijo), y también realizar búsquedas más complejas sin sobrecosto adicional, como búsquedas por proximidad o por frase, por ejemplo encontrar palabras cercanas entre sí o en un orden específico
- En sistemas de búsqueda tradicionales, a menudo se necesita ejecutar procesos adicionales en segundo plano para mantener actualizado el índice de búsqueda, pero el sistema de Apple procesa todo en tiempo real, por lo que el índice de búsqueda se actualiza inmediatamente en cuanto cambian los datos y no se requieren pasos extra
2. Resolver el problema de las zonas con alta concurrencia
- CloudKit usa FoundationDB para manejar sin fricciones muchas actualizaciones que ocurren al mismo tiempo
- Antes, al usar Cassandra, CloudKit dependía de un índice especial que rastreaba los cambios de cada zona para sincronizar datos entre múltiples dispositivos
- Cuando un dispositivo necesitaba actualizar datos, revisaba ese índice para ver qué había de nuevo
- Pero tenía la desventaja de que podían producirse conflictos si varias actualizaciones ocurrían simultáneamente
- Con FoundationDB, CloudKit usa un tipo especial de índice que rastrea el orden exacto de cada cambio sin generar conflictos
- Esto se logra asignando una “versión” única a cada cambio, y cuando hace falta sincronizar, CloudKit revisa esas versiones para identificar qué actualizaciones se perdió cada dispositivo
- Si CloudKit necesita mover datos entre varios clústeres de almacenamiento para distribuir mejor la carga, la situación se complica porque los números de versión de cada clúster no coinciden
- Para resolver esto, CloudKit asigna a los datos de cada usuario un “conteo de migraciones” (llamado “incarnation”) que aumenta cada vez que los datos se transfieren a un nuevo clúster
- Cada actualización de registro incluye el número actual de “incarnation” del usuario, así que incluso después de mover los datos, CloudKit puede determinar el orden correcto de las actualizaciones revisando tanto la incarnation como el número de versión
- Al migrar al nuevo sistema, CloudKit enfrentó el problema de tener que manejar datos antiguos que no tenían esos números de versión
- Pero resolvió esto de forma inteligente usando una función especial que ordena primero las actualizaciones antiguas del sistema previo frente a las del sistema nuevo
- Gracias a eso, no fue necesario hacer cambios complejos en las aplicaciones ni mantener código viejo
- Para mantener el orden correcto del historial, toma en cuenta la incarnation, la versión y el valor previo del contador de actualizaciones
3. Resolver el problema de las consultas de alta latencia
- FoundationDB está diseñado para alta concurrencia, no para baja latencia. Es decir, en vez de enfocarse en la velocidad de una operación individual, puede manejar muchas operaciones al mismo tiempo
- Para aprovechar al máximo este diseño, Record Layer realiza muchas operaciones de forma “asíncrona”
- Pone en cola tareas que se completarán después y mientras tanto permite ejecutar otras
- Este enfoque ayuda a cubrir la latencia que puede surgir durante esas operaciones
- Sin embargo, la herramienta que usa FoundationDB para comunicarse con la base de datos fue diseñada para usar un solo hilo para la red, ejecutando una sola tarea a la vez
- En versiones anteriores, esta configuración causaba congestión porque todas las tareas tenían que esperar su turno en ese hilo de red
- Como Record Layer usaba este enfoque de un solo hilo, se generaba un cuello de botella
- Para mejorarlo, Apple redujo la carga de trabajo de ese hilo de red
- Ahora el sistema trabaja con la base de datos en varios frentes al mismo tiempo, sin formar una cola, así que las operaciones complejas son más rápidas
- Así se oculta la latencia, o la lentitud percibida, porque el sistema no espera a que una tarea termine antes de iniciar otra
4. Resolver el problema de las transacciones en conflicto
- En FoundationDB, ocurre un “conflicto de transacción” cuando una transacción lee una clave específica mientras otra modifica esa misma clave
- FoundationDB ofrece mecanismos de control sobre el conjunto de claves que pueden provocar esos conflictos al leer o escribir, permitiendo gestionarlos con precisión
- Una forma común de evitar conflictos innecesarios es hacer un tipo especial de lectura que no genera conflictos, llamada lectura “snapshot”, sobre varias claves
- Si en esa lectura se encuentra una clave importante, la transacción marca solo la clave específica con posible conflicto, no todo el rango
- Esto permite que la transacción solo se vea afectada por cambios que realmente importan para el resultado
- Record Layer usa esta estrategia para gestionar eficientemente una estructura llamada skip list, que forma parte de su sistema de índices de ranking
- Sin embargo, configurar manualmente estos rangos de conflicto puede ser complicado y, sobre todo cuando se mezclan con la lógica principal de la aplicación, puede derivar en errores difíciles de identificar
- Por eso, en sistemas construidos sobre FoundationDB se recomienda crear herramientas de más alto nivel, como índices personalizados, para manejar estos patrones
- Este enfoque ayuda a evitar situaciones en las que la responsabilidad de relajar las reglas de conflicto recaiga en cada aplicación cliente, lo que podría causar errores e inconsistencias
1 comentarios
Opiniones de Hacker News
Un usuario de Hacker News compartió una observación de cuando trabajó en Apple sobre la diferencia entre bases de datos y sistemas de archivos. Mencionó que, en esencia, las bases de datos y los sistemas de archivos cumplen la misma función y que son optimizaciones para resolver problemas específicos. Como ejemplo, iCloud muestra una forma de definir un sistema de archivos sobre una base de datos. Este usuario también compartió su experiencia usando Cassandra para almacenar video.
Otro usuario mencionó su experiencia en una empresa anterior construyendo un sistema de catálogo transaccional con FoundationDB y RecordLayer. Dijo que el sistema fue muy efectivo y que usar gRPC y Protobuf se sintió natural. Sin embargo, señaló como desventaja que la barrera de entrada para operar FoundationDB a gran escala es alta.
Un usuario evaluó que la sincronización de Apple Notes maneja mejor los conflictos que las aplicaciones de notas basadas en Markdown. Comentó que por eso terminó cambiándose a Apple Notes.
Se mencionan publicaciones anteriores sobre FoundationDB. Incluyen enlaces sobre el almacén distribuido de clave-valor de FoundationDB, la Record Layer, la adquisición por parte de Apple y el funcionamiento y las características de FoundationDB.
Se menciona como algo interesante la arquitectura de software nativo de escritorio que se mueve gradualmente hacia almacenamiento y colaboración basados en la nube. Es importante manejar bien los cambios de esquema y las migraciones de versión, ya que esto ocurre a gran escala sin intervención de administradores.
Un usuario expresó que le gustaría que iCloud pudiera almacenar respaldos de Time Machine.
Dado que FoundationDB se basa en SQLite, surgió la curiosidad sobre si el motor HCTree podría aplicarse a FoundationDB. HCTree tiene el potencial de mejorar 10 veces el rendimiento de lectura/escritura de SQLite.
Hay quejas sobre cómo iCloud administra los archivos del usuario. A veces resulta problemático que iCloud mueva automáticamente a la nube archivos, apps y fotos usados recientemente para liberar espacio.
Un usuario recordó un sistema de reportes llamado Hyperion que usó cuando trabajaba en un banco. Ese sistema creaba una base de datos nueva para cada reporte; en ese momento le parecía extraño, pero ahora comenta que, visto en retrospectiva, era una forma adelantada a su tiempo.