feat: WorkerHub Federation - Verteilte Worker-Pools mit Prioritäten #263

Closed
opened 2026-01-25 11:26:15 +00:00 by jack · 2 comments
Owner

Übersicht

Einführung einer WorkerHub Federation - mehrere WorkerHubs können sich beim Backend registrieren, eigene Worker verwalten und unterschiedliche Prioritäten haben.

Aktueller Stand

Bestehende Architektur

┌─────────────────────────────────────────────────────────┐
│                      Backend                             │
│  ┌──────────────┐  ┌──────────────┐  ┌──────────────┐  │
│  │  WorkerHub   │  │TaskDispatcher│  │ ProcessMgr   │  │
│  │  (WebSocket) │◄─┤  (Polling)   │  │  (Spawning)  │  │
│  └──────┬───────┘  └──────────────┘  └──────────────┘  │
└─────────┼───────────────────────────────────────────────┘
          │ WebSocket /ws
          ▼
    ┌─────────────┐
    │   Workers   │  (direkt verbunden)
    └─────────────┘

Komponenten:

  • WorkerHub - WebSocket-Server für Worker-Verbindungen
  • TaskDispatcher - Pollt Tasks, matched zu Workern
  • WorkerProcessManager - Spawnt lokale Worker-Prozesse
  • WorkerService - Worker-Client (verbindet zu Backend)

Task-Assignment:

  • Capability-basiert (primary + fallback)
  • First-available Worker Selection (keine Priorisierung)
  • Priority-Feld existiert, wird aber nicht enforced

Vorgeschlagene Architektur: WorkerHub Federation

┌─────────────────────────────────────────────────────────────────┐
│                         Backend                                  │
│  ┌──────────────┐  ┌──────────────┐  ┌──────────────────────┐  │
│  │  HubRegistry │  │TaskDispatcher│  │  FederatedRouter     │  │
│  │  (WebSocket) │◄─┤  (Enhanced)  │◄─┤  (Priority + Weight) │  │
│  └──────┬───────┘  └──────────────┘  └──────────────────────┘  │
└─────────┼───────────────────────────────────────────────────────┘
          │ WebSocket /ws/hub
          │
    ┌─────┴──────────────────────────────────────┐
    │                                            │
    ▼                                            ▼
┌─────────────────────┐                ┌─────────────────────┐
│  WorkerHub (Local)  │                │ WorkerHub (Remote)  │
│  Priority: 100      │                │ Priority: 50        │
│  Weight: 70%        │                │ Weight: 30%         │
│  ┌───────────────┐  │                │  ┌───────────────┐  │
│  │ Worker 1      │  │                │  │ Worker A      │  │
│  │ Worker 2      │  │                │  │ Worker B      │  │
│  │ Worker 3      │  │                │  │ Worker C      │  │
│  └───────────────┘  │                │  └───────────────┘  │
└─────────────────────┘                └─────────────────────┘
       (Docker/NPM)                         (Extern/Cloud)

Konzept

1. WorkerHub als eigenständige Komponente

Ein WorkerHub kann:

  • Lokal laufen (wie aktuell, im Backend-Prozess)
  • Standalone laufen (Docker/NPM, verbindet sich zum Backend)
  • Remote/Extern laufen (eigene Infrastruktur, Cloud)
interface WorkerHubConfig {
  id: string;
  name: string;
  endpoint?: string;        // Für Remote-Hubs
  priority: number;         // Höher = bevorzugt (default: 50)
  weight: number;           // Prozentuale Verteilung (0-100)
  region?: string;          // Geografische Region
  capabilities: string[];   // Unterstützte Capabilities
  maxWorkers: number;       // Max Worker in diesem Hub
  tags: string[];           // Für Routing (z.B. "gpu", "high-memory")
}

2. Hub-Registrierung beim Backend

// WorkerHub → Backend
{
  type: 'hub:register',
  hubId: 'hub-eu-west-1',
  config: {
    name: 'EU West Worker Pool',
    priority: 100,
    weight: 40,
    region: 'eu-west-1',
    capabilities: ['observation', 'summarize', 'claudemd'],
    maxWorkers: 10,
    tags: ['production', 'low-latency']
  }
}

// Backend → WorkerHub
{
  type: 'hub:registered',
  hubId: 'hub-eu-west-1',
  backendVersion: '2.49.6'
}

3. Task-Routing mit Prioritäten

interface TaskRoutingRule {
  // Capability-Match (required)
  capability: string;
  
  // Optional: Prefer specific hubs
  preferHubs?: string[];      // Hub-IDs
  preferTags?: string[];      // z.B. ["gpu", "fast"]
  preferRegion?: string;      // z.B. "eu-west-1"
  
  // Routing-Strategie
  strategy: 'priority' | 'weighted' | 'round-robin' | 'least-loaded';
}

Routing-Strategien:

Strategie Beschreibung
priority Höchste Priorität zuerst, Fallback auf niedrigere
weighted Prozentuale Verteilung nach Weight
round-robin Gleichmäßige Verteilung
least-loaded Hub mit wenigsten aktiven Tasks

4. Failover & Health

interface HubHealth {
  hubId: string;
  status: 'healthy' | 'degraded' | 'unhealthy' | 'offline';
  connectedWorkers: number;
  activeWorkers: number;
  queuedTasks: number;
  avgLatencyMs: number;
  lastHeartbeat: Date;
}

Automatisches Failover:

  • Hub offline → Tasks an andere Hubs routen
  • Hub degraded → Weight reduzieren
  • Hub healthy → Normaler Betrieb

Technische Implementierung

Phase 1: Hub-Registry im Backend

// packages/backend/src/websocket/hub-registry.ts
export class HubRegistry {
  private hubs: Map<string, RegisteredHub> = new Map();
  
  registerHub(config: WorkerHubConfig): void;
  unregisterHub(hubId: string): void;
  getHub(hubId: string): RegisteredHub | undefined;
  getHealthyHubs(): RegisteredHub[];
  getHubsForCapability(cap: string): RegisteredHub[];
}

Phase 2: Standalone WorkerHub Package

// packages/worker-hub/src/standalone-hub.ts
export class StandaloneWorkerHub {
  private backendConnection: WebSocket;
  private localWorkers: Map<string, Worker> = new Map();
  
  constructor(config: WorkerHubConfig) {}
  
  // Verbindung zum Backend
  connectToBackend(url: string): Promise<void>;
  
  // Lokale Worker verwalten
  spawnWorker(options: WorkerOptions): Promise<string>;
  terminateWorker(workerId: string): Promise<void>;
  
  // Task-Handling
  onTaskAssigned(handler: TaskHandler): void;
  reportTaskComplete(taskId: string, result: any): void;
  reportTaskError(taskId: string, error: Error): void;
}

Phase 3: FederatedRouter

// packages/backend/src/websocket/federated-router.ts
export class FederatedRouter {
  constructor(
    private hubRegistry: HubRegistry,
    private taskQueue: TaskQueue
  ) {}
  
  // Finde besten Hub für Task
  routeTask(task: Task): RegisteredHub | null {
    const hubs = this.hubRegistry.getHubsForCapability(task.capability);
    
    switch (task.routingStrategy) {
      case 'priority':
        return this.routeByPriority(hubs);
      case 'weighted':
        return this.routeByWeight(hubs);
      case 'least-loaded':
        return this.routeByLoad(hubs);
      default:
        return this.routeByPriority(hubs);
    }
  }
  
  private routeByPriority(hubs: RegisteredHub[]): RegisteredHub | null {
    return hubs
      .filter(h => h.health.status !== 'offline')
      .sort((a, b) => b.config.priority - a.config.priority)[0];
  }
  
  private routeByWeight(hubs: RegisteredHub[]): RegisteredHub | null {
    const healthyHubs = hubs.filter(h => h.health.status === 'healthy');
    const totalWeight = healthyHubs.reduce((sum, h) => sum + h.config.weight, 0);
    const random = Math.random() * totalWeight;
    
    let cumulative = 0;
    for (const hub of healthyHubs) {
      cumulative += hub.config.weight;
      if (random <= cumulative) return hub;
    }
    return healthyHubs[0];
  }
}

Phase 4: CLI & Distribution

# Als NPM Package
npm install -g @claude-mem/worker-hub
claude-mem-hub start \
  --name "EU Production" \
  --backend wss://api.example.com/ws/hub \
  --priority 100 \
  --workers 5

# Als Docker
docker run -d \
  -e BACKEND_URL=wss://api.example.com/ws/hub \
  -e HUB_NAME="Cloud Workers" \
  -e HUB_PRIORITY=50 \
  -e MAX_WORKERS=10 \
  claude-mem/worker-hub

UI-Anforderungen

Worker-Übersicht mit Hubs

┌─────────────────────────────────────────────────────────────────┐
│  Workers                                         [+ Hub] [⟳]   │
├─────────────────────────────────────────────────────────────────┤
│                                                                 │
│  📊 Overview                                                    │
│  ├─ Total Hubs: 3 (2 healthy, 1 degraded)                      │
│  ├─ Total Workers: 12 (8 active, 4 idle)                       │
│  └─ Tasks in Queue: 47                                          │
│                                                                 │
├─────────────────────────────────────────────────────────────────┤
│                                                                 │
│  🏠 Local Hub                    Priority: 100 │ Weight: 60%   │
│  │  Status: ✅ Healthy           Latency: 2ms                   │
│  │  Workers: 4/5 active                                         │
│  │  ├─ worker-1  🔄 processing  (observation)                  │
│  │  ├─ worker-2  🔄 processing  (summarize)                    │
│  │  ├─ worker-3  🔄 processing  (claudemd)                     │
│  │  ├─ worker-4  🔄 processing  (observation)                  │
│  │  └─ worker-5  💤 idle                                       │
│  │                                                              │
│  🌐 EU West Hub                  Priority: 80  │ Weight: 30%   │
│  │  Status: ✅ Healthy           Latency: 45ms                  │
│  │  Workers: 3/10 active                                        │
│  │  ├─ eu-worker-1  🔄 processing  (observation)               │
│  │  ├─ eu-worker-2  💤 idle                                    │
│  │  └─ ... +8 more                                              │
│  │                                                              │
│  ☁️ Cloud Burst Hub              Priority: 30  │ Weight: 10%   │
│     Status: ⚠️ Degraded          Latency: 120ms                 │
│     Workers: 1/20 active                                        │
│     └─ cloud-worker-1  💤 idle                                 │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘

Hub-Detail-Ansicht

┌─────────────────────────────────────────────────────────────────┐
│  EU West Hub                                      [Edit] [⏹]   │
├─────────────────────────────────────────────────────────────────┤
│                                                                 │
│  Configuration                                                  │
│  ├─ ID: hub-eu-west-1                                          │
│  ├─ Endpoint: wss://eu-west.workers.example.com                │
│  ├─ Region: eu-west-1                                          │
│  ├─ Priority: 80                    [━━━━━━━━░░] 80/100        │
│  ├─ Weight: 30%                     [━━━░░░░░░░] 30%           │
│  └─ Tags: production, gpu-enabled                              │
│                                                                 │
│  Capabilities                                                   │
│  ├─ ✅ observation                                              │
│  ├─ ✅ summarize                                                │
│  ├─ ✅ claudemd                                                 │
│  ├─ ✅ embedding (gpu-accelerated)                              │
│  └─ ❌ semantic-search                                          │
│                                                                 │
│  Health Metrics                                                 │
│  ├─ Status: ✅ Healthy                                          │
│  ├─ Uptime: 14d 3h 22m                                         │
│  ├─ Avg Latency: 45ms                                          │
│  ├─ Tasks Completed (24h): 1,247                               │
│  └─ Error Rate: 0.3%                                           │
│                                                                 │
│  Workers (3/10)                              [+ Spawn Worker]   │
│  ┌──────────────┬──────────┬──────────┬──────────────────────┐ │
│  │ ID           │ Status   │ Tasks    │ Capabilities         │ │
│  ├──────────────┼──────────┼──────────┼──────────────────────┤ │
│  │ eu-worker-1  │ 🔄 busy  │ 47       │ observation, embed   │ │
│  │ eu-worker-2  │ 💤 idle  │ 23       │ summarize, claudemd  │ │
│  │ eu-worker-3  │ 💤 idle  │ 31       │ observation          │ │
│  └──────────────┴──────────┴──────────┴──────────────────────┘ │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘

Best Practices aus der Recherche

Distributed Task Queue Patterns

Pattern Beschreibung Anwendung
Pull Model Worker holen sich Tasks selbst Verhindert Überlastung
Priority Queues Kritische Jobs zuerst Wichtige Tasks priorisieren
Weighted Routing Prozentuale Verteilung Canary Deployments, Load Balancing
Locality-Aware Nähe bevorzugen Latenz minimieren
Checkpointing Fortschritt speichern Long-Running Tasks absichern

Quellen:

Multi-Region Best Practices

Aspekt Empfehlung
Failover Automatisch bei Hub-Ausfall
Health Checks Heartbeat + Latency Monitoring
Weight Adjustment Dynamisch bei Degradation
Regional Routing Locality-Aware für niedrige Latenz

Quellen:


Implementierungs-Phasen

Phase 1: Hub-Registry & Protocol

  • Hub-Registry Klasse im Backend
  • WebSocket Endpoint /ws/hub für Hub-Verbindungen
  • Hub Registration/Heartbeat Protocol
  • Health Monitoring

Phase 2: Standalone WorkerHub Package

  • @claude-mem/worker-hub Package erstellen
  • CLI Entry Point (claude-mem-hub)
  • Docker Image
  • Dokumentation

Phase 3: Federated Router

  • Routing-Strategien implementieren
  • Priority-basiertes Routing
  • Weighted Routing
  • Failover-Logik

Phase 4: UI-Anpassungen

  • Hub-Übersicht in Worker-View
  • Hub-Detail-Ansicht
  • Priority/Weight Visualisierung
  • Health Status Anzeige

Phase 5: Advanced Features

  • Regional Routing
  • Tag-basiertes Routing
  • Dynamic Weight Adjustment
  • Hub Auto-Discovery

Offene Fragen

  1. Sollen Hubs auch Tasks untereinander weiterleiten können (Mesh)?
  2. Wie granular soll die Priority-Konfiguration sein (Hub-Level vs. Worker-Level)?
  3. Brauchen wir eine Hub-zu-Hub Kommunikation oder nur Hub-zu-Backend?
  4. Wie wird Authentication für externe Hubs gehandhabt?

Verwandte Issues

  • #261 - NPM-Package Distribution
  • #254 - Worker-Konfigurations-Modal
  • #256 - Auto-Spawning überprüfen
## Übersicht Einführung einer **WorkerHub Federation** - mehrere WorkerHubs können sich beim Backend registrieren, eigene Worker verwalten und unterschiedliche Prioritäten haben. ## Aktueller Stand ### Bestehende Architektur ``` ┌─────────────────────────────────────────────────────────┐ │ Backend │ │ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │ │ │ WorkerHub │ │TaskDispatcher│ │ ProcessMgr │ │ │ │ (WebSocket) │◄─┤ (Polling) │ │ (Spawning) │ │ │ └──────┬───────┘ └──────────────┘ └──────────────┘ │ └─────────┼───────────────────────────────────────────────┘ │ WebSocket /ws ▼ ┌─────────────┐ │ Workers │ (direkt verbunden) └─────────────┘ ``` **Komponenten:** - `WorkerHub` - WebSocket-Server für Worker-Verbindungen - `TaskDispatcher` - Pollt Tasks, matched zu Workern - `WorkerProcessManager` - Spawnt lokale Worker-Prozesse - `WorkerService` - Worker-Client (verbindet zu Backend) **Task-Assignment:** - Capability-basiert (primary + fallback) - First-available Worker Selection (keine Priorisierung) - Priority-Feld existiert, wird aber nicht enforced --- ## Vorgeschlagene Architektur: WorkerHub Federation ``` ┌─────────────────────────────────────────────────────────────────┐ │ Backend │ │ ┌──────────────┐ ┌──────────────┐ ┌──────────────────────┐ │ │ │ HubRegistry │ │TaskDispatcher│ │ FederatedRouter │ │ │ │ (WebSocket) │◄─┤ (Enhanced) │◄─┤ (Priority + Weight) │ │ │ └──────┬───────┘ └──────────────┘ └──────────────────────┘ │ └─────────┼───────────────────────────────────────────────────────┘ │ WebSocket /ws/hub │ ┌─────┴──────────────────────────────────────┐ │ │ ▼ ▼ ┌─────────────────────┐ ┌─────────────────────┐ │ WorkerHub (Local) │ │ WorkerHub (Remote) │ │ Priority: 100 │ │ Priority: 50 │ │ Weight: 70% │ │ Weight: 30% │ │ ┌───────────────┐ │ │ ┌───────────────┐ │ │ │ Worker 1 │ │ │ │ Worker A │ │ │ │ Worker 2 │ │ │ │ Worker B │ │ │ │ Worker 3 │ │ │ │ Worker C │ │ │ └───────────────┘ │ │ └───────────────┘ │ └─────────────────────┘ └─────────────────────┘ (Docker/NPM) (Extern/Cloud) ``` --- ## Konzept ### 1. WorkerHub als eigenständige Komponente Ein WorkerHub kann: - **Lokal** laufen (wie aktuell, im Backend-Prozess) - **Standalone** laufen (Docker/NPM, verbindet sich zum Backend) - **Remote/Extern** laufen (eigene Infrastruktur, Cloud) ```typescript interface WorkerHubConfig { id: string; name: string; endpoint?: string; // Für Remote-Hubs priority: number; // Höher = bevorzugt (default: 50) weight: number; // Prozentuale Verteilung (0-100) region?: string; // Geografische Region capabilities: string[]; // Unterstützte Capabilities maxWorkers: number; // Max Worker in diesem Hub tags: string[]; // Für Routing (z.B. "gpu", "high-memory") } ``` ### 2. Hub-Registrierung beim Backend ```typescript // WorkerHub → Backend { type: 'hub:register', hubId: 'hub-eu-west-1', config: { name: 'EU West Worker Pool', priority: 100, weight: 40, region: 'eu-west-1', capabilities: ['observation', 'summarize', 'claudemd'], maxWorkers: 10, tags: ['production', 'low-latency'] } } // Backend → WorkerHub { type: 'hub:registered', hubId: 'hub-eu-west-1', backendVersion: '2.49.6' } ``` ### 3. Task-Routing mit Prioritäten ```typescript interface TaskRoutingRule { // Capability-Match (required) capability: string; // Optional: Prefer specific hubs preferHubs?: string[]; // Hub-IDs preferTags?: string[]; // z.B. ["gpu", "fast"] preferRegion?: string; // z.B. "eu-west-1" // Routing-Strategie strategy: 'priority' | 'weighted' | 'round-robin' | 'least-loaded'; } ``` **Routing-Strategien:** | Strategie | Beschreibung | |-----------|--------------| | `priority` | Höchste Priorität zuerst, Fallback auf niedrigere | | `weighted` | Prozentuale Verteilung nach Weight | | `round-robin` | Gleichmäßige Verteilung | | `least-loaded` | Hub mit wenigsten aktiven Tasks | ### 4. Failover & Health ```typescript interface HubHealth { hubId: string; status: 'healthy' | 'degraded' | 'unhealthy' | 'offline'; connectedWorkers: number; activeWorkers: number; queuedTasks: number; avgLatencyMs: number; lastHeartbeat: Date; } ``` **Automatisches Failover:** - Hub offline → Tasks an andere Hubs routen - Hub degraded → Weight reduzieren - Hub healthy → Normaler Betrieb --- ## Technische Implementierung ### Phase 1: Hub-Registry im Backend ```typescript // packages/backend/src/websocket/hub-registry.ts export class HubRegistry { private hubs: Map<string, RegisteredHub> = new Map(); registerHub(config: WorkerHubConfig): void; unregisterHub(hubId: string): void; getHub(hubId: string): RegisteredHub | undefined; getHealthyHubs(): RegisteredHub[]; getHubsForCapability(cap: string): RegisteredHub[]; } ``` ### Phase 2: Standalone WorkerHub Package ```typescript // packages/worker-hub/src/standalone-hub.ts export class StandaloneWorkerHub { private backendConnection: WebSocket; private localWorkers: Map<string, Worker> = new Map(); constructor(config: WorkerHubConfig) {} // Verbindung zum Backend connectToBackend(url: string): Promise<void>; // Lokale Worker verwalten spawnWorker(options: WorkerOptions): Promise<string>; terminateWorker(workerId: string): Promise<void>; // Task-Handling onTaskAssigned(handler: TaskHandler): void; reportTaskComplete(taskId: string, result: any): void; reportTaskError(taskId: string, error: Error): void; } ``` ### Phase 3: FederatedRouter ```typescript // packages/backend/src/websocket/federated-router.ts export class FederatedRouter { constructor( private hubRegistry: HubRegistry, private taskQueue: TaskQueue ) {} // Finde besten Hub für Task routeTask(task: Task): RegisteredHub | null { const hubs = this.hubRegistry.getHubsForCapability(task.capability); switch (task.routingStrategy) { case 'priority': return this.routeByPriority(hubs); case 'weighted': return this.routeByWeight(hubs); case 'least-loaded': return this.routeByLoad(hubs); default: return this.routeByPriority(hubs); } } private routeByPriority(hubs: RegisteredHub[]): RegisteredHub | null { return hubs .filter(h => h.health.status !== 'offline') .sort((a, b) => b.config.priority - a.config.priority)[0]; } private routeByWeight(hubs: RegisteredHub[]): RegisteredHub | null { const healthyHubs = hubs.filter(h => h.health.status === 'healthy'); const totalWeight = healthyHubs.reduce((sum, h) => sum + h.config.weight, 0); const random = Math.random() * totalWeight; let cumulative = 0; for (const hub of healthyHubs) { cumulative += hub.config.weight; if (random <= cumulative) return hub; } return healthyHubs[0]; } } ``` ### Phase 4: CLI & Distribution ```bash # Als NPM Package npm install -g @claude-mem/worker-hub claude-mem-hub start \ --name "EU Production" \ --backend wss://api.example.com/ws/hub \ --priority 100 \ --workers 5 # Als Docker docker run -d \ -e BACKEND_URL=wss://api.example.com/ws/hub \ -e HUB_NAME="Cloud Workers" \ -e HUB_PRIORITY=50 \ -e MAX_WORKERS=10 \ claude-mem/worker-hub ``` --- ## UI-Anforderungen ### Worker-Übersicht mit Hubs ``` ┌─────────────────────────────────────────────────────────────────┐ │ Workers [+ Hub] [⟳] │ ├─────────────────────────────────────────────────────────────────┤ │ │ │ 📊 Overview │ │ ├─ Total Hubs: 3 (2 healthy, 1 degraded) │ │ ├─ Total Workers: 12 (8 active, 4 idle) │ │ └─ Tasks in Queue: 47 │ │ │ ├─────────────────────────────────────────────────────────────────┤ │ │ │ 🏠 Local Hub Priority: 100 │ Weight: 60% │ │ │ Status: ✅ Healthy Latency: 2ms │ │ │ Workers: 4/5 active │ │ │ ├─ worker-1 🔄 processing (observation) │ │ │ ├─ worker-2 🔄 processing (summarize) │ │ │ ├─ worker-3 🔄 processing (claudemd) │ │ │ ├─ worker-4 🔄 processing (observation) │ │ │ └─ worker-5 💤 idle │ │ │ │ │ 🌐 EU West Hub Priority: 80 │ Weight: 30% │ │ │ Status: ✅ Healthy Latency: 45ms │ │ │ Workers: 3/10 active │ │ │ ├─ eu-worker-1 🔄 processing (observation) │ │ │ ├─ eu-worker-2 💤 idle │ │ │ └─ ... +8 more │ │ │ │ │ ☁️ Cloud Burst Hub Priority: 30 │ Weight: 10% │ │ Status: ⚠️ Degraded Latency: 120ms │ │ Workers: 1/20 active │ │ └─ cloud-worker-1 💤 idle │ │ │ └─────────────────────────────────────────────────────────────────┘ ``` ### Hub-Detail-Ansicht ``` ┌─────────────────────────────────────────────────────────────────┐ │ EU West Hub [Edit] [⏹] │ ├─────────────────────────────────────────────────────────────────┤ │ │ │ Configuration │ │ ├─ ID: hub-eu-west-1 │ │ ├─ Endpoint: wss://eu-west.workers.example.com │ │ ├─ Region: eu-west-1 │ │ ├─ Priority: 80 [━━━━━━━━░░] 80/100 │ │ ├─ Weight: 30% [━━━░░░░░░░] 30% │ │ └─ Tags: production, gpu-enabled │ │ │ │ Capabilities │ │ ├─ ✅ observation │ │ ├─ ✅ summarize │ │ ├─ ✅ claudemd │ │ ├─ ✅ embedding (gpu-accelerated) │ │ └─ ❌ semantic-search │ │ │ │ Health Metrics │ │ ├─ Status: ✅ Healthy │ │ ├─ Uptime: 14d 3h 22m │ │ ├─ Avg Latency: 45ms │ │ ├─ Tasks Completed (24h): 1,247 │ │ └─ Error Rate: 0.3% │ │ │ │ Workers (3/10) [+ Spawn Worker] │ │ ┌──────────────┬──────────┬──────────┬──────────────────────┐ │ │ │ ID │ Status │ Tasks │ Capabilities │ │ │ ├──────────────┼──────────┼──────────┼──────────────────────┤ │ │ │ eu-worker-1 │ 🔄 busy │ 47 │ observation, embed │ │ │ │ eu-worker-2 │ 💤 idle │ 23 │ summarize, claudemd │ │ │ │ eu-worker-3 │ 💤 idle │ 31 │ observation │ │ │ └──────────────┴──────────┴──────────┴──────────────────────┘ │ │ │ └─────────────────────────────────────────────────────────────────┘ ``` --- ## Best Practices aus der Recherche ### Distributed Task Queue Patterns | Pattern | Beschreibung | Anwendung | |---------|--------------|-----------| | **Pull Model** | Worker holen sich Tasks selbst | Verhindert Überlastung | | **Priority Queues** | Kritische Jobs zuerst | Wichtige Tasks priorisieren | | **Weighted Routing** | Prozentuale Verteilung | Canary Deployments, Load Balancing | | **Locality-Aware** | Nähe bevorzugen | Latenz minimieren | | **Checkpointing** | Fortschritt speichern | Long-Running Tasks absichern | **Quellen:** - [Meta FOQS: Scaling Distributed Priority Queue](https://engineering.fb.com/2021/02/22/production-engineering/foqs-scaling-a-distributed-priority-queue/) - [Distributed Task Queue Patterns](https://www.geeksforgeeks.org/system-design/distributed-task-queue-distributed-systems/) - [BullMQ Documentation](https://docs.bullmq.io/) ### Multi-Region Best Practices | Aspekt | Empfehlung | |--------|------------| | **Failover** | Automatisch bei Hub-Ausfall | | **Health Checks** | Heartbeat + Latency Monitoring | | **Weight Adjustment** | Dynamisch bei Degradation | | **Regional Routing** | Locality-Aware für niedrige Latenz | **Quellen:** - [Azure AKS Multi-Region Deployment](https://learn.microsoft.com/en-us/azure/aks/reliability-multi-region-deployment-models) - [Istio Locality-Aware Routing](https://oneuptime.com/blog/post/2026-01-07-istio-locality-aware-routing/view) --- ## Implementierungs-Phasen ### Phase 1: Hub-Registry & Protocol - [ ] Hub-Registry Klasse im Backend - [ ] WebSocket Endpoint `/ws/hub` für Hub-Verbindungen - [ ] Hub Registration/Heartbeat Protocol - [ ] Health Monitoring ### Phase 2: Standalone WorkerHub Package - [ ] `@claude-mem/worker-hub` Package erstellen - [ ] CLI Entry Point (`claude-mem-hub`) - [ ] Docker Image - [ ] Dokumentation ### Phase 3: Federated Router - [ ] Routing-Strategien implementieren - [ ] Priority-basiertes Routing - [ ] Weighted Routing - [ ] Failover-Logik ### Phase 4: UI-Anpassungen - [ ] Hub-Übersicht in Worker-View - [ ] Hub-Detail-Ansicht - [ ] Priority/Weight Visualisierung - [ ] Health Status Anzeige ### Phase 5: Advanced Features - [ ] Regional Routing - [ ] Tag-basiertes Routing - [ ] Dynamic Weight Adjustment - [ ] Hub Auto-Discovery --- ## Offene Fragen 1. Sollen Hubs auch Tasks untereinander weiterleiten können (Mesh)? 2. Wie granular soll die Priority-Konfiguration sein (Hub-Level vs. Worker-Level)? 3. Brauchen wir eine Hub-zu-Hub Kommunikation oder nur Hub-zu-Backend? 4. Wie wird Authentication für externe Hubs gehandhabt? --- ## Verwandte Issues - #261 - NPM-Package Distribution - #254 - Worker-Konfigurations-Modal - #256 - Auto-Spawning überprüfen
Author
Owner

Runner-Registration Konzept (GitLab/Forgejo-Style)

Basierend auf den Patterns von GitLab CI Runners und Forgejo Actions Runners:

1. Token-basierte Registrierung

Statt einfacher WebSocket-Verbindung:

interface WorkerRegistration {
  // Auth Token (erstellt in WebUI oder CLI)
  authToken: string;           // z.B. "cmhub-abc123..."
  
  // System-ID (unique per Worker-Instance)
  systemId: string;            // z.B. hostname + pid
  
  // Worker-Metadaten
  capabilities: string[];      // ["mistral", "openai", "gpu"]
  labels: Record<string, string>;
  maxConcurrency: number;
  
  // Optional: Hub-Zuordnung
  targetHub?: string;          // Hub-ID falls mehrere
}

2. Token-Lifecycle

┌─────────────────────────────────────────────────────────────┐
│                     TOKEN MANAGEMENT                        │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  1. Token erstellen (WebUI/CLI)                            │
│     └─> POST /api/worker-tokens                            │
│         { name, capabilities, expiresAt?, hubId? }         │
│         └─> Returns: { token: "cmhub-...", id }            │
│                                                             │
│  2. Worker registriert sich                                │
│     └─> Worker startet mit Token                           │
│         worker-service --token cmhub-abc123                 │
│         └─> WebSocket auth mit Token                       │
│         └─> Mehrere Worker können gleichen Token nutzen    │
│                                                             │
│  3. Token widerrufen                                       │
│     └─> DELETE /api/worker-tokens/:id                      │
│         └─> Alle Worker mit Token werden disconnected      │
│                                                             │
└─────────────────────────────────────────────────────────────┘

3. Worker-Typen (wie GitLab Runner Types)

Typ Scope Use Case
Instance Global Shared Worker für alle Projekte
Group Hub-spezifisch Worker nur für bestimmten Hub
Project Projekt-spezifisch Worker nur für ein Projekt

4. Label-basiertes Routing (wie Forgejo)

// Worker-Labels definieren Capabilities
const workerLabels = {
  "model": "mistral",
  "gpu": "nvidia-rtx4090",
  "region": "eu-central",
  "env": "docker://node:20-bullseye"
};

// Tasks können Labels anfordern
interface Task {
  requiredLabels?: Record<string, string>;
  preferredLabels?: Record<string, string>;
}

5. Offline-Registration (für IaC/Automation)

# Token vorab erstellen (z.B. via Terraform/Ansible)
TOKEN=$(curl -X POST https://hub.example.com/api/worker-tokens \
  -H "Authorization: Bearer $ADMIN_TOKEN" \
  -d '{"name": "production-gpu", "capabilities": ["gpu"]}' \
  | jq -r '.token')

# Worker später mit Token starten
docker run claude-mem-worker \
  --hub wss://hub.example.com/ws \
  --token $TOKEN \
  --labels "region=eu,gpu=true"

6. Datenbank-Schema Erweiterung

CREATE TABLE worker_tokens (
  id TEXT PRIMARY KEY,
  name TEXT NOT NULL,
  token_hash TEXT NOT NULL,      -- bcrypt hash
  token_prefix TEXT NOT NULL,    -- "cmhub-abc" für Anzeige
  scope TEXT DEFAULT 'instance', -- instance/group/project
  hub_id TEXT REFERENCES hubs(id),
  capabilities TEXT,             -- JSON array
  created_at TEXT NOT NULL,
  expires_at TEXT,
  last_used_at TEXT,
  revoked_at TEXT
);

CREATE TABLE worker_registrations (
  id TEXT PRIMARY KEY,
  token_id TEXT REFERENCES worker_tokens(id),
  system_id TEXT NOT NULL,       -- unique per instance
  hostname TEXT,
  labels TEXT,                   -- JSON
  status TEXT DEFAULT 'online',
  connected_at TEXT,
  disconnected_at TEXT,
  last_heartbeat TEXT
);

7. WebUI Token-Management

┌─────────────────────────────────────────────────────────┐
│  Worker Tokens                              [+ New Token]│
├─────────────────────────────────────────────────────────┤
│                                                         │
│  🔑 production-gpu          Scope: Instance             │
│     Token: cmhub-abc...     Created: 2 days ago         │
│     Capabilities: gpu, mistral                          │
│     Workers: 3 online                                   │
│     [View Workers] [Revoke]                             │
│                                                         │
│  🔑 dev-local               Scope: Project              │
│     Token: cmhub-xyz...     Created: 1 hour ago         │
│     Capabilities: openai                                │
│     Workers: 1 online                                   │
│     [View Workers] [Revoke]                             │
│                                                         │
└─────────────────────────────────────────────────────────┘

8. Vorteile dieses Ansatzes

  • Sicherheit: Tokens können jederzeit widerrufen werden
  • Skalierung: Ein Token für beliebig viele Worker-Instanzen
  • Automation: Offline-Registration für CI/CD und IaC
  • Flexibilität: Label-basiertes Routing wie bei CI-Systemen
  • Audit: Nachvollziehbar welcher Worker wann aktiv war
  • Bekanntes Pattern: DevOps-Teams kennen das von GitLab/GitHub/Forgejo
## Runner-Registration Konzept (GitLab/Forgejo-Style) Basierend auf den Patterns von GitLab CI Runners und Forgejo Actions Runners: ### 1. Token-basierte Registrierung **Statt einfacher WebSocket-Verbindung:** ```typescript interface WorkerRegistration { // Auth Token (erstellt in WebUI oder CLI) authToken: string; // z.B. "cmhub-abc123..." // System-ID (unique per Worker-Instance) systemId: string; // z.B. hostname + pid // Worker-Metadaten capabilities: string[]; // ["mistral", "openai", "gpu"] labels: Record<string, string>; maxConcurrency: number; // Optional: Hub-Zuordnung targetHub?: string; // Hub-ID falls mehrere } ``` ### 2. Token-Lifecycle ``` ┌─────────────────────────────────────────────────────────────┐ │ TOKEN MANAGEMENT │ ├─────────────────────────────────────────────────────────────┤ │ │ │ 1. Token erstellen (WebUI/CLI) │ │ └─> POST /api/worker-tokens │ │ { name, capabilities, expiresAt?, hubId? } │ │ └─> Returns: { token: "cmhub-...", id } │ │ │ │ 2. Worker registriert sich │ │ └─> Worker startet mit Token │ │ worker-service --token cmhub-abc123 │ │ └─> WebSocket auth mit Token │ │ └─> Mehrere Worker können gleichen Token nutzen │ │ │ │ 3. Token widerrufen │ │ └─> DELETE /api/worker-tokens/:id │ │ └─> Alle Worker mit Token werden disconnected │ │ │ └─────────────────────────────────────────────────────────────┘ ``` ### 3. Worker-Typen (wie GitLab Runner Types) | Typ | Scope | Use Case | |-----|-------|----------| | **Instance** | Global | Shared Worker für alle Projekte | | **Group** | Hub-spezifisch | Worker nur für bestimmten Hub | | **Project** | Projekt-spezifisch | Worker nur für ein Projekt | ### 4. Label-basiertes Routing (wie Forgejo) ```typescript // Worker-Labels definieren Capabilities const workerLabels = { "model": "mistral", "gpu": "nvidia-rtx4090", "region": "eu-central", "env": "docker://node:20-bullseye" }; // Tasks können Labels anfordern interface Task { requiredLabels?: Record<string, string>; preferredLabels?: Record<string, string>; } ``` ### 5. Offline-Registration (für IaC/Automation) ```bash # Token vorab erstellen (z.B. via Terraform/Ansible) TOKEN=$(curl -X POST https://hub.example.com/api/worker-tokens \ -H "Authorization: Bearer $ADMIN_TOKEN" \ -d '{"name": "production-gpu", "capabilities": ["gpu"]}' \ | jq -r '.token') # Worker später mit Token starten docker run claude-mem-worker \ --hub wss://hub.example.com/ws \ --token $TOKEN \ --labels "region=eu,gpu=true" ``` ### 6. Datenbank-Schema Erweiterung ```sql CREATE TABLE worker_tokens ( id TEXT PRIMARY KEY, name TEXT NOT NULL, token_hash TEXT NOT NULL, -- bcrypt hash token_prefix TEXT NOT NULL, -- "cmhub-abc" für Anzeige scope TEXT DEFAULT 'instance', -- instance/group/project hub_id TEXT REFERENCES hubs(id), capabilities TEXT, -- JSON array created_at TEXT NOT NULL, expires_at TEXT, last_used_at TEXT, revoked_at TEXT ); CREATE TABLE worker_registrations ( id TEXT PRIMARY KEY, token_id TEXT REFERENCES worker_tokens(id), system_id TEXT NOT NULL, -- unique per instance hostname TEXT, labels TEXT, -- JSON status TEXT DEFAULT 'online', connected_at TEXT, disconnected_at TEXT, last_heartbeat TEXT ); ``` ### 7. WebUI Token-Management ``` ┌─────────────────────────────────────────────────────────┐ │ Worker Tokens [+ New Token]│ ├─────────────────────────────────────────────────────────┤ │ │ │ 🔑 production-gpu Scope: Instance │ │ Token: cmhub-abc... Created: 2 days ago │ │ Capabilities: gpu, mistral │ │ Workers: 3 online │ │ [View Workers] [Revoke] │ │ │ │ 🔑 dev-local Scope: Project │ │ Token: cmhub-xyz... Created: 1 hour ago │ │ Capabilities: openai │ │ Workers: 1 online │ │ [View Workers] [Revoke] │ │ │ └─────────────────────────────────────────────────────────┘ ``` ### 8. Vorteile dieses Ansatzes - **Sicherheit**: Tokens können jederzeit widerrufen werden - **Skalierung**: Ein Token für beliebig viele Worker-Instanzen - **Automation**: Offline-Registration für CI/CD und IaC - **Flexibilität**: Label-basiertes Routing wie bei CI-Systemen - **Audit**: Nachvollziehbar welcher Worker wann aktiv war - **Bekanntes Pattern**: DevOps-Teams kennen das von GitLab/GitHub/Forgejo
Author
Owner

Unified Architecture: Backend als Built-in Hub

Kernkonzept

Das Backend selbst IST ein Hub. Es gibt nur EINE Worker-Art - alle Worker verbinden sich zu einem Hub. Der Unterschied ist nur, ob das der eingebaute Backend-Hub ist oder ein externer Hub.

┌─────────────────────────────────────────────────────────────────────┐
│                     UNIFIED ARCHITECTURE                            │
├─────────────────────────────────────────────────────────────────────┤
│                                                                     │
│  KLEINES SETUP (Single Node):                                      │
│  ════════════════════════════                                      │
│                                                                     │
│     ┌──────────┐                                                   │
│     │ Worker 1 │──┐     ┌──────────────────────────────────┐       │
│     └──────────┘  │     │           BACKEND                │       │
│     ┌──────────┐  ├────►│  ┌────────────────────────────┐  │       │
│     │ Worker 2 │──┘     │  │     Built-in Hub           │  │       │
│     └──────────┘        │  │   (immer vorhanden)        │  │       │
│                         │  └────────────────────────────┘  │       │
│                         └──────────────────────────────────┘       │
│                                                                     │
│  → Worker verbinden direkt zum Backend                             │
│  → Kein extra Hub nötig                                            │
│  → Perfekt für lokale/kleine Setups                                │
│                                                                     │
├─────────────────────────────────────────────────────────────────────┤
│                                                                     │
│  GROSSES SETUP (Multi-Region/Federation):                          │
│  ════════════════════════════════════════                          │
│                                                                     │
│     ┌──────────┐                                                   │
│     │ Worker 1 │──┐     ┌──────────────────────────────────┐       │
│     └──────────┘  │     │           BACKEND                │       │
│     ┌──────────┐  ├────►│  ┌────────────────────────────┐  │       │
│     │ Worker 2 │──┘     │  │     Built-in Hub           │  │       │
│     └──────────┘        │  │      (Primary)             │  │       │
│                         │  └────────────────────────────┘  │       │
│                         │              ▲                   │       │
│                         │              │ Federation        │       │
│                         └──────────────┼───────────────────┘       │
│                                        │                           │
│                              ┌─────────┴─────────┐                 │
│                              ▼                   ▼                 │
│                         ┌─────────┐         ┌─────────┐            │
│                         │ Hub EU  │         │ Hub US  │            │
│                         └────┬────┘         └────┬────┘            │
│                              │                   │                 │
│                         ┌────┴────┐         ┌────┴────┐            │
│                         ▼         ▼         ▼         ▼            │
│                      Worker    Worker    Worker    Worker          │
│                                                                     │
│  → Worker verbinden zu ihrem lokalen Hub                           │
│  → Hubs föderieren mit Backend (Primary Hub)                       │
│  → Skaliert über Regionen/Netzwerke                                │
│                                                                     │
└─────────────────────────────────────────────────────────────────────┘

Vorteile dieser Architektur

Aspekt Vorteil
Eine Worker-Implementierung Worker-Code bleibt gleich, egal ob direktes Backend oder externer Hub
Keine Migration nötig Kleine Setups starten direkt, können später Hubs hinzufügen
Konsistente API Hub-Protokoll ist überall identisch
Einfaches Deployment Backend = vollständiges System out-of-the-box

Komponenten

// Hub kann im Backend oder standalone laufen
interface HubConfig {
  mode: 'embedded' | 'standalone';
  
  // Embedded: Teil des Backends
  // Standalone: Eigener Prozess, föderiert mit Backend
  
  federation?: {
    primaryHub: string;        // URL zum Backend/Primary Hub
    authToken: string;         // Token für Federation
    syncInterval: number;      // Task-Sync Intervall
  };
}

Deployment-Optionen

# Option 1: Alles-in-einem (Standard)
services:
  backend:
    image: claude-mem-backend
    # Built-in Hub ist automatisch aktiv
    
  worker:
    image: claude-mem-worker
    environment:
      HUB_URL: ws://backend:37777/ws
      
# Option 2: Mit externen Hubs
services:
  backend:
    image: claude-mem-backend
    
  hub-eu:
    image: claude-mem-hub
    environment:
      FEDERATION_PRIMARY: ws://backend:37777/federation
      FEDERATION_TOKEN: ${HUB_EU_TOKEN}
      
  hub-us:
    image: claude-mem-hub
    environment:
      FEDERATION_PRIMARY: ws://backend:37777/federation
      FEDERATION_TOKEN: ${HUB_US_TOKEN}
      
  worker-eu:
    image: claude-mem-worker
    environment:
      HUB_URL: ws://hub-eu:37777/ws
      
  worker-us:
    image: claude-mem-worker
    environment:
      HUB_URL: ws://hub-us:37777/ws

Fazit

  • Worker = Immer gleich, verbindet sich zu einem Hub
  • Hub = Kann embedded (im Backend) oder standalone sein
  • Backend = Hat immer einen Built-in Hub (Primary)
  • Federation = Optional, für Multi-Region/Skalierung
## Unified Architecture: Backend als Built-in Hub ### Kernkonzept Das Backend selbst IST ein Hub. Es gibt nur EINE Worker-Art - alle Worker verbinden sich zu einem Hub. Der Unterschied ist nur, ob das der eingebaute Backend-Hub ist oder ein externer Hub. ``` ┌─────────────────────────────────────────────────────────────────────┐ │ UNIFIED ARCHITECTURE │ ├─────────────────────────────────────────────────────────────────────┤ │ │ │ KLEINES SETUP (Single Node): │ │ ════════════════════════════ │ │ │ │ ┌──────────┐ │ │ │ Worker 1 │──┐ ┌──────────────────────────────────┐ │ │ └──────────┘ │ │ BACKEND │ │ │ ┌──────────┐ ├────►│ ┌────────────────────────────┐ │ │ │ │ Worker 2 │──┘ │ │ Built-in Hub │ │ │ │ └──────────┘ │ │ (immer vorhanden) │ │ │ │ │ └────────────────────────────┘ │ │ │ └──────────────────────────────────┘ │ │ │ │ → Worker verbinden direkt zum Backend │ │ → Kein extra Hub nötig │ │ → Perfekt für lokale/kleine Setups │ │ │ ├─────────────────────────────────────────────────────────────────────┤ │ │ │ GROSSES SETUP (Multi-Region/Federation): │ │ ════════════════════════════════════════ │ │ │ │ ┌──────────┐ │ │ │ Worker 1 │──┐ ┌──────────────────────────────────┐ │ │ └──────────┘ │ │ BACKEND │ │ │ ┌──────────┐ ├────►│ ┌────────────────────────────┐ │ │ │ │ Worker 2 │──┘ │ │ Built-in Hub │ │ │ │ └──────────┘ │ │ (Primary) │ │ │ │ │ └────────────────────────────┘ │ │ │ │ ▲ │ │ │ │ │ Federation │ │ │ └──────────────┼───────────────────┘ │ │ │ │ │ ┌─────────┴─────────┐ │ │ ▼ ▼ │ │ ┌─────────┐ ┌─────────┐ │ │ │ Hub EU │ │ Hub US │ │ │ └────┬────┘ └────┬────┘ │ │ │ │ │ │ ┌────┴────┐ ┌────┴────┐ │ │ ▼ ▼ ▼ ▼ │ │ Worker Worker Worker Worker │ │ │ │ → Worker verbinden zu ihrem lokalen Hub │ │ → Hubs föderieren mit Backend (Primary Hub) │ │ → Skaliert über Regionen/Netzwerke │ │ │ └─────────────────────────────────────────────────────────────────────┘ ``` ### Vorteile dieser Architektur | Aspekt | Vorteil | |--------|---------| | **Eine Worker-Implementierung** | Worker-Code bleibt gleich, egal ob direktes Backend oder externer Hub | | **Keine Migration nötig** | Kleine Setups starten direkt, können später Hubs hinzufügen | | **Konsistente API** | Hub-Protokoll ist überall identisch | | **Einfaches Deployment** | Backend = vollständiges System out-of-the-box | ### Komponenten ```typescript // Hub kann im Backend oder standalone laufen interface HubConfig { mode: 'embedded' | 'standalone'; // Embedded: Teil des Backends // Standalone: Eigener Prozess, föderiert mit Backend federation?: { primaryHub: string; // URL zum Backend/Primary Hub authToken: string; // Token für Federation syncInterval: number; // Task-Sync Intervall }; } ``` ### Deployment-Optionen ```yaml # Option 1: Alles-in-einem (Standard) services: backend: image: claude-mem-backend # Built-in Hub ist automatisch aktiv worker: image: claude-mem-worker environment: HUB_URL: ws://backend:37777/ws # Option 2: Mit externen Hubs services: backend: image: claude-mem-backend hub-eu: image: claude-mem-hub environment: FEDERATION_PRIMARY: ws://backend:37777/federation FEDERATION_TOKEN: ${HUB_EU_TOKEN} hub-us: image: claude-mem-hub environment: FEDERATION_PRIMARY: ws://backend:37777/federation FEDERATION_TOKEN: ${HUB_US_TOKEN} worker-eu: image: claude-mem-worker environment: HUB_URL: ws://hub-eu:37777/ws worker-us: image: claude-mem-worker environment: HUB_URL: ws://hub-us:37777/ws ``` ### Fazit - **Worker** = Immer gleich, verbindet sich zu einem Hub - **Hub** = Kann embedded (im Backend) oder standalone sein - **Backend** = Hat immer einen Built-in Hub (Primary) - **Federation** = Optional, für Multi-Region/Skalierung
jack closed this issue 2026-01-25 19:58:50 +00:00
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Reference
customable/claude-mem#263
No description provided.