Tema representativo de entrevista

Entrevista de diseño de sistemas: ¿Cómo sincronizarías de forma confiable un índice de búsqueda con CDC?

Diseño de sistemasDifícil
Equipo editorial de Offer.ccPublicado Actualizado

Pregunta

Una base de datos de productos es la fuente de la verdad para las búsquedas. Diseña una canalización de CDC en tiempo casi real que sincronice de forma confiable inserciones, actualizaciones y eliminaciones en un índice de búsqueda. Debe admitir una instantánea inicial, puesta al día, entrega de duplicados, interrupción de consumidores, evolución de esquemas y reconstrucciones de índices. Explica cómo demostrarías que no se pierden eventos y que un evento más antiguo no puede sobrescribir a uno más nuevo.

Consigna y roles aplicables

Una base de datos de productos es la fuente de la verdad para las búsquedas. Diseña una canalización de CDC en tiempo casi real que sincronice de forma confiable inserciones, actualizaciones y eliminaciones en un índice de búsqueda. Debe admitir una instantánea inicial, puesta al día, entrega de duplicados, interrupción de consumidores, evolución de esquemas y reconstrucciones de índices. Explica cómo demostrarías que no se pierden eventos y que un evento más antiguo no puede sobrescribir a uno más nuevo.

Esto encaja en entrevistas de backend, infraestructura de datos, plataformas de búsqueda y diseño de sistemas. Asume que la fuente puede exponer el orden de confirmación (commit) o una posición equivalente en el registro (log), y que el índice de búsqueda es un sistema derivado reconstruible. Kafka, Debezium y Elasticsearch son opciones, no requisitos; define primero la semántica y los límites de falla.

Qué está evaluando el entrevistador

El entrevistador quiere ver si separas "la escritura en la base de datos se confirmó" de "el índice es finalmente visible", con un contrato observable para cada etapa. Una respuesta sólida define la clave del evento, la operación, la transacción o posición de registro y la versión del esquema; elige la captura basada en registros en lugar de un sondeo de marcas de tiempo inseguro; y maneja la superposición de instantáneas, la reproducción at-least-once, el ordenamiento por clave, las lápidas (tombstones) de eliminación y el cambio atómico de alias. Un diagrama que solo contenga una base de datos, una cola y un cuadro de búsqueda no puede demostrar confiabilidad sin puntos de control (checkpoints), reproducción y reglas de reconciliación.

Preguntas para aclarar antes de responder

  • ¿Cuál es el objetivo de frescura? ¿Cinco segundos después del commit o minutos? Esto define el almacenamiento en búfer, las alertas y los presupuestos de respaldo.
  • ¿Qué tipo de ordenamiento se requiere? Por lo general, un producto individual debe seguir el orden de confirmación de la fuente; un orden global entre todos los productos es innecesario. Los invariantes entre tablas pueden requerir un evento agregado.
  • ¿Las eliminaciones son físicas (hard) o lógicas (soft)? Las eliminaciones físicas necesitan lápidas duraderas o eventos de eliminación; las eliminaciones lógicas necesitan reglas de visibilidad en el documento indexado.
  • ¿Pueden continuar las escrituras durante la instantánea? Si es así, define una posición de instantánea y retén los cambios después de esa posición para cubrir la ventana de la instantánea.
  • ¿Cómo evoluciona el esquema? ¿Pueden los consumidores antiguos ignorar los campos agregados? ¿Los campos eliminados o con tipos modificados requieren lectura/escritura dual, una nueva versión de evento o una reconstrucción?
  • ¿Las reconstrucciones deben ser sin tiempo de inactividad (zero-downtime)? Si es así, escribe en un nuevo índice, cambia un alias de forma atómica y retén un punto de reproducción para los consumidores antiguos.

Estructura de respuesta de 30 segundos

"Trataría la base de datos principal como la fuente de la verdad y capturaría las inserciones, actualizaciones y eliminaciones confirmadas desde su registro. Cada evento lleva una clave, operación, LSN de origen, ID de transacción, versión de esquema y valores anteriores/posteriores. La carga inicial comienza a partir de una instantánea consistente mientras registra su posición en el registro; los eventos posteriores a esa posición continúan a través del mismo flujo reproducible, y los consumidores escriben de manera idempotente por clave de producto.

La entrega es at-least-once. El punto de control solo avanza después de que el efecto secundario en el índice tiene éxito, y los eventos duplicados se rechazan mediante una condición de clave más versión o LSN. Un consumidor detenido se reanuda desde su punto de control. Expondría métricas de desfase (lag), antigüedad del evento más viejo, retención de ranuras (slots) y muestras de versiones entre la base de datos y el índice. Una reconstrucción escribe el mismo flujo en un nuevo índice, espera a que se ponga al día y luego cambia atómicamente el alias."

Análisis detallado paso a paso

Paso 1: Definir el contrato del evento y el límite de captura

El CDC basado en registros lee los cambios confirmados de la base de datos y preserva el orden de origen o una posición de registro. En PostgreSQL, la decodificación lógica extrae cambios de la WAL, y una ranura de replicación representa un flujo que se puede reproducir en el orden de origen. Una ranura retiene la WAL requerida, por lo que su retención debe ser monitoreada; un conector estancado puede agotar el disco de la base principal.

Cada evento debe incluir entity_id, operation, source_position, transaction_id, schema_version, before y after. Usa source_position para auditoría y deduplicación, no la hora de llegada del mensaje como orden de negocio. Si una transacción modifica varios productos, decide si el índice puede exponerlos uno por uno o si el flujo debe agregarse en el límite de la transacción.

Paso 2: Conectar la instantánea y el flujo con una sola posición

La superposición peligrosa ocurre cuando una instantánea lee una fila antigua mientras el flujo entrega un evento más nuevo para la misma clave. Registra una posición de registro P0 cuando comience la instantánea. Los documentos de la instantánea representan el estado en el inicio; los eventos posteriores a P0 permanecen disponibles y se aplican después del resultado de la instantánea.

text
P0 = captureSourcePosition()
startStreaming(after=P0)
for row in consistentSnapshot():
  indexUpsert(row, version=P0)

for event in stream:
  if event.position > indexedVersion[event.key]:
    applyIdempotently(event)
  checkpoint(event.position)  # only after index write succeeds

Los conectores reales pueden usar ventanas de instantáneas, fragmentos de clave primaria y búferes para resolver colisiones entre eventos READ y UPDATE. En una entrevista, explica que esto evita que una fila de instantánea antigua sobrescriba una actualización confirmada; "iniciar el flujo después de que termine la instantánea" no es suficiente.

Paso 3: Hacer explícitos el ordenamiento, la idempotencia y la recuperación

Particiona por entity_id para que los eventos de un producto retengan el orden de origen mientras diferentes productos se procesan en paralelo. Utiliza una versión externa, escritura condicional o documento versionado para que un evento solo pueda sobrescribir una posición anterior. Un DELETE escribe una lápida o una eliminación versionada y retiene suficientes metadatos para evitar que un UPDATE tardío resucite el documento.

El punto de control significa "el efecto secundario para este evento se ha completado de manera duradera". No lo confirmes después de extraer un mensaje o enviar una solicitud HTTP. Una caída después de escribir en el índice pero antes del punto de control causa un duplicado, por lo que la escritura en el destino debe ser idempotente. Si el punto de control avanza antes de escribir en el índice, se pierden datos; define un límite de confirmación verificable o utiliza un trabajo de indexación reproducible junto con reconciliación.

Paso 4: Manejar la reproducción, la evolución de esquemas y las reconstrucciones

Asigna a cada consumidor una ranura independiente o un progreso equivalente, en lugar de hacer que los consumidores compitan por un único cursor de consumidor individual. Antes de la reproducción, congela o etiqueta la política de versiones del índice de destino, delimita el rango de reproducción y asegúrate de que los eventos antiguos solo puedan escribir versiones más antiguas. Define reglas de compatibilidad: los consumidores antiguos a menudo pueden ignorar un campo opcional agregado, mientras que un campo eliminado o con tipo cambiado puede requerir una nueva versión de evento, lectura/escritura dual o reindexación.

No vacíes el índice activo para una reconstrucción. Crea un nuevo índice y reproduce desde la misma posición de instantánea hasta que su posición aplicada alcance una compuerta de cambio (cutover). Cambia el alias de forma atómica y continúa consumiendo el mismo flujo. Si el cambio falla, mantén el alias antiguo y el progreso del nuevo índice, repara y vuelve a ponerte al día; no adivines un nuevo punto de inicio.

Paso 5: Demostrar confiabilidad con métricas y reconciliación

Rastrea la latencia de lectura de CDC, el trabajo acumulado (backlog) por partición, la antigüedad del evento más viejo, la retención de WAL en la ranura, el punto de control de cada consumidor, las fallas de escritura en el índice, los reintentos, las cartas muertas (dead letters) y la diferencia de versión de muestras de claves primarias en la base de datos y el índice. Las eliminaciones merecen sus propios contadores de lápidas y documentos residuales.

Prueba deteniendo el consumidor, duplicando entregas, reordenando mensajes entre particiones, actualizando una clave durante una instantánea, entregando una eliminación tardía, cambiando el esquema y realizando una conmutación por error (failover) de la base principal. Una herramienta de reconciliación debe volver a leer la versión actual de la base de datos, reproducir el flujo hasta una posición elegida y emitir la muestra inconsistente más pequeña. Una cola vacía por sí sola no demuestra que no se omitieron eventos o que las escrituras en el índice no fallaron.

Ejemplo de respuesta de alta calidad

"Primero definiría el contrato: la base de datos es la fuente de autoridad y el índice es reconstruible; el objetivo es que sea buscable dentro de los cinco segundos posteriores a la confirmación, con el orden de origen preservado por producto y sin orden global entre productos. Los eventos llevan la clave, inserción/actualización/eliminación, LSN, ID de transacción, versión de esquema y valores anteriores/posteriores.

Utilizaría CDC basado en registros. Al inicio de la instantánea registro P0 y continúo consumiendo después de P0. Las lecturas (READ) de la instantánea pueden colisionar con las actualizaciones (UPDATE) del flujo, por lo que una ventana de instantánea o una regla equivalente de clave-versión debe descartar la lectura obsoleta; simplemente iniciar el flujo después de la instantánea deja una brecha. Los consumidores particionan por clave y usan una versión externa o escritura condicional. Las eliminaciones retienen una versión de lápida para que una actualización tardía no pueda resucitar un documento.

El punto de control solo avanza después de que el efecto secundario en el índice tiene éxito. Por lo tanto, las caídas crean una reproducción at-least-once, que el destino debe tolerar de forma idempotente. Mientras un conector está inactivo, monitoreo la WAL retenida por la ranura; tras la recuperación, se reanuda desde la última posición segura. Para una reconstrucción, escribo el mismo flujo en un nuevo índice, espero la puesta al día y cambio el alias de forma atómica.

La aceptación va más allá de tener una cola vacía. Inyecto actualizaciones durante la instantánea, duplicados y desorden, una eliminación tardía, caídas de consumidores, cambios de esquema y conmutación por error de la base principal. Luego comparo la versión de la base de datos, la versión del índice y el punto de control por clave. Las señales clave son la antigüedad del evento más viejo, la retención de WAL, el desfase del índice, las cartas muertas y las muestras inconsistentes; cualquier brecha debe ser reproducible a partir de una posición de registro guardada."

Errores comunes

  • Sondear una marca de tiempo de actualización como CDC → la precisión del reloj, el desplazamiento horario y las transacciones largas pueden ocultar cambios → lee el registro de confirmaciones o usa un cursor comprobable.
  • Iniciar la instantánea y el flujo de forma independiente → una fila de instantánea antigua puede sobrescribir un evento nuevo → registra P0 y resuelve colisiones entre READ/UPDATE.
  • Avanzar el punto de control inmediatamente después de enviar una solicitud al índice → una ventana de caída causa pérdida de datos → avanza solo después de un efecto secundario verificable.
  • Tratar at-least-once como exactly-once → los duplicados siguen ocurriendo → usa escrituras condicionales por versión y eliminaciones idempotentes.
  • Ordenar por llegada de mensajes → los reintentos desordenan la red → particiona por clave y usa el LSN o versión de origen.
  • Eliminar del índice sin una lápida versionada → una actualización tardía resucita el documento → retén los metadatos de versión de la eliminación.
  • Compartir una única ranura de replicación entre consumidores independientes → un consumidor puede consumir cambios que otros nunca recibirán → usa una ranura por consumidor o una capa de difusión explícita.
  • Limpiar el índice activo para una reconstrucción → una reproducción fallida crea una interrupción masiva de búsqueda → pon al día un nuevo índice y cambia el alias de forma atómica.
  • Usar una cola vacía como prueba → eventos omitidos o escrituras fallidas pueden dejarla vacía → reconcilia posiciones, versiones y muestras principales.

Preguntas de seguimiento y respuestas

Pregunta de seguimiento 1: ¿Puede un índice exponer una transacción que actualiza tanto un producto como su inventario fila por fila?

Sí, si el negocio acepta un estado de búsqueda intermedio. Si los resultados deben reflejar ambos cambios juntos, transporta el límite de la transacción y agrega los datos antes de actualizar el documento, o construye una única proyección buscable y confirmada. Las escrituras "casi simultáneas" entre índices no son atómicas.

Pregunta de seguimiento 2: Una interrupción prolongada hace que la ranura de replicación retenga demasiada WAL. ¿Cómo se contiene esto?

Protege la base principal primero: genera alertas, limita escrituras adicionales o degrada al consumidor, y verifica que la ranura aún tenga una posición de inicio utilizable. Si la ranura queda invalidada, no crees una nueva ranura asumiendo continuidad; los LSN faltantes pueden haberse perdido. Reconstruye desde una copia de seguridad o instantánea completa y reconcilia la brecha.

Pregunta de seguimiento 3: El flujo solo está ordenado dentro de las particiones. ¿Cómo se clasifican los resultados de búsqueda entre productos?

La búsqueda lee las versiones actuales del índice y no debe asumir un orden de eventos global. Si un campo de clasificación necesita un tiempo consistente, usa la hora de confirmación de origen más una regla de versión, o haz que un agregador produzca una clave de rango estable con un presupuesto de desviación temporal explícito. Un orden global entre particiones penaliza el rendimiento y debe justificarse por una regla invariable del producto.

Pregunta de seguimiento 4: ¿Qué le sucede al índice antiguo cuando se elimina un campo del esquema?

Primero despliega consumidores que puedan leer ambas versiones de eventos, luego deja de producir el campo antiguo, confirma que el trabajo acumulado y las ventanas de reproducción estén limpios y, finalmente, migra el mapeo o reconstruye. Si el campo cambia la autorización o el significado analítico, eliminar una propiedad JSON es insuficiente; retén eventos versionados y una ruta de reversión.

Fuentes públicas

Preguntas relacionadas

Herramienta de entrevista relacionada

Usa Resolver para una respuesta de diseño de sistemas

Aclara primero los requisitos y luego avanza a través de la escala, la arquitectura, la elección de componentes y las compensaciones (trade-offs).

Ver la herramienta