Comparing version 2.0.14 to 2.1.0
@@ -5,2 +5,10 @@ # Changelog | ||
## [2.1.0][] - 2021-03-15 | ||
- Improve server start and stop | ||
- Implmenent port re-bind | ||
- Unify logging output (before log init) | ||
- Disable Nagle's algorithm if configured | ||
- Improve config schemas | ||
## [2.0.14][] - 2021-03-13 | ||
@@ -116,3 +124,4 @@ | ||
[unreleased]: https://github.com/metarhia/impress/compare/v2.0.14...HEAD | ||
[unreleased]: https://github.com/metarhia/impress/compare/v2.1.0...HEAD | ||
[2.1.0]: https://github.com/metarhia/impress/compare/v2.0.14...v2.1.0 | ||
[2.0.14]: https://github.com/metarhia/impress/compare/v2.0.13...v2.0.14 | ||
@@ -119,0 +128,0 @@ [2.0.13]: https://github.com/metarhia/impress/compare/v2.0.12...v2.0.13 |
@@ -10,3 +10,5 @@ 'use strict'; | ||
const metavm = require('metavm'); | ||
const metautil = require('metautil'); | ||
const { loadSchema } = require('metaschema'); | ||
const { Logger } = require('metalog'); | ||
@@ -16,38 +18,50 @@ const CONFIG_SECTIONS = ['log', 'scale', 'server', 'sessions']; | ||
const CFG_PATH = path.join(PATH, 'application/config'); | ||
const LOG_PATH = path.join(PATH, 'log'); | ||
const CTRL_C = 3; | ||
const configError = (err) => { | ||
console.log('Can not read configuration: application/config/server.js'); | ||
console.error(err); | ||
process.exit(1); | ||
}; | ||
(async () => { | ||
const logOpt = { path: LOG_PATH, workerId: 0, toFile: [] }; | ||
const { console } = await new Logger(logOpt); | ||
const validateConfig = async (config) => { | ||
const schemaPath = path.join(__dirname, 'schemas/config'); | ||
let valid = true; | ||
for (const section of CONFIG_SECTIONS) { | ||
const schema = await loadSchema(path.join(schemaPath, section + '.js')); | ||
const checkResult = schema.check(config[section]); | ||
if (!checkResult.valid) { | ||
for (const err of checkResult.errors) console.log(err); | ||
valid = false; | ||
} | ||
} | ||
if (!valid) { | ||
console.error('Can not start server'); | ||
const exit = (message) => { | ||
console.error(metautil.replace(message, PATH, '')); | ||
process.exit(1); | ||
} | ||
}; | ||
}; | ||
(async () => { | ||
const validateConfig = async (config) => { | ||
const schemaPath = path.join(__dirname, 'schemas/config'); | ||
let valid = true; | ||
for (const section of CONFIG_SECTIONS) { | ||
const fileName = path.join(schemaPath, section + '.js'); | ||
const schema = await loadSchema(fileName); | ||
const checkResult = schema.check(config[section]); | ||
if (!checkResult.valid) { | ||
for (const err of checkResult.errors) { | ||
console.error(`${err} in application/config/${section}.js`); | ||
} | ||
valid = false; | ||
} | ||
} | ||
if (!valid) exit('Can not start server'); | ||
}; | ||
const context = metavm.createContext({ process }); | ||
const options = { mode: process.env.MODE, context }; | ||
const config = await new Config(CFG_PATH, options).catch(configError); | ||
const config = await new Config(CFG_PATH, options).catch((err) => { | ||
exit(`Can not read configuration: ${CFG_PATH}\n${err.stack}`); | ||
}); | ||
await validateConfig(config); | ||
if (!config.server) configError(new Error('Section "server" is not found')); | ||
const { balancer, ports = [], workers = {} } = config.server; | ||
const count = ports.length + (balancer ? 1 : 0) + (workers.pool || 0); | ||
let active = count; | ||
let startTimer = null; | ||
let active = 0; | ||
let starting = 0; | ||
const threads = new Array(count); | ||
const stop = async () => { | ||
for (const worker of threads) { | ||
worker.postMessage({ type: 'event', name: 'stop' }); | ||
} | ||
}; | ||
const start = (id) => { | ||
@@ -57,2 +71,3 @@ const workerPath = path.join(__dirname, 'lib/worker.js'); | ||
threads[id] = worker; | ||
worker.on('exit', (code) => { | ||
@@ -62,2 +77,18 @@ if (code !== 0) start(id); | ||
}); | ||
worker.on('online', () => { | ||
if (++starting === count) { | ||
startTimer = setTimeout(() => { | ||
if (active !== count) console.warn('Server initialization timed out'); | ||
}, config.server.timeouts.start); | ||
} | ||
}); | ||
worker.on('message', (data) => { | ||
if (data.type === 'event' && data.name === 'started') active++; | ||
if (active === count && startTimer) { | ||
clearTimeout(startTimer); | ||
startTimer = null; | ||
} | ||
}); | ||
}; | ||
@@ -67,8 +98,2 @@ | ||
const stop = async () => { | ||
for (const worker of threads) { | ||
worker.postMessage({ name: 'stop' }); | ||
} | ||
}; | ||
process.on('SIGINT', stop); | ||
@@ -75,0 +100,0 @@ process.on('SIGTERM', stop); |
@@ -81,4 +81,2 @@ 'use strict'; | ||
application.server = new Server(config.server, application); | ||
const { port } = application.server; | ||
console.info(`Listen port ${port} in worker ${threadId}`); | ||
} | ||
@@ -88,5 +86,6 @@ | ||
console.info(`Application started in worker ${threadId}`); | ||
worker.parentPort.postMessage({ type: 'event', name: 'started' }); | ||
worker.parentPort.on('message', async (message) => { | ||
if (message.name === 'stop') { | ||
worker.parentPort.on('message', async (data) => { | ||
if (data.type === 'event' && data.name === 'stop') { | ||
if (application.finalization) return; | ||
@@ -93,0 +92,0 @@ console.info(`Graceful shutdown in worker ${threadId}`); |
{ | ||
"name": "impress", | ||
"version": "2.0.14", | ||
"version": "2.1.0", | ||
"author": "Timur Shemsedinov <timur.shemsedinov@gmail.com>", | ||
@@ -70,4 +70,4 @@ "description": "Enterprise application server for Node.js", | ||
"@metarhia/config": "^2.1.0", | ||
"metacom": "^1.5.3", | ||
"metalog": "^3.1.0", | ||
"metacom": "^1.6.0", | ||
"metalog": "^3.1.1", | ||
"metaschema": "^1.0.2", | ||
@@ -78,3 +78,3 @@ "metautil": "^3.5.1", | ||
"devDependencies": { | ||
"@types/node": "^14.14.34", | ||
"@types/node": "^14.14.35", | ||
"eslint": "^7.22.0", | ||
@@ -81,0 +81,0 @@ "eslint-config-metarhia": "^7.0.1", |
({ | ||
keepDays: { type: 'number', default: 100 }, | ||
writeInterval: { type: 'number', default: 3000 }, | ||
writeBuffer: { type: 'number', default: 64 * 1024 }, | ||
keepDays: 'number', | ||
writeInterval: 'number', | ||
writeBuffer: 'number', | ||
toFile: { array: 'string' }, | ||
toStdout: { array: 'string' }, | ||
}); |
@@ -6,3 +6,3 @@ ({ | ||
token: { type: 'string', length: 32 }, | ||
gc: { type: 'number', default: 60 * 60 * 1000 }, | ||
gc: 'number', | ||
}); |
@@ -6,12 +6,18 @@ ({ | ||
ports: { array: 'number' }, | ||
timeout: { type: 'number', default: 5000 }, | ||
nagle: 'boolean', | ||
timeouts: { | ||
bind: 'number', | ||
start: 'number', | ||
stop: 'number', | ||
request: 'number', | ||
watch: 'number', | ||
}, | ||
queue: { | ||
concurrency: { type: 'number', default: 1000 }, | ||
size: { type: 'number', default: 2000 }, | ||
timeout: { type: 'number', default: 3000 }, | ||
concurrency: 'number', | ||
size: 'number', | ||
timeout: 'number', | ||
}, | ||
workers: { | ||
pool: { type: 'number', default: 0 }, | ||
timeout: { type: 'number', default: 3000 }, | ||
pool: 'number', | ||
}, | ||
}); |
({ | ||
sid: { type: 'string', default: 'token' }, | ||
characters: { | ||
type: 'string', | ||
default: 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789', | ||
}, | ||
length: { type: 'number', default: 64 }, | ||
sid: 'string', | ||
characters: 'string', | ||
length: 'number', | ||
secret: 'string', | ||
regenerate: { type: 'number', default: 60 * 60 * 1000 }, | ||
expire: { type: 'number', default: 24 * 60 * 60 * 1000 }, | ||
regenerate: 'number', | ||
expire: 'number', | ||
persistent: 'boolean', | ||
limits: { | ||
ip: { type: 'number', default: 20 }, | ||
user: { type: 'number', default: 5 }, | ||
ip: 'number', | ||
user: 'number', | ||
}, | ||
}); |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
41719
875
Updatedmetacom@^1.6.0
Updatedmetalog@^3.1.1