NOTE
Le client Redis de Bun prend en charge les versions 7.2 et supérieures du serveur Redis.Bun fournit des liaisons natives pour travailler avec les bases de données Redis avec une API moderne basée sur les Promesses. L'interface est conçue pour être simple et performante, avec une gestion de connexion intégrée, des réponses entièrement typées et une prise en charge de TLS.
import { redis } from "bun";
// Définir une clé
await redis.set("greeting", "Hello from Bun!");
// Récupérer une clé
const greeting = await redis.get("greeting");
console.log(greeting); // "Hello from Bun!"
// Incrémenter un compteur
await redis.set("counter", 0);
await redis.incr("counter");
// Vérifier si une clé existe
const exists = await redis.exists("greeting");
// Supprimer une clé
await redis.del("greeting");Démarrage
Pour utiliser le client Redis, vous devez d'abord créer une connexion :
import { redis, RedisClient } from "bun";
// Utilisation du client par défaut (lit les informations de connexion depuis l'environnement)
// process.env.REDIS_URL est utilisé par défaut
await redis.set("hello", "world");
const result = await redis.get("hello");
// Création d'un client personnalisé
const client = new RedisClient("redis://username:password@localhost:6379");
await client.set("counter", "0");
await client.incr("counter");Par défaut, le client lit les informations de connexion depuis les variables d'environnement suivantes (par ordre de priorité) :
REDIS_URLVALKEY_URL- Si non défini, la valeur par défaut est
"redis://localhost:6379"
Cycle de vie de la connexion
Le client Redis gère automatiquement les connexions en arrière-plan :
// Aucune connexion n'est établie tant qu'une commande n'est pas exécutée
const client = new RedisClient();
// La première commande initialise la connexion
await client.set("key", "value");
// La connexion reste ouverte pour les commandes suivantes
await client.get("key");
// Fermer explicitement la connexion lorsque terminé
client.close();Vous pouvez également contrôler manuellement le cycle de vie de la connexion :
const client = new RedisClient();
// Se connecter explicitement
await client.connect();
// Exécuter des commandes
await client.set("key", "value");
// Se déconnecter lorsque terminé
client.close();Opérations de base
Opérations sur les chaînes
// Définir une clé
await redis.set("user:1:name", "Alice");
// Récupérer une clé
const name = await redis.get("user:1:name");
// Récupérer une clé comme Uint8Array
const buffer = await redis.getBuffer("user:1:name");
// Supprimer une clé
await redis.del("user:1:name");
// Vérifier si une clé existe
const exists = await redis.exists("user:1:name");
// Définir l'expiration (en secondes)
await redis.set("session:123", "active");
await redis.expire("session:123", 3600); // expire dans 1 heure
// Obtenir le temps de vie restant (en secondes)
const ttl = await redis.ttl("session:123");Opérations numériques
// Définir la valeur initiale
await redis.set("counter", "0");
// Incrémenter de 1
await redis.incr("counter");
// Décrémenter de 1
await redis.decr("counter");Opérations sur les hachages
// Définir plusieurs champs dans un hachage
await redis.hmset("user:123", ["name", "Alice", "email", "alice@example.com", "active", "true"]);
// Récupérer plusieurs champs depuis un hachage
const userFields = await redis.hmget("user:123", ["name", "email"]);
console.log(userFields); // ["Alice", "alice@example.com"]
// Récupérer un seul champ depuis un hachage (retourne la valeur directement, null si manquant)
const userName = await redis.hget("user:123", "name");
console.log(userName); // "Alice"
// Incrémenter un champ numérique dans un hachage
await redis.hincrby("user:123", "visits", 1);
// Incrémenter un champ flottant dans un hachage
await redis.hincrbyfloat("user:123", "score", 1.5);Opérations sur les ensembles
// Ajouter un membre à un ensemble
await redis.sadd("tags", "javascript");
// Supprimer un membre d'un ensemble
await redis.srem("tags", "javascript");
// Vérifier si un membre existe dans un ensemble
const isMember = await redis.sismember("tags", "javascript");
// Récupérer tous les membres d'un ensemble
const allTags = await redis.smembers("tags");
// Récupérer un membre aléatoire
const randomTag = await redis.srandmember("tags");
// Pop (supprimer et retourner) un membre aléatoire
const poppedTag = await redis.spop("tags");Pub/Sub
Bun fournit des liaisons natives pour le protocole Redis Pub/Sub. Nouveau dans Bun 1.2.23
Utilisation de base
Pour commencer à publier des messages, vous pouvez configurer un éditeur dans publisher.ts :
import { RedisClient } from "bun";
const writer = new RedisClient("redis://localhost:6739");
await writer.connect();
writer.publish("general", "Hello everyone!");
writer.close();Dans un autre fichier, créez l'abonné dans subscriber.ts :
import { RedisClient } from "bun";
const listener = new RedisClient("redis://localhost:6739");
await listener.connect();
await listener.subscribe("general", (message, channel) => {
console.log(`Reçu : ${message}`);
});Dans un shell, exécutez votre abonné :
bun run subscriber.tset, dans un autre, exécutez votre éditeur :
bun run publisher.tsNOTE
Le mode d'abonnement prend le contrôle de la connexion `RedisClient`. Un client avec des abonnements ne peut appeler que `RedisClient.prototype.subscribe()`. En d'autres termes, les applications qui doivent envoyer des messages à Redis ont besoin d'une connexion séparée, obtenable via `.duplicate()` :import { RedisClient } from "bun";
const redis = new RedisClient("redis://localhost:6379");
await redis.connect();
const subscriber = await redis.duplicate();
await subscriber.subscribe("foo", () => {});
await redis.set("bar", "baz");Publication
La publication de messages se fait via la méthode publish() :
await client.publish(channelName, message);Abonnements
Le RedisClient de Bun vous permet de vous abonner à des canaux via la méthode .subscribe() :
await client.subscribe(channel, (message, channel) => {});Vous pouvez vous désabonner via la méthode .unsubscribe() :
await client.unsubscribe(); // Se désabonner de tous les canaux.
await client.unsubscribe(channel); // Se désabonner d'un canal particulier.
await client.unsubscribe(channel, listener); // Se désabonner d'un écouteur particulier.Utilisation avancée
Exécution de commandes et pipelining
Le client pipeline automatiquement les commandes, améliorant les performances en envoyant plusieurs commandes par lot et en traitant les réponses au fur et à mesure de leur arrivée.
// Les commandes sont automatiquement pipelinées par défaut
const [infoResult, listResult] = await Promise.all([redis.get("user:1:name"), redis.get("user:2:email")]);Pour désactiver le pipelining automatique, vous pouvez définir l'option enableAutoPipelining à false :
const client = new RedisClient("redis://localhost:6379", {
enableAutoPipelining: false,
});Commandes brutes
Lorsque vous devez utiliser des commandes qui n'ont pas de méthodes de commodité, vous pouvez utiliser la méthode send :
// Exécuter n'importe quelle commande Redis
const info = await redis.send("INFO", []);
// LPUSH vers une liste
await redis.send("LPUSH", ["mylist", "value1", "value2"]);
// Récupérer une plage de liste
const list = await redis.send("LRANGE", ["mylist", "0", "-1"]);La méthode send vous permet d'utiliser n'importe quelle commande Redis, même celles qui n'ont pas de méthodes dédiées dans le client. Le premier argument est le nom de la commande, et le deuxième argument est un tableau d'arguments de type chaîne.
Événements de connexion
Vous pouvez enregistrer des gestionnaires pour les événements de connexion :
const client = new RedisClient();
// Appelé lors de la connexion réussie au serveur Redis
client.onconnect = () => {
console.log("Connecté au serveur Redis");
};
// Appelé lors de la déconnexion du serveur Redis
client.onclose = error => {
console.error("Déconnecté du serveur Redis :", error);
};
// Connecter/déconnecter manuellement
await client.connect();
client.close();État et surveillance de la connexion
// Vérifier si connecté
console.log(client.connected); // booléen indiquant l'état de connexion
// Vérifier la quantité de données en mémoire tampon (en octets)
console.log(client.bufferedAmount);Conversion de type
Le client Redis gère automatiquement la conversion de type pour les réponses Redis :
- Les réponses entières sont retournées comme des nombres JavaScript
- Les chaînes bulk sont retournées comme des chaînes JavaScript
- Les chaînes simples sont retournées comme des chaînes JavaScript
- Les chaînes bulk nulles sont retournées comme
null - Les réponses de type tableau sont retournées comme des tableaux JavaScript
- Les réponses d'erreur lancent des erreurs JavaScript avec des codes d'erreur appropriés
- Les réponses booléennes (RESP3) sont retournées comme des booléens JavaScript
- Les réponses de type map (RESP3) sont retournées comme des objets JavaScript
- Les réponses de type set (RESP3) sont retournées comme des tableaux JavaScript
Traitement spécial pour certaines commandes :
EXISTSretourne un booléen au lieu d'un nombre (1 devient true, 0 devient false)SISMEMBERretourne un booléen (1 devient true, 0 devient false)
Les commandes suivantes désactivent le pipelining automatique :
AUTHINFOQUITEXECMULTIWATCHSCRIPTSELECTCLUSTERDISCARDUNWATCHPIPELINESUBSCRIBEUNSUBSCRIBEUNPSUBSCRIBE
Options de connexion
Lors de la création d'un client, vous pouvez passer diverses options pour configurer la connexion :
const client = new RedisClient("redis://localhost:6379", {
// Délai d'attente de connexion en millisecondes (par défaut : 10000)
connectionTimeout: 5000,
// Délai d'inactivité en millisecondes (par défaut : 0 = pas de délai)
idleTimeout: 30000,
// S'il faut se reconnecter automatiquement en cas de déconnexion (par défaut : true)
autoReconnect: true,
// Nombre maximum de tentatives de reconnexion (par défaut : 10)
maxRetries: 10,
// S'il faut mettre en file d'attente les commandes en cas de déconnexion (par défaut : true)
enableOfflineQueue: true,
// S'il faut automatiquement pipeliner les commandes (par défaut : true)
enableAutoPipelining: true,
// Options TLS (par défaut : false)
tls: true,
// Alternativement, fournir une configuration TLS personnalisée :
// tls: {
// rejectUnauthorized: true,
// ca: "path/to/ca.pem",
// cert: "path/to/cert.pem",
// key: "path/to/key.pem",
// }
});Comportement de reconnexion
Lorsqu'une connexion est perdue, le client tente automatiquement de se reconnecter avec un backoff exponentiel :
- Le client commence avec un petit délai (50ms) et le double à chaque tentative
- Le délai de reconnexion est plafonné à 2000ms (2 secondes)
- Le client tente de se reconnecter jusqu'à
maxRetriesfois (par défaut : 10) - Les commandes exécutées pendant la déconnexion sont :
- Mises en file d'attente si
enableOfflineQueueest true (par défaut) - Rejetées immédiatement si
enableOfflineQueueest false
- Mises en file d'attente si
Formats d'URL pris en charge
Le client Redis prend en charge divers formats d'URL :
// URL Redis standard
new RedisClient("redis://localhost:6379");
new RedisClient("redis://localhost:6379");
// Avec authentification
new RedisClient("redis://username:password@localhost:6379");
// Avec numéro de base de données
new RedisClient("redis://localhost:6379/0");
// Connexions TLS
new RedisClient("rediss://localhost:6379");
new RedisClient("rediss://localhost:6379");
new RedisClient("redis+tls://localhost:6379");
new RedisClient("redis+tls://localhost:6379");
// Connexions socket Unix
new RedisClient("redis+unix:///path/to/socket");
new RedisClient("redis+unix:///path/to/socket");
// TLS sur socket Unix
new RedisClient("redis+tls+unix:///path/to/socket");
new RedisClient("redis+tls+unix:///path/to/socket");Gestion des erreurs
Le client Redis lance des erreurs typées pour différents scénarios :
try {
await redis.get("non-existent-key");
} catch (error) {
if (error.code === "ERR_REDIS_CONNECTION_CLOSED") {
console.error("La connexion au serveur Redis a été fermée");
} else if (error.code === "ERR_REDIS_AUTHENTICATION_FAILED") {
console.error("Échec de l'authentification");
} else {
console.error("Erreur inattendue :", error);
}
}Codes d'erreur courants :
ERR_REDIS_CONNECTION_CLOSED- La connexion au serveur a été ferméeERR_REDIS_AUTHENTICATION_FAILED- Échec de l'authentification avec le serveurERR_REDIS_INVALID_RESPONSE- Réponse invalide reçue du serveur
Exemples de cas d'utilisation
Mise en cache
async function getUserWithCache(userId) {
const cacheKey = `user:${userId}`;
// Essayer de récupérer depuis le cache d'abord
const cachedUser = await redis.get(cacheKey);
if (cachedUser) {
return JSON.parse(cachedUser);
}
// Pas dans le cache, récupérer depuis la base de données
const user = await database.getUser(userId);
// Stocker dans le cache pendant 1 heure
await redis.set(cacheKey, JSON.stringify(user));
await redis.expire(cacheKey, 3600);
return user;
}Limitation de débit
async function rateLimit(ip, limit = 100, windowSecs = 3600) {
const key = `ratelimit:${ip}`;
// Incrémenter le compteur
const count = await redis.incr(key);
// Définir l'expiration si c'est la première requête dans la fenêtre
if (count === 1) {
await redis.expire(key, windowSecs);
}
// Vérifier si la limite est dépassée
return {
limited: count > limit,
remaining: Math.max(0, limit - count),
};
}Stockage de session
async function createSession(userId, data) {
const sessionId = crypto.randomUUID();
const key = `session:${sessionId}`;
// Stocker la session avec expiration
await redis.hmset(key, ["userId", userId.toString(), "created", Date.now().toString(), "data", JSON.stringify(data)]);
await redis.expire(key, 86400); // 24 heures
return sessionId;
}
async function getSession(sessionId) {
const key = `session:${sessionId}`;
// Récupérer les données de session
const exists = await redis.exists(key);
if (!exists) return null;
const [userId, created, data] = await redis.hmget(key, ["userId", "created", "data"]);
return {
userId: Number(userId),
created: Number(created),
data: JSON.parse(data),
};
}Notes d'implémentation
Le client Redis de Bun est implémenté en Zig et utilise le protocole de sérialisation Redis (RESP3). Il gère les connexions efficacement et fournit une reconnexion automatique avec backoff exponentiel.
Le client prend en charge le pipelining des commandes, ce qui signifie que plusieurs commandes peuvent être envoyées sans attendre les réponses des commandes précédentes. Cela améliore considérablement les performances lors de l'envoi de plusieurs commandes successives.
Limitations et plans futurs
Limitations actuelles du client Redis que nous prévoyons de corriger dans les versions futures :
- Les transactions (MULTI/EXEC) doivent être effectuées via des commandes brutes pour l'instant
Fonctionnalités non prises en charge :
- Redis Sentinel
- Redis Cluster