Planteamiento y contexto
Diseñe un registro de eventos para un servicio de cuentas multiinquilino. Cada cambio de saldo, permiso o configuración emite un evento, y los consumidores pueden reproducir el historial para reconstruir el estado. Una falla de instancia no puede escanear un historial ilimitado desde el principio. El sistema necesita adiciones de alto rendimiento, ordenamiento por inquilino, progreso independiente de los consumidores, recuperación basada en instantáneas y un costo de almacenamiento limitado. Explique las eliminaciones, los eventos fuera de orden y la evolución del esquema.
Esto se adapta a entrevistas de diseño de sistemas de nivel senior, plataforma e infraestructura de datos. El artículo sobre Event Sourcing de Martin Fowler define la idea central de mantener los cambios de estado como una secuencia de eventos. La documentación de diseño de Apache Kafka explica los registros particionados, las posiciones de los consumidores y la compactación de registros que retiene el valor más reciente para cada clave. Las preguntas públicas de diseño de sistemas también enumeran registros particionados de solo adición, replicación, retención, compactación y recuperación como áreas de evaluación. Estas fuentes respaldan la representatividad del tema, pero no establecen una pregunta fija de una empresa ni una frecuencia de entrevista determinada. La categoría es system-design porque las habilidades principales son la consistencia de extremo a extremo, los límites de recuperación y los compromisos de capacidad.
Qué evalúan los entrevistadores
Primero, ¿puede el candidato separar el historial de eventos del estado actual materializado? Una instantánea es una capa de aceleración derivada; no puede reemplazar eventos inmutables ni cruzar un límite de eventos inconsistente.
Segundo, ¿la clave de partición satisface tanto el orden como la escala? Particionar por inquilino o por ID de agregado preserva el orden de un agregado, pero los inquilinos con alta carga (hot tenants), las transacciones entre agregados y el orden global requieren límites explícitos.
Tercero, ¿entienden la semántica de la compactación? La compactación por clave retiene el registro más reciente por clave y se adapta a un flujo de cambios de estado; no es un historial de eventos. Las eliminaciones necesitan lápidas (tombstones) y una ventana de retención, y los consumidores no pueden asumir que los offsets arbitrarios signifiquen lo mismo antes y después de la compactación.
Por último, ¿cubren las operaciones? La validación de instantáneas, las versiones de eventos, los puntos de control atómicos, las confirmaciones de replicación, el costo de retención, el retraso del consumidor (consumer lag) y la compatibilidad de esquemas deben ser observables.
Preguntas aclaratorias para hacer primero
- ¿Son los eventos hechos de auditoría inmutables o cambios reconstruibles del estado actual? La auditoría necesita un historial completo; los flujos de estado pueden usar compactación por clave.
- ¿Cuál es el alcance del ordenamiento? ¿Solo dentro del mismo agregado, por inquilino o global? Cada garantía cambia el particionamiento y el rendimiento.
- ¿Deben los consumidores reproducir desde cualquier momento? De ser así, la compactación por clave más reciente por sí sola no es suficiente; mantenga un archivo o un registro de auditoría separado.
- ¿Quién crea y valida las instantáneas? Los productores, los consumidores y los trabajadores de instantáneas tienen responsabilidades diferentes; vincule las instantáneas a una posición del registro y a una versión del esquema.
- ¿Cómo se representan las eliminaciones? Un tombstone versionado o un evento de eliminación de negocio tienen diferentes implicaciones de compactación y cumplimiento.
Estructura de respuesta en 30 segundos
“Particiono los eventos de cada agregado por su ID de agregado, agrego únicamente al final (append-only) y confirmo a los productores en un offset replicado y confirmado. Los eventos incluyen un ID de evento, versión del agregado, versión del esquema y marca de tiempo. Los consumidores registran puntos de control (checkpoints) de los offsets después de actualizar su vista materializada, usan IDs de eventos para la desduplicación al menos una vez (at-least-once) y reconstruyen a partir de una instantánea más los eventos posteriores. Una instantánea almacena el estado, su último offset de evento y la versión del esquema; la recuperación la valida antes de reproducir el siguiente offset. La compactación por clave se aplica solo a los flujos de estado actual reconstruibles y retiene tombstones durante una ventana definida; los eventos de auditoría van a un archivo no compactado. Monitoreo el retraso de replicación, el retraso del consumidor, la antigüedad de las instantáneas, la acumulación de compactación y las diferencias de verificación de reproducción”.
Respuesta detallada
1. Definir los campos de eventos y los límites de ordenamiento
Un evento incluye eventId, aggregateId, aggregateVersion, schemaVersion, carga útil (payload), hora de creación y origen. aggregateVersion aumenta monótonamente para un agregado. La adición condicional rechaza una versión antigua, evitando que dos escritores concurrentes se sobrescriban silenciosamente entre sí.
Use el ID de agregado como clave de partición para que un agregado entre en un único registro ordenado. No prometa un orden global entre particiones. Si el producto necesita un hecho atómico entre agregados, codifique el resultado de una transacción como un único evento de agregado o use una bandeja de salida transaccional (transactional outbox) en lugar de ordenar por marcas de tiempo.
2. Adición, replicación y confirmación
Un líder agrega localmente y replica a suficientes seguidores; solo después de cumplir la condición de confirmación configurada confirma la aceptación. Los IDs de eventos y los números de secuencia del productor permiten la desduplicación en reintentos. Los segmentos de disco rotan por tamaño o tiempo, y los índices ayudan a los consumidores a localizar los offsets.
Haga explícita la confirmación: significa que el evento está en un registro confirmado y recuperable, no que todos los consumidores lo procesaron o que un modelo de lectura materializado está actualizado. Una falla del consumidor no revierte un evento ya agregado.
3. Puntos de control del consumidor e idempotencia
Cada grupo de consumidores almacena sus propios offsets de partición. Procese el evento y actualice el modelo de lectura antes de confirmar el punto de control; una caída puede repetir el procesamiento, por lo que los manejadores deben ser idempotentes por ID de evento o versión del agregado. Si el modelo y el punto de control necesitan un acoplamiento atómico, escriba ambos en un almacén transaccional o registre el resultado en un outbox.
Conserve el registro cuando un consumidor se retrase; no omita eventos no procesados solo para reducir el lag. Una vista reconstruible puede comenzar desde el offset de una instantánea. Un efecto secundario externo que no se puede reconstruir necesita compensación o revisión en lugar de una reproducción a ciegas.
4. Protocolo de instantáneas
Una instantánea almacena el ID del agregado, el estado serializado, el último offset aplicado, las versiones del agregado y del esquema, la suma de verificación (checksum) y la hora de creación. Genérela en un límite estable: registre el offset objetivo, aplique los eventos hasta ese offset y escriba la instantánea con el mismo límite. La recuperación solo acepta una instantánea validada cuyo offset pertenezca a la partición del agregado.
La recuperación carga la instantánea y reproduce desde snapshotOffset + 1. Si el esquema es antiguo, ejecute una migración versionada antes de publicar el estado; una falla de migración debe bloquear un estado inválido. Una instantánea es una caché: eliminarla no daña ningún registro y solo aumenta el tiempo de recuperación.
5. Separar retención, compactación y archivo
La retención por tiempo elimina datos antiguos del registro y se adapta a eventos con una ventana de reproducción definida. La compactación por clave conserva el valor más reciente de cada clave en una partición y permite a un consumidor de estado reconstruir el estado actual a partir de un registro más corto. No puede admitir auditorías ni reproducción en un punto arbitrario en el tiempo porque los eventos intermedios pueden haber desaparecido.
Un tombstone representa una clave eliminada. Consérvelo hasta que todos los consumidores cubiertos por el contrato puedan observarlo, y luego permita que la compactación lo elimine. Almacene los eventos de auditoría en un archivo inmutable con control de acceso, cifrado y reglas de retención; un registro de estado compactado no es una evidencia de auditoría completa.
6. Esquema, desorden y eventos venenosos (poison events)
Utilice reglas de esquema compatibles hacia atrás: agregue campos opcionales, preserve los significados antiguos y permita que los consumidores ignoren los campos desconocidos. Los cambios incompatibles necesitan una nueva versión de esquema, un período de lectura dual o una ventana de migración. Registre las versiones de esquema y los fallos de procesamiento; nunca confirme silenciosamente el offset de un evento que no se pueda analizar.
Los eventos fuera de orden para un agregado generalmente indican una infracción del productor o de la replicación. Rechace una versión de agregado antigua y mantenga un espacio libre para su reparación. Los eventos entre agregados necesitan una regla de compensación por tiempo de negocio o ID causal; la hora de llegada al servidor no es un sustituto de la causalidad.
7. Capacidad, recuperación y observabilidad
Modele eventos por segundo, tamaño promedio y percentil de cola del payload, factor de replicación, ventana de retención, tamaño de instantánea, ahorro por compactación y velocidad de reproducción. Intervalos de instantáneas más cortos mejoran la recuperación pero agregan amplificación de escritura y almacenamiento. Una compactación más agresiva reduce el costo de reconstrucción del estado pero debilita la auditoría y las consultas históricas.
Monitoree la latencia de confirmación del productor, el retraso de replicación, los segmentos de disco, la acumulación de compactación, el retraso del consumidor, la antigüedad de las instantáneas, la tasa de reproducción, los errores de esquema, la tasa de duplicados y las diferencias de checksum en instantáneas. Reconstruya periódicamente el estado de muestra a partir de instantáneas y eventos y compárelo con la vista materializada; conserve los offsets y las muestras de eventos cuando aparezca una discrepancia.
Respuesta de muestra de alta calidad
“Uso el ID de agregado como clave de partición, agrego eventos inmutables y rechazo escrituras obsoletas concurrentes mediante una versión de agregado. Una confirmación al productor significa que el evento está en un registro replicado y confirmado; cada consumidor confirma su propio offset de partición tras aplicar el evento, por lo que una caída solo provoca repeticiones desduplicables.
Las instantáneas contienen el estado del agregado, el último offset de evento, versiones de agregado y de esquema, y un checksum. La recuperación valida la instantánea y reproduce desde el siguiente offset; eliminar una instantánea solo ralentiza la recuperación. La compactación por clave se limita a los flujos de estado actual reconstruibles, con tombstones retenidos según el contrato del consumidor. El flujo de auditoría permanece como un archivo inmutable.
Prometo orden únicamente dentro de un mismo agregado, no entre particiones. El modelo de capacidad incluye replicación, retención, compactación y amplificación de escritura de instantáneas. Las métricas cubren lag, antigüedad de instantáneas, acumulación de compactación, errores de esquema y diferencias de verificación de reproducción, con comprobaciones periódicas de reconstrucción a partir de instantáneas más eventos”.
Errores comunes
- Tratar las instantáneas como la fuente de eventos → eliminar una elimina la evidencia de recuperación y auditoría → las instantáneas son aceleradores derivados vinculados a un offset.
- Tratar un registro compactado como el historial completo → los eventos intermedios y el estado en un punto en el tiempo se pierden → archive los eventos de auditoría y compacte solo los flujos de estado.
- Ordenar todos los eventos por marca de tiempo → el desfase de reloj (clock skew) crea un orden falso → defina el orden mediante la versión del agregado y el contrato de partición.
- Confirmar offsets tras efectos secundarios arbitrarios → los duplicados son inevitables pero no especificados → haga que los manejadores sean idempotentes y defina el acoplamiento entre modelo y punto de control.
- Eliminar tombstones de inmediato → un consumidor retrasado puede resucitar una clave → reténgalos durante la ventana del contrato del consumidor.
- Omitir un esquema desconocido y confirmar → el registro y la vista divergen silenciosamente → haga una pausa, aísle el evento venenoso y conserve la evidencia.
- Omitir el modelado de capacidad → las instantáneas, la compactación o la reproducción se convierten en el cuello de botella de la interrupción → calcule el lag, el tiempo de recuperación y la amplificación de escritura.
- Afirmar que 'exactamente una vez' (exactly-once) resuelve todos los duplicados → los efectos secundarios externos aún pueden repetirse → utilice claves de idempotencia, transacciones o compensación.
Preguntas de seguimiento y respuestas
¿Por qué no almacenar solo el estado actual en una base de datos?
Si solo importan las lecturas actuales, una base de datos es más simple. Un registro de eventos proporciona reproducción, auditoría, múltiples consumidores y reconstrucción de diferentes vistas, a costa del esquema, la reproducción, la capacidad y la idempotencia de efectos secundarios. Elíjalo para esos requisitos en lugar de asumir que el event sourcing es universalmente superior.
¿Cómo puede un nuevo consumidor reconstruir desde el principio después de la compactación?
Solo puede reconstruir el estado actual que aún está representado después de la compactación; el historial intermedio eliminado no se puede reconstruir. Si el historial importa, mantenga un archivo no compactado o un registro de cambios separado y documente la capacidad de reproducción de cada tema.
¿Qué sucede si llegan nuevos eventos mientras se construye una instantánea?
Elija un offset objetivo estable. Los eventos posteriores a ese offset continúan agregándose pero no forman parte de la instantánea; la recuperación carga la instantánea y reproduce desde el siguiente offset. Si falla la escritura de la instantánea, conserve la instantánea antigua y el registro en lugar de publicar un archivo parcial.
¿Cómo se migran los esquemas de eventos?
Defina primero la compatibilidad y los campos de versión, luego permita que los consumidores lean las versiones antiguas y nuevas durante una ventana de migración. Los escritores agregan nuevos campos y dejan de usar los campos antiguos solo después de que todos los consumidores se hayan actualizado. Los cambios disruptivos utilizan un nuevo tipo de evento o una migración fuera de línea, validada mediante pruebas de reproducción sobre eventos históricos.