Planteamiento y contexto
Varios workers sin estado deben tener exactamente una instancia ejecutando un trabajo de liquidación programado a la vez. Los workers pueden fallar, reiniciarse, pausarse o quedar particionados, y el sistema no debe permitir que dos workers escriban durante un período prolongado. Diseña una elección de líder con un coordinador, lease, renovación, fencing, failover y límites operativos. El núcleo es la coordinación segura de un único escritor, no simplemente un mutex.
Qué está evaluando el entrevistador
Definir seguridad (safety) y vivacidad (liveness)
Safety significa que el recurso acepta como máximo a un líder por término (term); liveness significa que un candidato en buen estado finalmente toma el control después de confirmarse que el lease anterior ha expirado. Una partición minoritaria no puede anunciar un nuevo líder solo para preservar la disponibilidad.
Elegir un coordinador con semántica de consenso
El registro de líder necesita compare-and-set linealizable, leases y semántica de watch, como etcd. Un TTL de Redis o un reloj local por sí solos no pueden demostrar que un líder obsoleto es incapaz de escribir.
Evitar escrituras obsoletas (stale writes)
Después de que expira un lease, el proceso antiguo puede recuperarse y continuar. Cada escritura posterior (downstream) debe llevar un fencing token monótonamente creciente, y el recurso debe rechazar tokens más antiguos para evitar daños por split-brain.
Preguntas para aclarar primero
- ¿Puede el trabajo ejecutarse dos veces, o el downstream debe aceptar estrictamente una única ejecución serializada?
- ¿La elección es global, por tenant, por shard o por trabajo?
- ¿Qué tiempo de failover y qué ventana de pausa son aceptables?
- ¿Cuántos dominios de falla del coordinador y qué quorum/copias de seguridad están disponibles?
- ¿Pueden los recursos downstream validar un fencing token y recuperarse de forma idempotente?
- ¿Se requieren eventos de watch, historial de auditoría, alertas y transferencia manual?
Una respuesta de 30 segundos
“Primero definiría el alcance de la elección y el presupuesto de fallas, luego almacenaría un registro de líder con lease en un coordinador linealizable respaldado por consenso. Los candidatos compiten con una actualización transaccional create-or-compare; el ganador obtiene un término de fencing más alto y renueva dentro del TTL. El fallo de renovación detiene el nuevo trabajo y las escrituras. Cada escritura downstream valida el término, por lo que un líder obsoleto recuperado no puede escribir. Los watches solo aceleran un nuevo intento de CAS. Monitorearía el término, la latencia de renovación, el tiempo de failover, los rechazos de fencing, los duplicados y la salud del quorum”.
Respuesta detallada paso a paso
Definir el registro de término
Almacena election_name, leader_id, lease_id, term, metadatos del candidato y marcas de tiempo. El término o fencing token debe incrementarse monótonamente y ser asignado atómicamente por el coordinador, no generado a partir del reloj de un cliente.
Elegir el coordinador y la condición de escritura
Un candidato crea un registro efímero respaldado por un lease; una transacción linealizable puede escribir solo cuando la clave está ausente. Si existe un líder, los candidatos observan (watch) la clave y reintentan. La API de elección de etcd permite a los participantes competir en un único nombre de elección con un solo líder exitoso a la vez.
Renovar y fallar de forma segura (fail closed)
El líder renueva mediante un bucle de keepalive independiente. El tiempo de espera de renovación agotado, la pérdida de conexión, la pausa del proceso o una anomalía en el reloj local lo mueven a suspect, donde deja de aceptar nuevo trabajo y escrituras downstream. No debe seguir trabajando confiadamente mientras esté desconectado del coordinador.
Gestionar la conmutación por error (failover)
Un candidato no puede decidir que el líder anterior está muerto a partir de su TTL local. Debe observar la expiración o eliminación del lease confirmada por el coordinador y luego competir con un CAS. El tiempo de failover es la suma del TTL, la detección y los retrasos de programación, por lo que se debe establecer un límite con margen para el jitter.
Agregar un fencing token
El nuevo líder obtiene un término más alto y lo adjunta a actualizaciones de bases de datos, mensajes o solicitudes a APIs externas. Un recurso almacena el token aceptado más alto y rechaza uno inferior. Esto bloquea escrituras peligrosas incluso cuando un proceso antiguo no se ha detenido.
Gestionar particiones y split brain
Una partición minoritaria no puede emitir nuevos términos. Un candidato que no pueda alcanzar el quorum del coordinador debe detenerse o permanecer en modo de solo lectura. Después de reconectarse, un líder antiguo debe volver a leer el término actual y competir nuevamente en lugar de confiar en el estado en caché.
Pseudocódigo
~~~text campaign(): lease = coordinator.grant(ttl) result = coordinator.txn(key absent -> put(candidate, lease, next_term)) if result.succeeded: token = result.term keepalive(lease) runwithfencing(token) else: watch(key)
onkeepalivefailureorexpiry: stopnewwork() stopdownstreamwrites() ~~~
Complejidad, recuperación y observabilidad
Cada elección y renovación implica viajes de ida y vuelta (round trips) al coordinador; más candidatos agregan carga de watch y reintentos, por lo que se debe usar retroceso exponencial (exponential backoff) con jitter. Registra el líder actual, el término, el RTT de renovación, las expiraciones de lease, la duración de la elección, los rechazos de fencing, los trabajos duplicados y la salud del quorum para reconstruir las transiciones de términos.
| Mecanismo | Qué resuelve | Qué sigue siendo necesario |
|---|---|---|
| CAS linealizable | Evita dos ganadores | Quorum del coordinador |
| Keepalive de lease | Detecta fallas en los procesos | Manejo de pausas y particiones |
| Fencing token | Rechaza escrituras obsoletas | Validación downstream duradera |
| Watch y backoff | Acelera el failover y reduce la carga | No puede reemplazar la prueba de seguridad |
Respuesta modelo
“Almacenaría un registro de líder con lease en un coordinador respaldado por consenso con transacciones linealizables. Los candidatos compiten creando el registro solo si está ausente; el ganador recibe un término monótonamente creciente y lo renueva dentro de un TTL. Si la renovación falla, detiene inmediatamente los nuevos trabajos y las escrituras downstream. Cada escritura en base de datos, mensaje o llamada externa lleva el fencing token, y el recurso rechaza un token inferior a su valor aceptado más alto, por lo que un líder antiguo pausado o particionado no puede continuar escribiendo. Una minoría no puede emitir un nuevo término, mientras que watch solo reduce el retraso de la elección. Monitorearía el RTT de renovación, los términos, la duración del failover, los rechazos de fencing, los trabajos duplicados y el quorum, con un interruptor de apagado manual (kill switch)”.
Errores comunes
Usar solo un TTL de Redis
La expiración de un lease y la observación de la expiración por parte de un cliente no son lo mismo. El retraso de la red y las pausas pueden hacer que dos clientes crean que pueden trabajar. Utiliza coordinación linealizable junto con fencing downstream.
Tratar un lease como protección de escritura
Un lease ayuda a detectar fallas, pero no puede detener un proceso antiguo de forma instantánea. Sin validación de tokens, el líder obsoleto puede sobrescribir el resultado del nuevo líder.
Generar términos a partir de la hora local
Los relojes pueden desviarse, saltar o pausarse. El coordinador debe asignar y persistir los términos de forma atómica.
Forzar la toma de posesión durante una partición
Una minoría no puede confirmar el estado del líder anterior. Forzar la toma de posesión genera split-brain; un diseño seguro acepta una breve pérdida de disponibilidad.
Permitir que los eventos de watch decidan la seguridad
Los watches pueden retrasarse, perderse o reconectarse. Deben activar una relectura y un CAS, no reemplazar las lecturas y escrituras linealizables.
Omitir la idempotencia del trabajo
Una elección correcta no evita duplicados tras caídas o reenvíos de mensajes. Los trabajos necesitan claves de idempotencia, registros de progreso o transacciones reejecutables.
Preguntas de seguimiento y respuestas
¿En qué se diferencia la elección de líder de un bloqueo distribuido (distributed lock)?
Un lock protege una sección crítica; la elección mantiene un rol de coordinador de larga duración con términos, renovación, watches, fencing y failover. Pueden compartir un coordinador, pero su superficie de seguridad es más amplia.
¿Cómo se debe elegir el TTL?
Debe cubrir el RTT de renovación normal, pausas de GC o del planificador, jitter de red y el SLO de failover. Un TTL corto causa inestabilidad (churn); un TTL largo retrasa la toma de posesión. Calibra con inyección de fallas.
¿Por qué el recurso debe verificar el fencing token?
El coordinador no puede detener instantáneamente cada proceso obsoleto. El rechazo del lado del recurso bloquea escrituras peligrosas mientras ese proceso sigue activo.
¿Qué sucede cuando un clúster de etcd pierde el quorum?
No puede confirmar un nuevo término o estado de lease; el líder actual debe detener las escrituras después de que no pueda renovar. Los candidatos compiten nuevamente después de que regrese el quorum.
¿Cómo se gestiona una pausa prolongada del líder?
La renovación falla durante la pausa y un nuevo candidato puede tomar el control. Cuando el proceso antiguo se reanuda, su token antiguo es rechazado y debe unirse nuevamente a la elección.
¿Cómo se realizan pruebas contra el split-brain?
Inyecta pausas en los procesos, particiones, saltos de reloj y fallas del coordinador. Verifica que solo se acepte un token por término para las escrituras, e inspecciona la duración del failover y los registros de rechazo.