Donner aux prix en cache une identité et une durée de vie claires

RedisBeginner
Pratiquer maintenant

Introduction

Un petit magasin conserve ses prix de référence dans une base principale préparée et utilise Redis pour mémoriser les prix que les clients ont déjà consultés. Les consultations répétées doivent utiliser cette valeur mémorisée. Mais un SKU peut avoir des prix différents selon les magasins ou les devises, et un prix mémorisé doit finir par expirer.

Vous livrerez un court policy.py qui choisit l’identité complète du cache et donne une durée de vie limitée à chaque remplissage. Vous devez savoir naviguer dans un terminal et connaître GET/SET de Redis. Le modèle Python et chaque expression que vous modifiez sont expliqués ici ; aucun cours Python préalable n’est requis. La source principale est une base SQLite préparée, pas une seconde tâche d’implémentation de base de données. Cette unité indépendante utilise uniquement Terminal, avec ses propres données neuves.

Modifiez uniquement /home/labex/project/cache-desk/policy.py. Conservez les outils fournis, la table source, toutes les identités et tous les prix sources, ainsi que la note du catalogue sans rapport. Utilisez uniquement l’API source/cache documentée et des expressions ordinaires de politique dans les trois fonctions ; gardez les imports, l’exécution shell et les ressources sans rapport hors de la politique. Ne désactivez pas le cache, ne videz pas Redis globalement et ne changez pas la configuration générale. Nous testons des demandes séquentielles contrôlées, sans garantie de latence en production ni de cohérence forte. La durée estimée par l’auteur comprend la lecture et les erreurs ordinaires ; elle n’a pas été chronométrée auprès de débutants.

Distinguer les prix réellement demandés par les clients

Dans cette étape, vous observerez un véritable accès réussi au cache, trouverez une collision de clés et intégrerez l’identité complète de chaque prix à sa clé.

Commencez dans Terminal, à l’invite du shell. cd change le répertoire courant ; ce chemin absolu fonctionne depuis n’importe quel répertoire. ./desk exécute un programme préparé de ce répertoire. Chaque commande ci-dessous est une commande shell séparée, pas une entrée destinée à une invite Redis interactive.

cd /home/labex/project/cache-desk
./desk status

Lisez l’ordre des colonnes : shop / sku / currency / integer cents. North MUG USD coûte 1200 centimes, South MUG USD coûte 1500, North MUG EUR coûte 1100 et North FREE USD coûte 0. L’affichage d’état lit les prix de référence ; il ne remplit jamais le cache.

Un cache miss, ou absence dans le cache, signifie que Redis n’a aucune valeur pour la clé demandée. Un cache hit, ou accès réussi au cache, signifie qu’il en a une. Cette application préparée utilise cache-aside : lire Redis d’abord, lire la source principale uniquement en cas d’absence, puis mémoriser le résultat. Essayez deux fois la même demande normale :

./desk view north MUG USD
./desk view north MUG USD

Le premier résultat doit montrer "cents": 1200, "origin": "source", "primary_selects": 1 ; le second montre "origin": "cache", "primary_selects": 0. Le compteur provient des traces natives de SELECT SQLite pour cette demande, pas d’une accélération estimée. La sortie JSON utilise des noms de champs entre guillemets ; ces trois champs identifient le prix, l’origine et les lectures de la source principale.

Demandez maintenant un autre magasin et examinez la véritable clé Redis :

./desk view south MUG USD
./desk inspect south MUG USD
./desk policy

La clé incomplète utilise uniquement MUG. Elle peut renvoyer les 1200 de North pour South, même si la source principale stocke toujours 1500. inspect lit la clé et sa véritable valeur ; il ne répare pas le prix. Le préfixe fourni catalog délimite les clés de ce cours. Chaque clé doit commencer par le préfixe fourni suivi de deux-points, comme catalog:. Le suffixe doit distinguer les trois champs d’identité, pas seulement le SKU ; les encodages de suffixe équivalents sont acceptés.

Le modèle est en Python. def définit une fonction ; les noms entre parenthèses sont ses arguments. Un corps indenté appartient à cette fonction. return fournit son résultat. identity est un tuple de trois éléments, dans l’ordre des champs affichés : identity[0] est le magasin, [1] le SKU, [2] la devise. Les indices commencent à zéro. f"...{name}..." insère une expression dans une chaîne ; les deux-points sont ici des séparateurs ordinaires. Les identifiants du cours ne contiennent que des lettres, chiffres, traits d’union et traits de soulignement ; ils ne peuvent donc pas contenir ce séparateur. Ce format de clé n’est pas un encodage général pour du texte utilisateur quelconque.

Dans read_price, = affecte une valeur. cache.command("GET", key) envoie une véritable commande Redis. None signifie aucune valeur en cache ; if value is not None: ne prend cette branche que lorsqu’une valeur existe. Ce test explicite préserve un prix à zéro. Redis renvoie sa valeur sous forme de chaîne ; int(value) la convertit donc en centimes entiers. La virgule dans return cents, "source" renvoie une paire. source.read(identity) exécute le SELECT préparé de la source principale et lève une erreur de recherche pour une identité inconnue. Il n’y a pas de prix de référence de remplacement.

Enregistrez un fichier complet avec une clé complète. cat > policy.py remplace ce fichier ; le marqueur de here-document 'POLICY' entre guillemets fait copier littéralement par le shell chaque ligne suivante jusqu’à la ligne POLICY seule. Copiez tout le bloc en gardant une indentation de quatre espaces. Les fonctions répétées conservent le comportement de lecture préparé ; seul le choix de la clé change dans cette étape.

cat > policy.py <<'POLICY'
def cache_key(prefix, identity):
    return f"{prefix}:{identity[0]}:{identity[1]}:{identity[2]}"


def read_price(source, cache, prefix, identity):
    key = cache_key(prefix, identity)
    value = cache.command("GET", key)
    if value is not None:
        return int(value), "cache"
    cents = source.read(identity)
    cache.command("SET", key, cents)
    return cents, "source"


def change_price(source, cache, prefix, identity, cents):
    source.write(identity, cents)
POLICY

Consultez chaque identité deux fois :

./desk view north MUG USD
./desk view north MUG USD
./desk view south MUG USD
./desk view south MUG USD
./desk view north MUG EUR
./desk view north MUG EUR
./desk view north FREE USD
./desk view north FREE USD

Chaque première lecture d’une nouvelle clé complète provient de la source, et sa deuxième de Redis. Les prix sont respectivement 1200, 1500, 1100 et 0. Un accès réussi au cache avec un mauvais prix n’est pas une réussite. L’ancienne clé jetable catalog:MUG n’est plus utilisée ; aucune suppression globale n’est nécessaire.

Si vous saisissez ./deskk, le shell signale que le fichier manque. ls liste les véritables outils ; relancez ./desk avec la bonne orthographe. Si Python signale une erreur de syntaxe ou d’indentation, affichez ./desk policy, puis recopiez le bloc complet du fichier en conservant les espaces de début de ligne. Ne réinitialisez pas la VM et ne changez pas les outils préparés. La vérification de cette étape utilise des identités modifiées dans un jeu de test jetable et préserve la source principale active ; elle n’impose pas une écriture particulière de clé.

Donner une échéance à chaque remplissage

Dans cette étape, vous ajouterez une durée de vie limitée lorsqu’un prix entre dans Redis et empêcherez les accès réussis au cache de déplacer cette échéance.

Continuez dans Terminal, dans /home/labex/project/cache-desk. Examinez une clé complète existante :

./desk inspect north MUG USD

pttl_ms est la durée de vie restante en millisecondes. -1 signifie que la clé n’a aucune expiration ; -2 qu’elle est absente. Une valeur positive décroît. Une valeur sans échéance peut survivre indéfiniment à un changement de prix externe.

Redis SET key value EX seconds stocke la valeur et son expiration en une seule commande. Choisissez 15 secondes pour ce magasin pédagogique ; les remplissages acceptés doivent commencer avec une durée de 12 à 20 secondes. Cet intervalle court rend l’expiration naturelle observable ; ce n’est pas une recommandation de TTL pour la production. Définissez l’échéance uniquement en cas d’absence. La renouveler à chaque accès réussi peut garder indéfiniment en vie un ancien prix populaire. Un SET ordinaire qui écrase une clé à expiration supprime son ancienne expiration, sauf si sa préservation a été demandée explicitement.

Remplacez le fichier avec la même identité complète et un SET temporisé. En Python, les arguments séparés "EX", 15 sont transmis à Redis ; 15 est un entier, pas un identifiant entre guillemets.

cat > policy.py <<'POLICY'
def cache_key(prefix, identity):
    return f"{prefix}:{identity[0]}:{identity[1]}:{identity[2]}"


def read_price(source, cache, prefix, identity):
    key = cache_key(prefix, identity)
    value = cache.command("GET", key)
    if value is not None:
        return int(value), "cache"
    cents = source.read(identity)
    cache.command("SET", key, cents, "EX", 15)
    return cents, "source"


def change_price(source, cache, prefix, identity, cents):
    source.write(identity, cents)
POLICY

Les valeurs d’identité complète chargées à l’étape précédente n’ont toujours aucune expiration : changer le code n’ajoute pas rétroactivement une échéance. forget supprime volontairement uniquement la clé actuelle d’une identité connue, en laissant son prix principal et les autres clés intacts. Retirez individuellement ces quatre entrées d’entraînement explicitement jetables. C’est une action explicite de l’apprenant, jamais un vidage global ni une réparation cachée par le lecteur. Les remplissages suivants utiliseront tous la nouvelle échéance. Chargez ensuite le prix de North et examinez-le :

./desk forget north MUG USD
./desk forget south MUG USD
./desk forget north MUG EUR
./desk forget north FREE USD
./desk view north MUG USD
./desk inspect north MUG USD
./desk view north MUG USD
./desk inspect north MUG USD

La première consultation est source/1, la suivante cache/0 si elle a lieu avant l’expiration. Le PTTL positif diminue au lieu de redémarrer à 15000. Si la lecture prend plus de 15 secondes, une véritable absence est attendue : répétez rapidement les commandes de remplissage et d’inspection, sans changer votre politique.

Vous pouvez aussi lire les valeurs Redis natives via la connexion en lecture seule fournie. ./redis transmet ses arguments au Redis privé de ce cours, pas au service Redis générique :

./redis GET catalog:north:MUG:USD
./redis TTL catalog:north:MUG:USD

Le programme intermédiaire affiche une chaîne JSON telle que "1200", un entier TTL natif ou null pour une valeur manquante. Il ne récupère jamais un prix absent. Maintenant, sleep 16 demande au shell d’attendre 16 véritables secondes. Il ne simule pas le temps et ne supprime pas une clé pour vous :

sleep 16
./desk inspect north MUG USD
./desk view north MUG USD
./desk view north MUG USD

Avant le nouveau remplissage, inspect affiche value: null et PTTL -2. La consultation suivante relit la source et la suivante peut utiliser Redis. Tous les prix de référence restent inchangés dans cette unité. L’évaluation teste l’expiration réelle sur des clés isolées ; les clés actives peuvent naturellement expirer pendant que vous lisez, donc terminer ne dépend pas d’une course contre un court compte à rebours.

Résumé

Vous avez distingué le magasin, le SKU et la devise dans de véritables clés Redis, préservé les prix à zéro, vérifié les accès réussis au cache par rapport aux lectures de la source principale et donné à chaque remplissage une durée de vie non glissante. Votre politique est réutilisable ; la source principale préparée et les données du catalogue sans rapport restent intactes.