Skip to content

Conversation

@nitsujlangston
Copy link
Member

  • ✅ Unlimited restarts - Workers will continuously restart on crash
  • ✅ Abnormal exit only - Only restarts on crashes (code !== 0 or signal)
  • ✅ 5-second delay - Fixed 5-second delay before each restart (hardcoded
    )
  • ✅ No backoff - Consistent 5-second delay, no exponential backoff
  • ✅ Shutdown protection - Workers will NOT restart during service
    shutdown

Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR implements worker process restart functionality with automatic recovery from crashes. Workers now restart after a 5-second delay when they exit abnormally, while graceful shutdowns and service stops are respected.

Key changes:

  • Workers now automatically restart after crashes with a fixed 5-second delay
  • Added tracking of worker IDs and restart counts across restarts
  • Shutdown protection prevents restarts during service termination

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 3 comments.

File Description
packages/bitcore-node/src/services/worker.ts Implemented worker restart logic with exit handlers, restart scheduling, and shutdown protection
packages/bitcore-node/test/unit/services/worker.spec.ts Added comprehensive test suite covering restart scenarios, timing, edge cases, and error conditions

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +98 to +116
newWorker.on('exit', (code, signal) => {
const exitReason = code !== 0 || signal ? 'crashed' : 'stopped gracefully';
logger[code == 0 ? 'info' : 'error'](
`Worker ${newWorker.process.pid} ${exitReason} (code: ${code}, signal: ${signal})`
);

const workerIndex = this.workers.findIndex(w => w.worker === newWorker);
if (workerIndex > -1) {
const workerData = this.workers[workerIndex];
this.workers.splice(workerIndex, 1);

if ((code !== 0 || signal) && !this.shuttingDown) {
logger.info(`Scheduling worker ${workerData.workerId} restart in 5 seconds...`);
setTimeout(() => {
this.restartWorker(workerData.workerId, workerData.restartCount + 1);
}, 5 * 1000);
}
}
});
Copy link

Copilot AI Dec 24, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The exit handler logic is duplicated between the start() method (lines 33-50) and the restartWorker() method (lines 98-116). This duplication makes maintenance harder and increases the risk of inconsistencies. Consider extracting the exit handler into a separate private method like setupExitHandler() that accepts the worker, workerId, and restartCount as parameters.

Copilot uses AI. Check for mistakes.
Comment on lines +94 to +96
newWorker.on('message', (msg: any) => {
this.emit(msg.id, msg);
});
Copy link

Copilot AI Dec 24, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The message handler is duplicated in both start() (line 30-32) and restartWorker() (lines 94-96). Similar to the exit handler, this should be extracted to avoid duplication and ensure consistent behavior across initial workers and restarted workers.

Copilot uses AI. Check for mistakes.

// Should NOT have restarted
expect(forkStub.callCount).to.equal(initialWorkerCount);
expect(loggerStubs.info.calledWith(sinon.match('Not restarting worker'))).to.be.false;
Copy link

Copilot AI Dec 24, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This assertion is checking for false, but when a worker crashes during shutdown, the message 'Not restarting worker' should actually be logged. The test should verify that this message IS logged (i.e., expect it to be true), not that it isn't logged. The current assertion contradicts the test's purpose of verifying shutdown protection.

Suggested change
expect(loggerStubs.info.calledWith(sinon.match('Not restarting worker'))).to.be.false;
expect(loggerStubs.info.calledWith(sinon.match('Not restarting worker'))).to.be.true;

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant