Fix worker ID mapping - use database ID for command routing

Problem:
- Workers generate random UUID on startup (runtime ID)
- Database stores workers with persistent IDs (database ID)
- UI sends commands using database ID
- Server couldn't find worker connection (stored by runtime ID)
- Result: 400 Bad Request "Worker not connected"

Solution:
- When worker connects, look up database ID by worker name
- Store WebSocket connection in Map using BOTH IDs:
  * Runtime ID (from worker_connect message)
  * Database ID (from database lookup by name)
- Commands from UI use database ID → finds correct WebSocket
- Cleanup both IDs when worker disconnects

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
2026-01-07 22:29:23 -05:00
parent 8bda9672d6
commit e13fe9d22f

View File

@@ -179,21 +179,40 @@ wss.on('connection', (ws) => {
const { worker_id, worker_name } = message; const { worker_id, worker_name } = message;
console.log(`Worker connected: ${worker_name} (${worker_id})`); console.log(`Worker connected: ${worker_name} (${worker_id})`);
// Store worker WebSocket connection // Find the database worker ID by name
workers.set(worker_id, ws); const [dbWorkers] = await pool.query(
'SELECT id FROM workers WHERE name = ?',
[worker_name]
);
if (dbWorkers.length > 0) {
const dbWorkerId = dbWorkers[0].id;
// Store worker WebSocket connection using BOTH IDs
workers.set(worker_id, ws); // Runtime ID
workers.set(dbWorkerId, ws); // Database ID
// Store mapping for cleanup
ws.workerId = worker_id;
ws.dbWorkerId = dbWorkerId;
console.log(`Mapped worker: runtime_id=${worker_id}, db_id=${dbWorkerId}, name=${worker_name}`);
// Update worker status to online // Update worker status to online
await pool.query( await pool.query(
`UPDATE workers SET status='online', last_heartbeat=NOW() WHERE id=?`, `UPDATE workers SET status='online', last_heartbeat=NOW() WHERE id=?`,
[worker_id] [dbWorkerId]
); );
// Broadcast worker status update // Broadcast worker status update with database ID
broadcast({ broadcast({
type: 'worker_update', type: 'worker_update',
worker_id: worker_id, worker_id: dbWorkerId,
status: 'online' status: 'online'
}); });
} else {
console.log(`Worker ${worker_name} not found in database, will be created on heartbeat`);
}
} }
if (message.type === 'pong') { if (message.type === 'pong') {
@@ -212,13 +231,14 @@ wss.on('connection', (ws) => {
ws.on('close', () => { ws.on('close', () => {
clients.delete(ws); clients.delete(ws);
// Remove worker from workers map when disconnected // Remove worker from workers map when disconnected (both runtime and db IDs)
for (const [workerId, workerWs] of workers.entries()) { if (ws.workerId) {
if (workerWs === ws) { workers.delete(ws.workerId);
workers.delete(workerId); console.log(`Worker ${ws.workerId} (runtime ID) disconnected`);
console.log(`Worker ${workerId} disconnected`);
break;
} }
if (ws.dbWorkerId) {
workers.delete(ws.dbWorkerId);
console.log(`Worker ${ws.dbWorkerId} (database ID) disconnected`);
} }
}); });
}); });