Cache Local comme Fallback d'Idempotence
Problème
Les APIs externes qui devraient être idempotentes (même entrée = même sortie) ne le sont parfois pas. Cela cause des problèmes lors de la réexécution de scripts :
- Création de ressources dupliquées
- Les identifiants changent entre les exécutions
- L'état devient incohérent entre les systèmes
Contexte / Conditions de Déclenchement
- La réexécution d'un script crée de nouvelles ressources au lieu de trouver les existantes
- L'API externe a « corrigé » l'idempotence mais retourne toujours des valeurs différentes
- Besoin d'identifiants stables (pubkeys, user IDs, resource IDs) entre les exécutions
- Le script fonctionne une fois mais échoue aux exécutions suivantes en raison d'IDs modifiés
Solution
Utiliser le cache de base de données comme source de vérité pour l'idempotence :
// AVANT : Appelle toujours l'API externe (fragile)
const { pubkey, token } = await externalApi.createUser(userId, username);
await db.saveUser({ userId, pubkey, token });
// APRÈS : Vérifier le cache en premier (robuste)
const cached = await db.getUser(userId);
let pubkey: string;
let token: string;
if (cached) {
// Utiliser les valeurs en cache - stables entre les exécutions
pubkey = cached.pubkey;
token = cached.token;
console.log(`Using cached pubkey: ${pubkey}`);
} else {
// Appeler l'API externe seulement pour les nouveaux éléments
const result = await externalApi.createUser(userId, username);
pubkey = result.pubkey;
token = result.token;
// Mettre en cache immédiatement pour la prochaine exécution
await db.saveUser({ userId, pubkey, token });
console.log(`Created new pubkey: ${pubkey}`);
}
Motif Clé
- Vérifier localement en premier : Toujours interroger votre base de données avant d'appeler l'API externe
- Utiliser les valeurs en cache : Si trouvées, utiliser les valeurs locales même si obsolètes
- Ne créer que si absent : L'API externe est appelée seulement pour les nouveaux éléments
- Mettre en cache immédiatement : Sauvegarder le résultat juste après l'appel API réussi
- Logger la source : Indiquer si la valeur est « (cached) » ou « (new) » pour le débogage
Vérification
- Réexécuter le script plusieurs fois
- Même identifiant utilisé à chaque fois (depuis le cache)
- Aucune ressource dupliquée créée dans le système externe
- Le script est idempotent indépendamment du comportement de l'API externe
Exemple
Application réelle - Création de compte Keycast :
// Vérifier si nous avons un compte en cache en premier (DB local est la source de vérité)
const cached = await db.getImportedUser(creator.user_id);
let pubkey: string;
let token: string;
if (cached) {
// Utiliser le compte en cache - la pubkey est stable
pubkey = cached.pubkey;
token = cached.token;
console.log(`Pubkey: ${pubkey} (cached)`);
} else {
// Créer un nouveau compte via l'API externe
const result = await keycast.createPreloadedUser(
creator.user_id,
username,
displayName
);
pubkey = result.pubkey;
token = result.token;
console.log(`Pubkey: ${pubkey} (new)`);
// Mettre en cache le compte en base de données immédiatement
await db.saveImportedUser({
vine_user_id: creator.user_id,
username: creator.username,
pubkey,
token,
});
}
Notes
- Ce motif fonctionne même quand l'API externe prétend être idempotente
- Le schéma de base de données doit utiliser l'identifiant d'entrée comme clé primaire (prévient les doublons)
- Envisager d'ajouter des timestamps pour suivre quand les valeurs en cache ont été créées
- Pour les systèmes critiques, ajouter une logique de réconciliation pour détecter/corriger la divergence
- Le cache devient votre source de vérité - le traiter en conséquence
Motifs Associés
- Upsert on conflict : Utiliser
ON CONFLICT DO UPDATEpour gérer les conditions de course - Soft delete : Conserver les anciens enregistrements pour suivre l'historique des modifications
- Cache invalidation : Ajouter un TTL ou un rafraîchissement manuel si les valeurs externes peuvent légitimement changer
Skills Associées
stale-cache-external-service-recovery: Que faire quand le service externe perd des données et les identifiants en cache n'existent plus (détecter 404, supprimer le cache, recréer)