Désynchronisation entre métadonnées et source de vérité (stockage)
Problème
Quand un système a à la fois des métadonnées (magasin KV, champ de statut en base) et du contenu réel dans le stockage objet, les métadonnées peuvent devenir obsolètes. Si le handler de service utilise le statut des métadonnées comme gate avant de vérifier le stockage, le contenu qui existe devient inaccessible.
Causes courantes :
- Les callbacks webhook (transcodage terminé, migration faite) ont échoué silencieusement
- Les scripts de remplissage ont créé du contenu mais n'ont pas mis à jour les métadonnées
- Race conditions entre l'écriture des métadonnées et l'écriture en stockage
- Les opérations manuelles qui ont contourné le flux normal de mise à jour du statut
Contexte / Conditions de déclenchement
- L'API retourne « processing » ou « 202 Accepted » pour du contenu qui existe réellement en stockage
transcode_statusest Processing/Pending/None mais les fichiers HLS existent dans GCS- Le contenu fonctionne quand on y accède directement via l'URL du stockage mais pas via l'API
- Le script de remplissage s'est exécuté avec succès (contenu dans le bucket) mais les métadonnées n'ont pas été mises à jour
- L'endpoint webhook a eu un downtime ou a retourné des erreurs lors du traitement par batch
Solution
Pattern : stockage d'abord avec réparation automatique des métadonnées
Vérifier le stockage (source de vérité) AVANT de vérifier le statut des métadonnées. Si le contenu existe, le servir et corriger les métadonnées comme effet secondaire.
// MAUVAIS : métadonnées d'abord (bloque l'accès quand les métadonnées sont obsolètes)
match metadata.transcode_status {
Some(Complete) => { /* serve from storage */ }
Some(Processing) => { return 202; } // Blocks even if content exists!
_ => { trigger_transcoding(); return 202; }
}
// BON : stockage d'abord avec réparation automatique
match download_from_storage(&path) {
Ok(content) => {
// Content exists — serve it and fix metadata if needed
if metadata.transcode_status != Some(Complete) {
eprintln!("[FIX] {} has content but status was {:?}", id, metadata.transcode_status);
update_status(id, Complete); // Auto-repair
}
serve(content)
}
Err(NotFound) => {
// Content truly doesn't exist — now check metadata for status
match metadata.transcode_status {
Some(Processing) => return 202,
_ => { trigger_processing(); return 202; }
}
}
}
Principes clés
- Le stockage est la source de vérité — si le fichier existe, le servir indépendamment des métadonnées
- Réparer automatiquement les métadonnées — quand vous trouvez une désynchronisation, la corriger en ligne (meilleur effort, ne pas échouer si la mise à jour échoue)
- Enregistrer les désynchronisations — suivre la fréquence des erreurs de métadonnées pour trouver la cause racine
- Ne pas bloquer sur les métadonnées — le statut des métadonnées doit être consultatif, pas un gate
Appliquer aussi aux requêtes HEAD
Les handlers HEAD ont souvent le même bug. Appliquer le même pattern stockage d'abord :
// HEAD: Check storage, not just metadata
match check_storage_exists(&path) {
Ok(true) => {
if metadata.status != Complete {
update_status(id, Complete);
}
return 200;
}
Ok(false) | Err(_) => {
// Fall back to metadata-based response
}
}
Vérification
- Demander du contenu qui retournait précédemment 202 — devrait maintenant retourner 200 avec le contenu
- Vérifier les logs pour les messages « [FIX] » montrant la réparation automatique en action
- Après l'échauffement du trafic, vérifier que les métadonnées ont été corrigées
Exemple
Dans Divine Blossom, le handler HLS vérifiait transcode_status dans KV avant GCS :
- ~57 000 objets dans le bucket GCS, la plupart avec des transcodages HLS
- Beaucoup avaient
transcode_status: NoneouProcessingdans KV (webhooks échoués lors du remplissage) - Le handler retournait « 202 transcoding in progress » pour du contenu qui était déjà dans GCS
- Correction : vérifier GCS d'abord, servir s'il existe, réparer les métadonnées KV comme effet secondaire
Notes
- La réparation automatique est meilleur effort — si la mise à jour des métadonnées échoue, ne pas échouer la requête
- Ce pattern fonctionne bien avec les couches de cache (CDN, VCL) car la réparation ne s'exécute que sur les cache miss, et les requêtes suivantes accèdent au cache
- Envisager d'exécuter un script de réparation en batch des métadonnées pour corriger tous les enregistrements désynchronisés à la fois, plutôt que de compter uniquement sur la réparation à la demande
- La cause racine devrait quand même être investiguée (pourquoi les webhooks ont-ils échoué ?) pour prévenir les futures désynchronisations