Skip to content
This repository was archived by the owner on Mar 1, 2026. It is now read-only.

Latest commit

 

History

History
201 lines (160 loc) · 5.85 KB

File metadata and controls

201 lines (160 loc) · 5.85 KB

プロセス復旧とデータ永続化要件の分析

🔍 プロセス再起動時の問題

消失するデータ(メモリ内)

// ❌ 再起動で失われるもの
- setTimeout/setInterval のタイマー
- WebSocket接続状態
- 進行中のPromise/コールバック
- メモリキャッシュ
- イベントリスナーの登録状態

残存するデータ(Redis)

// ✅ 再起動後も残るもの
- セッション基本情報
- Discord Thread ID  Devin Session ID マッピング
- セッション状態(ただし不正確な可能性)
- ユーザー情報

📋 復旧戦略の比較

戦略 Redis ログ 追加DB 復旧速度 実装複雑度 推奨度
起動時復旧 - - ⭐⭐⭐⭐⭐
定期チェック - - ⭐⭐⭐⭐
手動復旧 - - ⭐⭐⭐
完全永続化 ⭐⭐

🎯 推奨アプローチ:Redis + 起動時復旧

実装例

class DevinDiscordBot {
  async startup(): Promise<void> {
    console.log('🚀 Bot starting up...');
    
    // 1. Discord 接続
    await this.connectDiscord();
    
    // 2. Redis 接続
    await this.connectRedis();
    
    // 3. セッション復旧
    await this.recoverSessions();
    
    console.log('✅ Bot ready!');
  }
  
  private async recoverSessions(): Promise<void> {
    const sessionKeys = await this.redis.keys('sessions:*');
    console.log(`🔄 Found ${sessionKeys.length} sessions to check`);
    
    const recoveryTasks = sessionKeys.map(key => this.recoverSession(key));
    await Promise.allSettled(recoveryTasks);
  }
  
  private async recoverSession(sessionKey: string): Promise<void> {
    try {
      const sessionData = await this.redis.get(sessionKey);
      if (!sessionData) return;
      
      const session: SessionData = JSON.parse(sessionData);
      
      // 実行中のセッションのみ処理
      if (session.status !== 'running') return;
      
      console.log(`🔍 Checking session: ${session.id}`);
      
      // Devin API で現在の状態を確認
      const devinStatus = await this.devinClient.getStatus(
        session.devinSessionId
      );
      
      if (devinStatus.completed) {
        await this.handleCompletion(session, devinStatus.result);
      } else if (devinStatus.failed) {
        await this.handleError(session, devinStatus.error);
      } else {
        // まだ実行中 → ポーリング再開
        await this.resumePolling(session);
      }
      
    } catch (error) {
      console.error(`❌ Failed to recover session ${sessionKey}:`, error);
    }
  }
  
  private async resumePolling(session: SessionData): Promise<void> {
    // Discord に復旧通知
    const thread = this.discord.channels.cache.get(session.discordThreadId);
    await thread?.send({
      embeds: [{
        color: 0x00ff00,
        title: '🔄 監視を再開しました',
        description: 'Botが再起動したため、セッションの監視を再開します。',
        timestamp: new Date().toISOString()
      }]
    });
    
    // ポーリング開始
    this.startPolling(session.id);
  }
}

Redis データ構造の改善

interface SessionData {
  id: string;
  discordThreadId: string;
  devinSessionId: string;
  userId: string;
  status: SessionStatus;
  createdAt: number;
  updatedAt: number;        // ← 追加:最終更新時刻
  lastPolled: number;       // ← 追加:最終ポーリング時刻
  taskDescription: string;
  metadata?: {
    retryCount?: number;    // ← 追加:リトライ回数
    errorHistory?: string[]; // ← 追加:エラー履歴
  };
}

// 孤児セッション検出用のハートビート
interface HeartbeatData {
  sessionId: string;
  processId: string;        // プロセス識別子
  lastHeartbeat: number;
}

⚖️ Redis単体で十分な理由

✅ 十分な点

  1. セッション状態の保持: 基本情報は保存される
  2. DevinAPI連携: セッションIDがあれば状態確認可能
  3. Discord連携: ThreadIDがあれば通知送信可能
  4. 自動復旧: 起動時に状態同期できる

⚠️ 制限事項

  1. 詳細ログなし: エラーの詳細分析は困難
  2. 長期統計なし: 使用状況の分析不可
  3. 完全性保証なし: Redis障害時はデータ消失

🎯 対策

// 最低限のログ出力でカバー
class SimpleLogger {
  static logSessionEvent(event: string, sessionId: string, data?: any): void {
    const logEntry = {
      timestamp: new Date().toISOString(),
      event,
      sessionId,
      data
    };
    
    // 標準出力(Docker ログで収集可能)
    console.log(JSON.stringify(logEntry));
  }
}

// 使用例
SimpleLogger.logSessionEvent('session.created', sessionId, { task });
SimpleLogger.logSessionEvent('session.completed', sessionId, { duration });
SimpleLogger.logSessionEvent('process.recovered', sessionId, { devinStatus });

🚀 実装ステップ

Phase 1: 基本復旧機能

  1. Redis セッション管理
  2. 起動時のセッション復旧
  3. 基本的なエラーハンドリング

Phase 2: 強化機能

  1. ハートビート機能
  2. 孤児セッション検出
  3. 詳細ログ出力

Phase 3: 監視・アラート

  1. メトリクス収集
  2. アラート設定
  3. ダッシュボード構築

💡 結論

Redisとログだけで十分です。

  • 起動時復旧でセッション連続性を保証
  • DevinAPIで状態同期可能
  • シンプルなログでトラブルシューティング対応
  • 必要に応じて後から PostgreSQL 追加可能

追加の永続化は、運用してみて実際に必要性を感じてからでも遅くありません。