Redis : deux stores, deux promesses
Le paradoxe apparent
Le mĂȘme thread_id de conversation copilot vit dans deux stores : le checkpointer LangGraph dans Postgres, et l'historique de chat dans Redis. Doublon ? Non â deux promesses diffĂ©rentes :
| Checkpointer (Postgres) | Store conversation (Redis) | |
|---|---|---|
| Contient | l'état d'exécution du graphe agent | les messages affichables + titres + tri sidebar |
| Promesse | ne doit jamais mentir (reprise aprĂšs crash) | best-effort (une erreur â historique vide, l'app continue) |
| Durée de vie | la vie du thread | TTL 30 jours glissant (réinitialisé à chaque activité) |
| Consommateur | LangGraph (reprise, interrupts) | l'UI (sidebar, rechargement de page) |
La leçon gĂ©nĂ©rale : la source de vĂ©ritĂ© et la projection d'affichage n'ont ni le mĂȘme SLA ni le mĂȘme cycle de vie â leur donner le mĂȘme store, c'est imposer Ă l'un les contraintes de l'autre.
L'anatomie des clés Redis
conv:{tenant}:{user}:{conv} â JSON : les messages du thread
conv:meta:{tenant}:{user}:{conv} â JSON : {titre, nb_messages, derniĂšre_activitĂ©}
conv:list:{tenant}:{user} â ZSET : conv_ids scorĂ©s par timestamp (le tri de la sidebar)
Trois idées dedans :
- l'isolation tenant/user est dans la forme de la clĂ© â pas de RLS en Redis, la clĂ© EST le scope ;
- le ZSET donne la sidebar triée par activité en une commande (
zrevrange) â la structure de donnĂ©es choisie EST la requĂȘte ; - TTL glissant : rĂ©-armĂ© Ă chaque Ă©criture â un thread actif ne meurt jamais, un thread abandonnĂ© s'efface tout seul (RGPD-friendly, zĂ©ro job de purge).
La discipline single-key (cluster-ready)
Toutes les opĂ©rations sont mono-clĂ© (set, get, zadd, zrevrange, expireâŠ) â jamais de transaction multi-clĂ©s. C'est ce qui rend le code identique entre le Redis standalone du dev et le cluster ElastiCache 3 shards de la prod : en cluster, les clĂ©s sont rĂ©parties par slot, et une op multi-clĂ©s exige que toutes vivent sur le mĂȘme nĆud (hash-tags, complexitĂ©). La consĂ©quence assumĂ©e : de petites incohĂ©rences possibles (une entrĂ©e de ZSET dont le meta a expirĂ©) â nettoyĂ©es paresseusement Ă la lecture plutĂŽt que prĂ©venues par transaction.
Et l'invariant privacy du module 4 se prolonge ici : Redis ne persiste que les model_view des rĂ©sultats de tools â les ui_events (donnĂ©es complĂštes) ne sont jamais stockĂ©s. Recharger la page ne peut pas ressusciter des donnĂ©es que l'utilisateur n'aurait plus le droit de voir.
Pourquoi le mĂȘme thread vit-il dans Postgres ET Redis ?