metadata-storage-source-of-truth-desync

Par divinevideo · divine-mobile

Corrige les échecs de diffusion de contenu causés par une désynchronisation entre les métadonnées (KV store, base de données) et le stockage réel (GCS, S3, système de fichiers). À utiliser lorsque : (1) le contenu existe dans le stockage mais l'API renvoie "processing" ou "not ready", (2) des callbacks webhook censés mettre à jour les métadonnées ont échoué silencieusement, (3) des scripts de backfill ont généré du contenu sans mettre à jour les enregistrements de statut, (4) le contenu HLS/transcodé existe mais `transcode_status` n'est pas "Complete", (5) le handler vérifie le statut des métadonnées AVANT de vérifier si le contenu existe réellement dans le stockage.

npx skills add https://github.com/divinevideo/divine-mobile --skill metadata-storage-source-of-truth-desync

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_status est 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

  1. Le stockage est la source de vérité — si le fichier existe, le servir indépendamment des métadonnées
  2. 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)
  3. Enregistrer les désynchronisations — suivre la fréquence des erreurs de métadonnées pour trouver la cause racine
  4. 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

  1. Demander du contenu qui retournait précédemment 202 — devrait maintenant retourner 200 avec le contenu
  2. Vérifier les logs pour les messages « [FIX] » montrant la réparation automatique en action
  3. 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: None ou Processing dans 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

Skills similaires