Salut 'Nal,
On a un compteur Redis, session:{id}:billableSecondsPending, qui accumule des secondes à facturer tant qu'une session tourne. Les clés de session vivent 15 minutes. Une fin normale les supprime tout de suite. Le TTL ne couvre que les sessions orphelines, crash ou coupure, quand plus personne n'appelle le ménage.
Le reset du compteur passait par GETSET. On lit la valeur, on la remet à zéro, un seul aller-retour, pour que deux appels concurrents ne ramassent pas le même solde. GETSET remplace aussi la valeur, et il n'a pas d'option pour garder l'expiration. La clé devient persistante. Redis documente ce comportement. Le ticket interne #601 le note comme cause de la fuite.
Si l'appel suivant qui repose un EXPIRE ne vient jamais (sauvegarde du contexte, ou nouvel incrément), la clé reste. Session déjà morte, compteur à zéro, Redis la garde. Je n'ai pas compté les clés en prod.
Le correctif est un script Lua. On lit. Si la clé existe, SET à 0 avec KEEPTTL, qui conserve le temps restant. Si elle n'existe pas, on ne touche à rien. Un SET ... KEEPTTL sur une clé absente la crée quand même, et sans TTL. La fuite reviendrait, juste plus tôt.
local prev = redis.call('GET', KEYS[1])
if prev then
redis.call('SET', KEYS[1], '0', 'KEEPTTL')
end
return prev
L'incrément avait un trou du même genre, un cran avant. INCRBY puis EXPIRE dans un second appel : un plantage entre les deux, et la clé n'a plus de durée de vie. Les deux commandes tiennent dans le même script, TTL de 900 secondes :
redis.call('INCRBY', KEYS[1], tonumber(ARGV[1]))
redis.call('EXPIRE', KEYS[1], tonumber(ARGV[2]))
GETSET est déprécié depuis Redis 6.2, au profit de SET avec l'option GET. Ça ne sauve pas le TTL. Sans KEEPTTL, ou sans un nouveau délai, SET efface l'expiration déjà posée.
# Pas compris
Posté par fork_bomb . Évalué à 1 (+0/-0).
Justement, faire un SET avec à la fois l'option GET et l'option KEEPTTL ne résout pas le problème ?
Envoyer un commentaire
Suivre le flux des commentaires
Note : les commentaires appartiennent à celles et ceux qui les ont postés. Nous n’en sommes pas responsables.