Redis n'est pas une base relationnelle. Il n'y a pas de tables, pas de schéma, pas de SQL. Un client de base de données conçu autour de la ligne, de la colonne et de la requête doit donc décider comment représenter un keyspace sans le trahir. Dans QoreDB, le driver Redis expose les clés comme des collections, dispatche l'affichage selon le type Redis de chaque clé, et refuse de simuler des concepts qui n'existent pas côté serveur.
L'objectif est double. D'abord donner à quelqu'un qui utilise déjà PostgreSQL ou MongoDB dans la même app un repère cohérent : une base, une liste de choses navigables, un panneau de contenu. Ensuite ne rien inventer : les commandes affichées, les types, la pagination reflètent exactement ce que le serveur expose. Cet article décrit trois choix qui structurent le driver : le remplacement systématique de KEYS par SCAN, la lecture polymorphe des valeurs par TYPE, et la classification de sécurité propre à Redis.
SCAN plutôt que KEYS, par principe
Sur une instance chargée, la commande KEYS bloque le serveur mono-thread le temps de parcourir l'ensemble du keyspace. C'est acceptable dans un shell de développement local, pas dans un client desktop qu'un utilisateur peut brancher sur une prod. Le driver n'expose donc jamais KEYS pour lister le contenu d'une base. Le listing des collections repose exclusivement sur SCAN, avec un curseur que le driver relance jusqu'à ce que le serveur renvoie zéro.
Chaque appel SCAN utilise un COUNT de 500 et un MATCH construit à partir du champ de recherche. Si l'utilisateur tape une chaîne dans le filtre de la sidebar, elle est encapsulée dans un pattern glob. Sans filtre, le pattern reste une étoile. Le curseur est initialisé à zéro, et la boucle continue jusqu'au retour d'un curseur nul. Cette mécanique reste incrémentale : le serveur garde la main entre deux appels, aucune commande longue ne bloque les autres clients.
La pagination pose un problème que SCAN ne résout pas seul : il n'y a pas d'ordre stable, et parcourir tout le keyspace deux fois n'est pas la même chose à quelques millisecondes d'intervalle. Le driver traite ce cas avec un tas borné. Pour une page donnée, il maintient un max-heap qui garde uniquement les plus petites clés dans la limite offset plus limit, quel que soit le volume total. Le total renvoyé au frontend est un compteur agrégé sur tout le scan, ce qui donne un ordre de grandeur fiable sans matérialiser un million de clés côté client. En listing complet sans pagination, un plafond dur à 100 000 clés protège la mémoire du process.
Une navigation guidée par TYPE
Redis a cinq types de valeur principaux : string, list, hash, set et sorted set. Une même base peut mélanger les cinq sur des clés voisines. Ouvrir une clé sans connaître son type et tenter GET partout renverrait des erreurs WRONGTYPE en cascade. Le driver commence donc toute lecture par TYPE. Le résultat détermine la commande à utiliser, les colonnes retournées et la stratégie de pagination.
Pour une string, GET renvoie une seule ligne avec une colonne value. Pour une list, LRANGE permet de découper par index avec un offset et un stop calculés à partir de la page demandée : la pagination est native, Redis répond directement sur la plage utile. Pour un hash, HSCAN itère par curseur sur les couples champ/valeur et le driver assemble deux colonnes field et value. Pour un set, SSCAN fait pareil sur une seule colonne member. Pour un sorted set, ZRANGE avec l'option WITHSCORES donne un tableau à deux colonnes member et score.
Cette approche a une conséquence directe : l'UI ne prétend pas afficher un "table row" uniforme. Le nom des colonnes change avec le type. Le TTL est requêté séparément par TTL, ce qui laisse la valeur -2 identifier une clé expirée entre le listing et l'ouverture. Rien n'est simulé côté client, rien n'est extrapolé. Quand on clique sur une clé de type hash, on lit un hash. Quand on clique sur une clé de type stream, le driver renvoie une erreur explicite plutôt que d'afficher un résultat approximatif.
Les commandes Redis restent des commandes Redis
L'éditeur de requête envoie du texte au driver, exactement comme redis-cli. Il n'y a pas de couche SQL intermédiaire, pas d'AST commun, pas de traduction. Une ligne comme HGETALL user:42 ou XADD mystream telle qu'elle serait tapée dans un shell arrive côté serveur inchangée. Le driver parse simplement la ligne, extrait la commande et ses arguments, et les passe à redis-rs. Le mapping des retours vers le format tabulaire de QoreDB se fait après.
Cette transparence évite deux dettes qu'on aurait accumulées en abstrayant Redis derrière une DSL. D'abord, l'app suit automatiquement les évolutions du protocole : un nouveau champ de retour, une nouvelle sous-commande, un nouveau type se propagent sans changement dans le driver. Ensuite, un développeur qui connaît Redis n'a rien à réapprendre. Ce qu'il tape dans redis-cli marche dans QoreDB, avec en plus un affichage tabulaire quand la commande renvoie du contenu structuré.
La sélection de base fonctionne selon le même principe. SELECT db3 est capté avant d'être envoyé au serveur uniquement pour tenir à jour le compteur current_db du côté du driver ; la commande elle-même part bien vers Redis. Cette double comptabilité sert au moment où le frontend veut afficher "vous êtes sur db3" sans refaire un round-trip.
Une classification de sécurité adaptée à Redis
La logique de production safety qui existe pour PostgreSQL ou MySQL doit être réécrite pour Redis, parce que les catégories ne se recouvrent pas. Il n'existe pas de mot-clé DROP ou DELETE global ; en revanche FLUSHDB, FLUSHALL, SHUTDOWN, EVAL ou MIGRATE peuvent effacer, exécuter du code arbitraire ou exfiltrer des données. Le driver classe donc chaque commande en Read, Mutation, Dangerous ou Unknown, avec des règles spécifiques au protocole.
FLUSHALL, FLUSHDB, SHUTDOWN et SWAPDB sont marquées Dangerous. EVAL, EVALSHA, FCALL et MIGRATE aussi : elles peuvent contourner toute autre restriction, soit en exécutant du Lua qui appelle CONFIG SET, soit en poussant les données vers un hôte arbitraire. Certaines sous-commandes sont analysées finement, comme CONFIG SET, SCRIPT FLUSH, FUNCTION DELETE, MODULE LOAD ou CLUSTER RESET. Sur une session déclarée en production ou en read-only, les commandes classées Dangerous ou Mutation sont bloquées avant même d'être envoyées au serveur.
Cette classification est délibérément conservatrice pour les commandes inconnues. Une commande absente des listes tombe dans Unknown et suit la politique par défaut du garde-fou. Mieux vaut demander une confirmation explicite qu'exécuter en silence quelque chose de mal compris.
En usage courant
Concrètement, connecter une instance Redis ouvre une arborescence avec seize databases numérotées (le comportement par défaut de Redis standalone). Chaque base présente ses clés paginées. Le champ de recherche transforme sa valeur en pattern glob que SCAN évalue côté serveur. Cliquer sur une clé déclenche un TYPE, puis la commande de lecture appropriée. La cellule TTL est peuplée en parallèle. L'éditeur permet toutes les commandes que le serveur accepte, avec le classifieur en amont pour les scénarios prod.
L'annulation d'une requête utilise un AbortHandle attaché à chaque QueryId : quand le futur est annulé, la tâche s'interrompt côté client. Redis ne propose pas de mécanisme d'interruption d'une commande en vol, mais dans le cas typique d'un SCAN paginé, l'utilisateur peut couper la boucle sans attendre la fin du keyspace.
Conclusion
Le driver Redis illustre ce que le trait DataEngine autorise dans QoreDB : chaque moteur peut faire ce qu'il fait le mieux, sans être forcé dans un modèle relationnel qu'il ne partage pas. Les clés sont des collections, le type détermine la lecture, SCAN remplace KEYS partout où c'était possible, et la classification de sécurité est écrite pour le protocole réel de Redis. Le résultat est un client qui reste utile sur une prod chargée sans mentir à son utilisateur sur ce qu'est vraiment un keyspace Redis.
Restez informé des nouveautés
Rejoignez notre newsletter pour recevoir les mises à jour majeures, les nouveaux drivers et nos coulisses techniques.

