Infrastructure
Azure SQL : qualifier un failover group avant un basculement forcé
Un runbook de production pour mesurer la réplication, vérifier le listener et la capacité secondaire, puis décider basculement planifié, forcé ou maintien en place.
Une région répond mal, les connexions Azure SQL expirent et le failover group existe précisément pour ce scénario. La tentation est immédiate : promouvoir le secondaire avec --allow-data-loss pour rétablir le service. Cette commande peut réduire l’indisponibilité, mais elle transforme aussi un incident de disponibilité en décision irréversible sur les données.
Le cas d’usage est une API transactionnelle dont plusieurs bases appartiennent au même failover group. Le primaire est dégradé sans être totalement inaccessible. L’équipe doit établir l’état de réplication, prouver que l’application utilise le listener, valider la région secondaire et choisir entre attente, basculement planifié ou basculement forcé. Le runbook se termine par une décision documentée et un chemin de failback, pas par un clic isolé.
Figer le contrat de décision
Avant toute commande d’écriture, consignez l’heure UTC du début d’impact, les bases concernées, le RPO accepté et le point au-delà duquel l’indisponibilité devient plus coûteuse qu’une perte de données potentielle.
incident_id: INC-2026-0916-sql
failover_group: fog-orders-prod
primary_server: sql-orders-weu
secondary_server: sql-orders-neu
read_write_listener: fog-orders-prod.database.windows.net
affected_databases:
- orders
- payments
rpo_max_seconds: 30
rto_max_minutes: 15
write_freeze_requested: true
forced_failover_approvers:
- incident-commander
- data-owner
rollback: planned-failback-after-primary-recovery-and-validation Un RPO ne doit pas être reformulé en « quelques secondes ». Il doit être comparé au replication_lag_sec observé et aux transactions métier produites pendant cette fenêtre. Si le primaire accepte encore des écritures, tentez de les suspendre proprement : chaque nouvelle transaction agrandit l’enveloppe de divergence.
Prouver la topologie et le rôle courant
Interrogez le groupe depuis le serveur que l’équipe considère comme primaire. Ne déduisez pas le rôle depuis le nom de la ressource, une ancienne documentation ou la région habituellement active.
PRIMARY_RG="rg-data-weu"
PRIMARY_SERVER="sql-orders-weu"
SECONDARY_RG="rg-data-neu"
SECONDARY_SERVER="sql-orders-neu"
FOG="fog-orders-prod"
az sql failover-group show \
--resource-group "$PRIMARY_RG" \
--server "$PRIMARY_SERVER" \
--name "$FOG" \
--query "{role:replicationRole,databases:databases,partners:partnerServers,readWrite:readWriteEndpoint,readOnly:readOnlyEndpoint}" \
--output json
az sql failover-group show \
--resource-group "$SECONDARY_RG" \
--server "$SECONDARY_SERVER" \
--name "$FOG" \
--query "{role:replicationRole,databases:databases,partners:partnerServers}" \
--output json Les deux vues doivent décrire le même groupe, les mêmes bases et des rôles opposés. Une base absente, un partenaire inattendu ou un groupe visible d’un seul côté est un incident de topologie à qualifier avant le failover.
Mesurer la réplication base par base
Un failover group bascule toutes ses bases ensemble, mais leur retard n’est pas nécessairement identique. Commencez par l’état exposé par Azure Resource Manager, puis mesurez la liaison dans chaque base encore joignable.
for DB in orders payments; do
az sql db replica list-links \
--resource-group "$PRIMARY_RG" \
--server "$PRIMARY_SERVER" \
--name "$DB" \
--query "[].{database:databaseName,partner:partnerServer,role:role,replicationState:replicationState,percentComplete:percentComplete}" \
--output table
done Depuis chaque base primaire, la vue dynamique fournit la preuve la plus proche du risque de perte :
SELECT
DB_NAME() AS database_name,
partner_server,
partner_database,
replication_state_desc,
last_replication,
replication_lag_sec
FROM sys.dm_geo_replication_link_status; Exécutez cette requête séparément dans chaque base, pas dans master. Conservez l’heure de collecte et le résultat brut. CATCH_UP avec un lag faible rend un basculement planifié plausible ; SUSPENDED, un last_replication ancien ou une base inaccessible impose d’évaluer explicitement la perte. Une moyenne masque la base la plus en retard : la décision porte sur le maximum observé et sur sa valeur métier.
Vérifier la cible avant de la promouvoir
Une réplique à jour n’est pas encore une région de reprise prête. Vérifiez que le serveur secondaire et ses dépendances peuvent absorber la charge d’écriture : service tier et capacité, règles d’accès, identités Entra, clés gérées, Private DNS si le chemin est privé, quotas et composants applicatifs régionaux.
Le Private Endpoint, lorsqu’il existe, n’est qu’un segment du chemin. Le contrôle utile consiste à résoudre et joindre le listener depuis les workloads qui seront actifs après le basculement, puis à confirmer que leur identité peut ouvrir une session sur la base secondaire sans utiliser le nom direct du serveur.
Control Expected proof
Server and database capacity Same approved tier or tested DR capacity
Replication Every database present; worst lag recorded
Listener path Application uses failover-group listener
DNS Listener resolves from future active workloads
Identity Login succeeds with production runtime identity
Dependencies App, queue, storage and secrets ready in target region
Write protection No uncontrolled dual writer remains active
Observability Errors, latency and business checks visible by region Si l’application se connecte à sql-orders-weu.database.windows.net au lieu du listener, le failover Azure ne déplacera pas ce trafic. Corrigez ou préparez cette dépendance avant de compter sur le groupe.
Qualifier le comportement applicatif et DNS
Le listener read-write conserve son nom et Azure met à jour sa cible après le basculement. Cela ne garantit pas que tous les processus rouvrent immédiatement leurs connexions. Les pools existants, les caches DNS et les retries mal bornés peuvent prolonger l’incident ou multiplier les écritures ambiguës.
Depuis chaque zone d’exécution, capturez avant le failover :
- la chaîne de connexion sans secret, notamment le hostname et la base ;
- la résolution du listener et son TTL observé ;
- le nombre de connexions actives et la capacité à les renouveler ;
- la politique de retry pour les erreurs transitoires ;
- un identifiant métier permettant de vérifier la dernière écriture confirmée.
Le canari doit ouvrir une nouvelle connexion via le listener. Une session SQL restée ouverte avant la bascule n’est pas une preuve de reroutage.
Choisir le mode de basculement
Si le primaire et le secondaire communiquent, utilisez un basculement planifié. Le service synchronise les bases avant d’inverser les rôles, ce qui vise un changement sans perte de données.
az sql failover-group set-primary \
--resource-group "$SECONDARY_RG" \
--server "$SECONDARY_SERVER" \
--name "$FOG" Si le primaire est indisponible et que le RTO impose une reprise, --allow-data-loss autorise un basculement même lorsque la synchronisation ne peut pas aboutir. Ne l’exécutez qu’après avoir consigné le pire lag observé, la dernière transaction métier vérifiable, les approbateurs et les conséquences attendues.
az sql failover-group set-primary \
--resource-group "$SECONDARY_RG" \
--server "$SECONDARY_SERVER" \
--name "$FOG" \
--allow-data-loss L’option --try-planned-before-forced-failover essaie d’abord le chemin planifié puis force si celui-ci échoue. Elle reste une autorisation de perdre des données : ne la traitez pas comme une variante « plus sûre » sans critère d’arrêt.
Valider le nouveau primaire sans fermer trop tôt
Après la commande, relisez le rôle sur les deux serveurs et attendez que toutes les bases aient changé de rôle. Ouvrez ensuite de nouvelles connexions via le listener, jamais via le hostname direct.
La validation minimale couvre :
- le listener read-write mène au nouveau primaire depuis chaque workload actif ;
- une lecture retrouve la dernière transaction attendue ou borne précisément l’écart ;
- une écriture canari idempotente est confirmée puis relue ;
- les erreurs de connexion, retries et latences reviennent dans la plage approuvée ;
- aucun composant resté dans l’ancienne région ne continue à écrire directement.
Conservez l’identifiant du canari, les heures UTC, les sorties CLI et le résultat métier. Un statut Azure Primary sans transaction applicative ne suffit pas.
Décider maintien, failback ou restauration
MAINTAIN THE NEW PRIMARY
Listener, canary and business checks pass
Data-loss envelope is measured and accepted
Old-region writers are stopped or fenced
HOLD AND INVESTIGATE
Roles changed but one database, dependency or workload is unhealthy
Keep writes bounded; do not stack another failover on uncertain state
PLANNED FAILBACK
Original region is stable and replication has caught up
Execute a planned role reversal during an approved window
Repeat listener, write and business validation
RESTORE OR RECONCILE
Forced failover lost confirmed transactions
Preserve both timelines and reconcile from business evidence
Never merge by replaying an unbounded queue or old writer Le rollback n’est pas un second failover forcé lancé par réflexe. Une fois l’ancienne région revenue, attendez que la réplication soit saine, bloquez tout writer résiduel et effectuez un failback planifié. Si des transactions confirmées manquent, le chemin est une restauration ou une réconciliation métier, pas une simple inversion de rôle.
Conclusion
Un failover group Azure SQL fournit le mécanisme de changement de rôle ; il ne prend pas la décision de perte de données à la place de l’équipe. Le runbook exploitable relie le rôle réel, le pire lag par base, le listener utilisé par l’application, la capacité de la cible et une preuve métier.
La décision devient alors explicite : attendre si le risque de perte dépasse l’impact d’indisponibilité, basculer de façon planifiée lorsque la synchronisation reste possible, ou forcer uniquement avec une enveloppe de perte acceptée. Après reprise, validez par une nouvelle connexion et une écriture canari, puis préparez un failback planifié ou une réconciliation documentée.