Launching Colyseus in any way, with the port already in use on the machine, causes an uncaught promise rejection, that is not possible to intercept. This seems to have been affected by #287, but also applies to ..attach({ server }).
Sample terminal output
Error: listen EADDRINUSE: address already in use 0.0.0.0:8080
at Server.setupListenHandle [as _listen2] (net.js:1280:14)
at listenInCluster (net.js:1328:12)
at Server.listen (net.js:1415:7)
at WebSocketTransport.listen (/sandbox/node_modules/colyseus/lib/transport/WebSocketTransport.js:58:21)
at Promise (/sandbox/node_modules/colyseus/lib/Server.js:67:32)
at new Promise (<anonymous>)
at Server.<anonymous> (/sandbox/node_modules/colyseus/lib/Server.js:66:20)
at Generator.next (<anonymous>)
at /sandbox/node_modules/colyseus/lib/Server.js:8:71
at new Promise (<anonymous>)
(node:82) UnhandledPromiseRejectionWarning: TypeError: Cannot read property 'port' of null
at Server.<anonymous> (/sandbox/node_modules/colyseus/lib/Server.js:94:47)
at Generator.next (<anonymous>)
at /sandbox/node_modules/colyseus/lib/Server.js:8:71
at new Promise (<anonymous>)
at __awaiter (/sandbox/node_modules/colyseus/lib/Server.js:4:12)
at Server.gracefullyShutdown (/sandbox/node_modules/colyseus/lib/Server.js:92:16)
at Server.Utils_1.registerGracefulShutdown (/sandbox/node_modules/colyseus/lib/Server.js:49:60)
at process.on (/sandbox/node_modules/colyseus/lib/Utils.js:25:9)
at process.emit (events.js:198:13)
at process._fatalException (internal/bootstrap/node.js:497:27)
(node:82) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). (rejection id: 1)
(node:82) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.
When scaling game servers, they might accidentally clash on a port number. This would result in an immediate exit of the application in future versions of node, without the possibility of notifying an administrator.
Create your own HTTP server, and attach Colyseus only once the server is listening.
const server = createServer((_req, res) => {
res.statusCode = 403;
res.end();
});
const colyseus = new Server({ noServer: true });
colyseus.define("lobby", Lobby);
colyseus.define("game", Game);
server.on("error", err => {
Logger.fatal({ err }, "The HTTP server encountered an error");
process.exit(1);
});
server.once("listening", () => {
colyseus.attach({ server });
Logger.info(`Server listening on 127.0.0.1:${PORT}`);
});
server.listen(PORT);
Hi @MarcusCemes, currently Colyseus does not support using the cluster module. It is recommended to use PM2 in "fork" mode - so you'll always have processes listening on different ports.
See the current state of the scalability docs here: https://docs.colyseus.io/scalability/
Thanks @endel, the issue is not a result of using the cluster module. The sandbox uses the same process, however, I ran into this problem using two very different "forked" processes.
The problem which I wanted to highlight is that the failure leaves the application in a zombie-like state, where it's still running, but Colyseus failed to start, confusing orchestrators such as PM2, and requiring the implementation of a health-check poller.
Under normal circumstances, assuming correct configuration and simple setup, this should not be a problem. If it does, under any circumstance, clash with an already used machine port, it is not possible to detect the application failure without
handleRejection event handler, such as the hard-rejection library.The main reason being, somewhere as a result of .listen(...) on a Colyseus server, a Promise is executing without being chained to the call, so why does the call return a promise anyway if underneath it executes in its own asynchronous context?
Thanks for the explanation @MarcusCemes, a bit late but this has been now fixed on https://github.com/colyseus/colyseus/commit/cb243a0c9dd8b96cc44f77e775660bcbc4e9cb6a
The .listen() promise can be caught now when EADDRINUSE happens.