forked from snxraven/p2ns
fix: prevent EADDRINUSE double-bind on Holesail client startup
Add global activeClientKeys Set to track all active clients and prevent race conditions between domain init and cache restore paths. Add EADDRINUSE error handling in holesail.js, holesail_child.js
This commit is contained in:
@@ -83,6 +83,20 @@ async function startHolesailClient(domain, hash, ip, port, persistent = false) {
|
||||
logDebug('Holesail', `Holesail client already exists for ${key}`);
|
||||
return;
|
||||
}
|
||||
// Check global activeClientKeys to prevent double-bind (both forked and direct paths)
|
||||
if (state.activeClientKeys && state.activeClientKeys.has(key)) {
|
||||
logDebug('Holesail', `Holesail client already starting/active for ${key} (global check), waiting...`);
|
||||
// Wait for the existing promise to complete
|
||||
if (state.starting.has(key)) {
|
||||
await state.starting.get(key);
|
||||
return;
|
||||
}
|
||||
return;
|
||||
}
|
||||
// Mark as active before starting
|
||||
if (state.activeClientKeys) {
|
||||
state.activeClientKeys.add(key);
|
||||
}
|
||||
let startPromise = state.starting.get(key);
|
||||
if (!startPromise) {
|
||||
startPromise = (async () => {
|
||||
@@ -230,10 +244,24 @@ async function startHolesailClient(domain, hash, ip, port, persistent = false) {
|
||||
state.holesailClientTimeouts.set(key, timeout);
|
||||
}
|
||||
} catch (err) {
|
||||
logError('Holesail', `Error connecting Holesail client for ${key}: ${err.message}`);
|
||||
if (err.code === 'EADDRINUSE' || err.message.includes('EADDRINUSE')) {
|
||||
logWarn('Holesail', `Port ${port} on ${ip} already in use (EADDRINUSE) for ${key}, skipping duplicate bind`);
|
||||
if (state.activeClientKeys) {
|
||||
state.activeClientKeys.delete(key);
|
||||
}
|
||||
} else {
|
||||
logError('Holesail', `Error connecting Holesail client for ${key}: ${err.message}`);
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
logError('Holesail', `Failed to start Holesail client for ${key}: ${err.message}`);
|
||||
if (err.code === 'EADDRINUSE' || err.message.includes('EADDRINUSE')) {
|
||||
logWarn('Holesail', `Port ${port} on ${ip} already in use (EADDRINUSE) for ${key}, skipping duplicate bind`);
|
||||
if (state.activeClientKeys) {
|
||||
state.activeClientKeys.delete(key);
|
||||
}
|
||||
} else {
|
||||
logError('Holesail', `Failed to start Holesail client for ${key}: ${err.message}`);
|
||||
}
|
||||
} finally {
|
||||
state.starting.delete(key);
|
||||
}
|
||||
|
||||
@@ -38,9 +38,20 @@ process.on('message', async (msg) => {
|
||||
}
|
||||
|
||||
const holesail = new Holesail(opts);
|
||||
await holesail.ready();
|
||||
process.send({ type: 'ready', info: holesail.info });
|
||||
// Keep running
|
||||
try {
|
||||
await holesail.ready();
|
||||
process.send({ type: 'ready', info: holesail.info });
|
||||
// Keep running
|
||||
} catch (err) {
|
||||
if (err.code === 'EADDRINUSE' || err.message.includes('EADDRINUSE')) {
|
||||
console.warn(`Port ${opts.port} on ${opts.host} already in use (EADDRINUSE), exiting gracefully`);
|
||||
process.send({ type: 'error', message: 'EADDRINUSE: Port already in use' });
|
||||
process.exit(0); // Graceful exit instead of crash
|
||||
} else {
|
||||
process.send({ type: 'error', message: err.message });
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
process.send({ type: 'error', message: err.message });
|
||||
process.exit(1);
|
||||
|
||||
Reference in New Issue
Block a user