Node.js Redis接続シングルトンパターン + 自動再接続戦略

問題

複数のモジュールがそれぞれcreateClient()を呼び出すと、Redis接続が重複して作成されます。さらに、ネットワーク切断時に再接続ロジックがないと、アプリがそのまま停止してしまいます。

解決方法

import { createClient, RedisClientType } from 'redis';

let redisClient: RedisClientType | null = null;
let isConnecting = false;

export async function getRedisClient(): Promise<RedisClientType> {
  // すでに接続されていればそのまま返す
  if (redisClient && redisClient.isOpen) {
    return redisClient;
  }

  // 他の箇所で接続中なら待機
  if (isConnecting) {
    while (isConnecting) {
      await new Promise(resolve => setTimeout(resolve, 100));
    }
    if (redisClient && redisClient.isOpen) return redisClient;
  }

  isConnecting = true;
  try {
    redisClient = createClient({
      url: process.env.REDIS_URL || 'redis://127.0.0.1:6379',
      socket: {
        reconnectStrategy: (retries) => {
          if (retries > 10) return new Error('Max retries reached');
          return Math.min(retries * 100, 3000); // 指数バックオフ
        }
      }
    });
    await redisClient.connect();
    return redisClient;
  } finally {
    isConnecting = false;
  }
}

ポイント

  • isConnectingフラグにより、複数箇所から同時にconnect()が呼ばれるのを防ぎます。これがないと、サーバー起動時の同時リクエストで接続が重複して作成されます。
  • reconnectStrategyではretries * 100で100ms、200ms、300ms…と増加し、Math.minで最大3秒まで待機します。10回失敗したら諦めます。
  • finallyブロックでisConnecting = falseにするのが重要です。接続失敗時にもフラグが解除されないと、次の試行ができなくなります。