fix: resolve race condition entre Kernel e Casa de Máquinas

- Move início do Kernel ANTES do servidor HTTP (porta 5000)
- Adiciona retry com exponential backoff na consulta ao Registry
- Garante que Registry esteja pronto antes de receber requisições

Fixes: Casa de Máquinas mostrava 0/0 serviços porque API
chamava Registry antes do Kernel iniciar na porta 5001
This commit is contained in:
Jonas Pacheco 2026-04-08 12:03:55 -03:00
parent ea58a45ea1
commit 71e2b654bd
2 changed files with 38 additions and 21 deletions

View File

@ -10,7 +10,23 @@ import http from "http";
// Consulta Registry do Kernel para serviços descobertos // Consulta Registry do Kernel para serviços descobertos
// Usa http.get em vez de fetch por causa de problemas com loopback no Node.js // Usa http.get em vez de fetch por causa de problemas com loopback no Node.js
async function fetchRegistryServices(): Promise<any[] | null> { // Implementa retry com backoff para maior resiliência
async function fetchRegistryServicesWithRetry(maxRetries = 3, delay = 500): Promise<any[] | null> {
for (let attempt = 1; attempt <= maxRetries; attempt++) {
const result = await fetchRegistryServicesOnce();
if (result !== null) {
return result;
}
if (attempt < maxRetries) {
console.log(`[Engine Room] Tentativa ${attempt} falhou, aguardando ${delay}ms...`);
await new Promise(r => setTimeout(r, delay));
delay *= 2; // Exponential backoff
}
}
return null;
}
async function fetchRegistryServicesOnce(): Promise<any[] | null> {
return new Promise((resolve) => { return new Promise((resolve) => {
const options = { const options = {
hostname: '127.0.0.1', hostname: '127.0.0.1',
@ -90,7 +106,7 @@ export function registerEngineRoomRoutes(app: Express): void {
return res.status(401).json({ error: "Not authenticated" }); return res.status(401).json({ error: "Not authenticated" });
} }
const registryServices = await fetchRegistryServices(); const registryServices = await fetchRegistryServicesWithRetry();
const engines: any[] = []; const engines: any[] = [];
if (registryServices && registryServices.length > 0) { if (registryServices && registryServices.length > 0) {
@ -131,7 +147,7 @@ export function registerEngineRoomRoutes(app: Express): void {
// Lista de engines - APENAS do Registry // Lista de engines - APENAS do Registry
app.get("/api/engine-room/engines", async (_req: Request, res: Response) => { app.get("/api/engine-room/engines", async (_req: Request, res: Response) => {
const registryServices = await fetchRegistryServices(); const registryServices = await fetchRegistryServicesWithRetry();
const engines = registryServices?.map(mapRegistryToEngine) || []; const engines = registryServices?.map(mapRegistryToEngine) || [];
res.json({ res.json({
@ -233,7 +249,7 @@ export function registerEngineRoomRoutes(app: Express): void {
return res.status(401).json({ error: "Not authenticated" }); return res.status(401).json({ error: "Not authenticated" });
} }
const registryServices = await fetchRegistryServices(); const registryServices = await fetchRegistryServicesWithRetry();
const service = registryServices?.find((s: any) => s.id === req.params.name || s.name === req.params.name); const service = registryServices?.find((s: any) => s.id === req.params.name || s.name === req.params.name);
if (!service) { if (!service) {

View File

@ -395,24 +395,8 @@ export function log(message: string, source = "express") {
await setupVite(httpServer, app); await setupVite(httpServer, app);
} }
// ALWAYS serve the app on the port specified in the environment variable PORT
// Other ports are firewalled. Default to 5000 if not specified.
// this serves both the API and the client.
// It is the only port that is not firewalled.
const port = parseInt(process.env.PORT || "5000", 10);
httpServer.listen(
{
port,
host: "0.0.0.0",
reusePort: true,
},
() => {
log(`serving on port ${port}`);
},
);
// Inicia Arcadia Kernel (Dashboard porta 5001) - Semana 3 // Inicia Arcadia Kernel (Dashboard porta 5001) - Semana 3
// Só inicia se KERNEL_ENABLED estiver setado // Inicia ANTES do servidor HTTP para garantir que o Registry esteja pronto
if (process.env.KERNEL_ENABLED === 'true') { if (process.env.KERNEL_ENABLED === 'true') {
try { try {
const kernel = new ArcadiaKernel({ const kernel = new ArcadiaKernel({
@ -427,4 +411,21 @@ export function log(message: string, source = "express") {
} else { } else {
log('Kernel desabilitado (set KERNEL_ENABLED=true para ativar)'); log('Kernel desabilitado (set KERNEL_ENABLED=true para ativar)');
} }
// ALWAYS serve the app on the port specified in the environment variable PORT
// Other ports are firewalled. Default to 5000 if not specified.
// this serves both the API and the client.
// It is the only port that is not firewalled.
// INICIA DEPOIS do Kernel para garantir que o Registry esteja pronto
const port = parseInt(process.env.PORT || "5000", 10);
httpServer.listen(
{
port,
host: "0.0.0.0",
reusePort: true,
},
() => {
log(`serving on port ${port}`);
},
);
})(); })();