Cloud Witness en AlwaysOn: cómo un BGP asimétrico hizo failover al nodo equivocado
> Caso real ocurrido en abril de 2026 sobre un Availability Group de SQL Server distribuido entre dos centros de datos en alta disponibilidad activa-pasiva. Nombres de servidores, BDs y ubicaciones físicas están enmascarados; los event IDs, los tiempos y el comportamiento del cluster son los reales.
## El síntoma: un failover en la dirección contraria
Un miércoles a las 16:07 saltaron las alertas de aplicación: la BD de aplicación no respondía. Treinta segundos después, las alertas cambiaron de tono — la base de datos sí respondía, pero estaba en un nodo distinto al esperado.
Eso ya era raro de por sí. El cluster AlwaysOn tenía dos nodos del AG en configuración activa-pasiva: el nodo del DC norte era el primario, el del DC sur el réplica síncrona de contingencia. Esa configuración llevaba meses estable. Pero a las 16:07 el secundario se había convertido en primario sin que nadie hubiera tocado nada — y, lo más inquietante, sin que el nodo del DC norte hubiera muerto.
Cuando entré por RDP al nodo del DC norte, SQL Server estaba arriba, la instancia funcionando, los discos respondiendo. Solo que ya no era primario del AG. Era secondary, y el AG estaba RESOLVING durante varios segundos antes de quedarse así.
Lo segundo que comprobé fue el nodo del DC sur: arriba, primario, sirviendo a la aplicación sin un solo error. Failover limpio en apariencia, pero en la dirección contraria a la prevista por diseño.
---
## Por qué importa el "sentido" del failover
En un AG bien planificado, el failover no es un evento neutro. Hay un nodo preferente — el que está más cerca de los consumidores principales, el que tiene la mejor latencia hacia los servidores de aplicación, el que tiene los backups locales más rápidos, el que vigila el equipo principal. En este caso, el nodo del DC norte era preferente por toda esa cadena de razones operativas.
Que el AG quedara servido desde el DC sur no era un desastre — la aplicación siguió funcionando — pero sí significaba latencia extra entre los servidores de aplicación y la base de datos, backups corriendo sobre la WAN, y un montón de afterthoughts que solo se notan cuando ocurren. Lo más grave era no entender por qué había pasado. Si el motivo no quedaba claro, podía volver a pasar mañana.
---
## El primer reflejo: revisar los logs de cluster
El log de errores de SQL Server no decía nada útil. La instancia no había caído, simplemente había recibido la orden de degradarse a secundario. El sitio donde había que mirar era el log de Failover Clustering de Windows.
`powershell
# Revisar eventos FailoverClustering/Operational
Get-WinEvent -LogName 'Microsoft-Windows-FailoverClustering/Operational' -ErrorAction SilentlyContinue |
Where-Object {
$_.TimeCreated -ge [datetime]'2026-04-02 16:00' -and
$_.Id -in @(1648,1649,1650)
} |
Sort-Object TimeCreated |
Select-Object TimeCreated, Id,
@{N='Msg'; E={$_.Message.Substring(0,300)}}
`
Los IDs relevantes en este escenario son tres:
- 1648 — el cluster está esperando una respuesta del Cloud Witness.
- 1649 — un recurso ha tardado más de un minuto en responder.
- 1650 — pérdida de heartbeats entre nodos.
El timeline que pinté con esos eventos contaba una historia muy clara, pero contraintuitiva.
---
## El timeline reconstruido
A las 16:06:43, los dos nodos perdieron heartbeats entre sí (evento 1650 en ambos lados). Esto suele ser señal de que algo le ha pasado a la red entre los datacenters. En ese momento, el cluster entra en modo "necesito desempate" — cada nodo se queda solo con un voto y necesita el voto del witness para decidir quién mantiene quorum.
El Cloud Witness está en Azure (es un blob storage que actúa como árbitro). El primer nodo en obtener el lock sobre ese blob gana el voto del witness y, por tanto, el quorum. El otro nodo, al no poder reclamar el witness, se considera en minoría y baja sus recursos.
Aquí venía la sorpresa:
`
16:06:43.124 Node DC-norte ID 1650 Lost cluster heartbeats with peer node
16:06:43.131 Node DC-sur ID 1650 Lost cluster heartbeats with peer node
16:06:44.002 Node DC-sur ID 1648 Waiting for Cloud Witness response
16:06:44.018 Node DC-norte ID 1648 Waiting for Cloud Witness response
16:07:06.221 Node DC-sur Cluster witness resource acquired
16:07:46.872 Node DC-norte ID 1649 Cloud Witness resource took > 60s
`
El nodo del DC sur consiguió el witness en ~22 segundos. El nodo del DC norte tardó más de 60 segundos — para entonces, el sur ya tenía quorum y había forzado al norte a degradarse. El AG migró su rol primario al DC sur. Failover inverso completado, no por avería de SQL Server ni por avería del nodo, sino por una carrera contra el reloj que el DC norte había perdido.
---
## La pregunta correcta: ¿por qué tardó tanto el DC norte en hablar con Azure?
Para entender la causa raíz hubo que salir de SQL Server por completo y mirar la red. La sospecha inicial era una asimetría de rutas: si los dos nodos no van hacia Azure por el mismo camino, la incidencia que tumbó los heartbeats entre datacenters también puede haber afectado a la ruta hacia Azure de uno de ellos, pero no del otro.
Una traza de ICMP simultánea desde los dos nodos hacia el endpoint del Cloud Witness lo confirmó:
`
[DC sur] tracert `
La topología era esta:
`
┌─────────────────────────────────┐
│ Azure Cloud │
│ ┌─────────────────────────┐ │
│ │ Cloud Witness (blob) │ │
│ └─────────────────────────┘ │
└──────────────▲──────────────────┘
│
│ peering ISP-A
│
┌──────────────┴──────────────────┐
│ DC sur │
│ ┌─────────────────────────┐ │
│ │ Nodo AG #2 (réplica) │ │
│ └─────────────┬───────────┘ │
└─────────────────┼───────────────┘
│
│ WAN intra-DC
│ (caía BGP aquí)
│
┌─────────────────┼───────────────┐
│ DC norte │
│ ┌─────────────▼───────────┐ │
│ │ Nodo AG #1 (primario) │ │
│ └─────────────────────────┘ │
└─────────────────────────────────┘
`
El DC norte no tenía salida directa a Internet/Azure. Todo su tráfico hacia el exterior pasaba por el DC sur por diseño (única salida BGP corporativa). Cuando el enlace BGP entre datacenters se degradó, el DC norte perdió simultáneamente:
1. Los heartbeats con su pareja del DC sur.
2. La única ruta de salida hacia Azure y, por tanto, hacia el Cloud Witness.
El DC sur, por su parte, conservó la ruta directa hacia Azure porque su salida a Internet era local. La carrera contra el reloj nunca fue justa: el nodo del DC sur tardaba 22 segundos porque tenía 12 ms de latencia hacia el blob; el del DC norte tardaba más de un minuto porque su tráfico estaba intentando reencaminarse por una ruta que en ese momento ya no existía.
La conclusión incómoda: el Cloud Witness, en una topología BGP asimétrica, premia siempre al nodo con la mejor ruta a Azure, no al primario. Si tu primario está en la peor ruta, perderás el quorum cada vez que haya un corte intermedio.
---
## La mitigación inmediata
El incidente se cerró devolviendo el AG manualmente a su nodo preferente una vez restablecido BGP. Ese paso es trivial cuando ambos nodos están síncronos:
`sql
-- Desde el nodo del DC norte, una vez recuperado
ALTER AVAILABILITY GROUP [AG_APP] FAILOVER;
`
Pero el problema de fondo seguía ahí: una próxima incidencia BGP volvería a producir el mismo failover inverso. Tocaba decidir si Cloud Witness seguía siendo la elección correcta para esta topología.
---
## La discusión real: ¿Cloud Witness o File Share Witness?
Cloud Witness suele venderse como la opción "moderna y sin servidor extra". Tiene ventajas obvias: nada que mantener, alta disponibilidad gestionada por Microsoft, coste residual. Pero tiene una hipótesis implícita: que los dos nodos tienen acceso equivalente a Azure. En cuanto esa hipótesis se rompe, el witness deja de ser un árbitro neutral y se convierte en un peso que inclina la balanza hacia el nodo mejor conectado.
File Share Witness en un tercer datacenter, o incluso en una pequeña VM dentro del propio DC corporativo, no tiene este problema en arquitecturas BGP asimétricas. La latencia hacia el FSW es controlable, simétrica y conocida.
La decisión final en este entorno fue híbrida:
- Corto plazo (semanas): Cloud Witness sustituido por un File Share Witness en una VM de gestión separada de los DCs operativos, con ruta independiente desde ambos nodos. Esto restaura la simetría.
- Largo plazo (meses): revisar la topología de salida a Internet de los datacenters para que ambos tengan peering directo, momento en el que Cloud Witness vuelve a ser viable y se reevalúa.
Cambiar de witness en un cluster Windows con AG operativo se hace en caliente sin failover, desde Failover Cluster Manager o por PowerShell:
`powershell
# Eliminar Cloud Witness actual
Set-ClusterQuorum -NoWitness
# Configurar File Share Witness
Set-ClusterQuorum -FileShareWitness '\\fsw-cluster\witness$'
`
---
## La alerta proactiva que ahora tenemos
Antes del incidente nadie monitorizaba la latencia desde los nodos hacia el Cloud Witness. Ahora sí. Una tarea programada en cada nodo mide cada cinco minutos el tiempo de ida y vuelta hacia el endpoint del witness:
`powershell
$endpoint = '`
El umbral es 50 ms de media. Si cualquiera de los dos nodos pasa de ahí — o, peor, deja de responder — se dispara aviso. La idea es detectar la asimetría antes de que el siguiente corte BGP provoque otro failover inverso.
Adicionalmente, una alerta sobre el evento 1649 de FailoverClustering. Si cualquier nodo lo registra fuera de una ventana de mantenimiento, es señal de que el witness está respondiendo más lento de lo que el cluster tolera. No es una avería pero es un precursor.
---
## La lección práctica
Cloud Witness es una herramienta excelente cuando los supuestos que están detrás se cumplen. El problema es que esos supuestos casi nunca aparecen en la documentación.
1. Cloud Witness asume conectividad simétrica a Azure. Antes de elegirlo, comprueba que ambos nodos del cluster tienen latencias comparables y rutas independientes hacia el endpoint del blob. Si la salida a Internet de uno de los nodos pasa por el otro datacenter, no hay simetría.
2. El primer nodo en reclamar el witness gana el quorum. No el primario actual. No el nodo "preferente". El más rápido en hablar con Azure. En topologías BGP asimétricas, esto significa que el nodo con peor ruta a Azure pierde sistemáticamente cuando hay un corte intermedio.
3. Monitoriza la latencia al witness desde ambos nodos. La métrica no es CPU ni memoria — es tiempo de respuesta al árbitro. Si la asimetría se nota antes de un incidente, hay tiempo para cambiar de tipo de witness sin estrés.
4. File Share Witness local sigue teniendo sentido. No es vintage ni obsoleto. Es la respuesta correcta cuando tu red corporativa no garantiza simetría hacia Azure, y la simetría sale más cara o más lenta que mantener una VM dedicada.
El failover inverso fue un buen recordatorio de que la alta disponibilidad no termina en SQL Server. Termina en la ruta de red más débil del eslabón completo — y a veces ese eslabón está en el router BGP de la sala de al lado, no en el motor de base de datos.