Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,9 @@
"license": "MIT",
"scripts": {
"dev": "tsx --watch ./example/index.ts",
"test": "bun run test:node",
"test": "bun run test:node && bun run test:node-cluster",
"test:node": "npm install --prefix ./test/node/cjs/ && npm install --prefix ./test/node/esm/ && node ./test/node/cjs/index.js && node ./test/node/esm/index.js",
"test:node-cluster": "node ./test/cluster/cluster.cjs port && node ./test/cluster/cluster.cjs object && node ./test/cluster/cluster.cjs true && node ./test/cluster/cluster.cjs false",
"build": "bun build.ts",
"release": "npm run build && npm run test && npm publish --access public"
},
Expand Down
4 changes: 3 additions & 1 deletion src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -107,9 +107,11 @@ export const node = () => {
port: options,
silent: true,
websocket,
fetch: app.fetch
fetch: app.fetch,
reusePort: true
}
: {
reusePort: true,
...options,
silent: true,
websocket,
Expand Down
89 changes: 89 additions & 0 deletions test/cluster/cluster.cjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
const cluster = require('node:cluster')
const { Elysia } = require('elysia')
const { exit, pid } = require('node:process')
const { node } = require('@elysiajs/node')

const workersAmount = 5
const port = 3000
// Use args to differentiate parameter
// So code is more compact
const arg2 = process.argv[2]
let parameter
if (arg2 === 'port') {
parameter = port
} else if (arg2 === 'object') {
parameter = {
port
}
} else if (arg2 === 'true') {
parameter = {
port,
reusePort: true
}
} else if (arg2 === 'false') {
parameter = {
port,
reusePort: false
}
} else {
console.error(`Unknown argument: ${arg2}. Expected: port, object, true, false`)
exit(1)
}

/**
* Method to stop workers and then exit the current program
* @param {Worker[]} workers list of workers
* @param {number} code exit code
*/
function shutdown(workers, code) {
workers.forEach((it) => {
it.kill()
})
exit(code)
}

/**
* Start primary node
* This will create workers and then send request to primary which will then
* spread the requests to the workers. After that it will check for the result.
*/
async function startPrimary() {
let workers = []
for (let i = 0; i < workersAmount; i++) {
workers.push(cluster.fork())
}
// we need some delay to allow Elysia to initialize
await new Promise((resolve) => setTimeout(resolve, 2000))

// Make n API calls, we should receive n different PIDs back
// Checking if a server is run can only be done this way at the moment
// because error is really deep in srvx and async
// Even callback in Elysia.listen will be still be run even on error
const promises = workers.map(async (it) => {
const result = await fetch(`http://localhost:${port}`)
const workerPid = await result.text()
return workerPid
})
const result = await Promise.all(promises)
const pidsCount = new Set(result).size;
if (arg2 === 'false') {
if (pidsCount !== 1) {
console.error('❌ Server should return 1 pid.')
shutdown(workers, 1)
}
console.log('✅ Test exclusive mode succeed!')
shutdown(workers, 0)
}
if (pidsCount !== workersAmount) {
console.error("❌ Clustering error, number of pids doesn't match.")
shutdown(workers, 1)
}
console.log('✅ Test cluster mode succeed!')
shutdown(workers, 0)
}

if (cluster.isPrimary) {
startPrimary()
} else {
new Elysia({ adapter: node() }).get(`/`, pid).listen(parameter)
}