feat: handle event propagation via DB (HA)
This commit is contained in:
@@ -125,6 +125,15 @@ uploads:
|
|||||||
|
|
||||||
offline: false
|
offline: false
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------
|
||||||
|
# High-Availability
|
||||||
|
# ---------------------------------------------------------------------
|
||||||
|
# Set to true if you have multiple concurrent instances running off the
|
||||||
|
# same DB (e.g. Kubernetes pods / load balanced instances). Leave false
|
||||||
|
# otherwise.
|
||||||
|
|
||||||
|
ha: false
|
||||||
|
|
||||||
# ---------------------------------------------------------------------
|
# ---------------------------------------------------------------------
|
||||||
# Data Path
|
# Data Path
|
||||||
# ---------------------------------------------------------------------
|
# ---------------------------------------------------------------------
|
||||||
|
|||||||
@@ -16,3 +16,4 @@ ssl:
|
|||||||
domain: $(LETSENCRYPT_DOMAIN)
|
domain: $(LETSENCRYPT_DOMAIN)
|
||||||
subscriberEmail: $(LETSENCRYPT_EMAIL)
|
subscriberEmail: $(LETSENCRYPT_EMAIL)
|
||||||
logLevel: info
|
logLevel: info
|
||||||
|
ha: $(HA_ACTIVE)
|
||||||
|
|||||||
+3
-11
@@ -5,7 +5,6 @@
|
|||||||
// Licensed under AGPLv3
|
// Licensed under AGPLv3
|
||||||
// ===========================================
|
// ===========================================
|
||||||
|
|
||||||
const Promise = require('bluebird')
|
|
||||||
const _ = require('lodash')
|
const _ = require('lodash')
|
||||||
const chalk = require('chalk')
|
const chalk = require('chalk')
|
||||||
|
|
||||||
@@ -60,16 +59,9 @@ const init = {
|
|||||||
})
|
})
|
||||||
},
|
},
|
||||||
async reload() {
|
async reload() {
|
||||||
console.warn(chalk.yellow('--- Stopping scheduled jobs...'))
|
console.warn(chalk.yellow('--- Gracefully stopping server...'))
|
||||||
if (global.WIKI.scheduler) {
|
await global.WIKI.kernel.shutdown()
|
||||||
global.WIKI.scheduler.stop()
|
|
||||||
}
|
|
||||||
console.warn(chalk.yellow('--- Closing DB connections...'))
|
|
||||||
await global.WIKI.models.knex.destroy()
|
|
||||||
console.warn(chalk.yellow('--- Closing Server connections...'))
|
|
||||||
if (global.WIKI.servers) {
|
|
||||||
await global.WIKI.servers.stopServers()
|
|
||||||
}
|
|
||||||
console.warn(chalk.yellow('--- Purging node modules cache...'))
|
console.warn(chalk.yellow('--- Purging node modules cache...'))
|
||||||
|
|
||||||
global.WIKI = {}
|
global.WIKI = {}
|
||||||
|
|||||||
@@ -69,6 +69,7 @@
|
|||||||
"dotize": "0.3.0",
|
"dotize": "0.3.0",
|
||||||
"elasticsearch6": "npm:@elastic/elasticsearch@6",
|
"elasticsearch6": "npm:@elastic/elasticsearch@6",
|
||||||
"elasticsearch7": "npm:@elastic/elasticsearch@7",
|
"elasticsearch7": "npm:@elastic/elasticsearch@7",
|
||||||
|
"eventemitter2": "6.0.0",
|
||||||
"emoji-regex": "9.0.0",
|
"emoji-regex": "9.0.0",
|
||||||
"express": "4.17.1",
|
"express": "4.17.1",
|
||||||
"express-brute": "1.0.1",
|
"express-brute": "1.0.1",
|
||||||
@@ -146,6 +147,7 @@
|
|||||||
"pg": "8.0.2",
|
"pg": "8.0.2",
|
||||||
"pg-hstore": "2.3.3",
|
"pg-hstore": "2.3.3",
|
||||||
"pg-query-stream": "3.0.6",
|
"pg-query-stream": "3.0.6",
|
||||||
|
"pg-pubsub": "0.5.0",
|
||||||
"pg-tsquery": "8.1.0",
|
"pg-tsquery": "8.1.0",
|
||||||
"pug": "2.0.4",
|
"pug": "2.0.4",
|
||||||
"punycode": "2.1.1",
|
"punycode": "2.1.1",
|
||||||
|
|||||||
@@ -28,6 +28,7 @@ defaults:
|
|||||||
maxFileSize: 5242880
|
maxFileSize: 5242880
|
||||||
maxFiles: 10
|
maxFiles: 10
|
||||||
offline: false
|
offline: false
|
||||||
|
ha: false
|
||||||
# DB defaults
|
# DB defaults
|
||||||
api:
|
api:
|
||||||
isEnabled: false
|
isEnabled: false
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ const migrateFromBeta = require('../db/beta')
|
|||||||
module.exports = {
|
module.exports = {
|
||||||
Objection,
|
Objection,
|
||||||
knex: null,
|
knex: null,
|
||||||
|
listener: null,
|
||||||
/**
|
/**
|
||||||
* Initialize DB
|
* Initialize DB
|
||||||
*
|
*
|
||||||
@@ -182,5 +183,55 @@ module.exports = {
|
|||||||
...this,
|
...this,
|
||||||
...models
|
...models
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* Subscribe to database LISTEN / NOTIFY for multi-instances events
|
||||||
|
*/
|
||||||
|
async subscribeToNotifications () {
|
||||||
|
const useHA = (WIKI.config.ha === true || WIKI.config.ha === 'true' || WIKI.config.ha === 1 || WIKI.config.ha === '1')
|
||||||
|
if (!useHA) {
|
||||||
|
return
|
||||||
|
} else if (WIKI.config.db.type !== 'postgres') {
|
||||||
|
WIKI.logger.warn(`Database engine doesn't support pub/sub. Will not handle concurrent instances: [ DISABLED ]`)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const PGPubSub = require('pg-pubsub')
|
||||||
|
|
||||||
|
this.listener = new PGPubSub(this.knex.client.connectionSettings, {
|
||||||
|
log (ev) {
|
||||||
|
WIKI.logger.debug(ev)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
this.listener.addChannel('wiki', payload => {
|
||||||
|
if (_.has(payload.event) && payload.source !== WIKI.INSTANCE_ID) {
|
||||||
|
WIKI.events.emit(payload.event, payload.value)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
WIKI.events.onAny(this.notifyViaDB)
|
||||||
|
|
||||||
|
WIKI.logger.info(`High-Availability Listener initialized successfully: [ OK ]`)
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* Unsubscribe from database LISTEN / NOTIFY
|
||||||
|
*/
|
||||||
|
async unsubscribeToNotifications () {
|
||||||
|
if (this.listener) {
|
||||||
|
WIKI.events.offAny(this.notifyViaDB)
|
||||||
|
this.listener.close()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* Publish event via database NOTIFY
|
||||||
|
*
|
||||||
|
* @param {string} event Event fired
|
||||||
|
* @param {object} value Payload of the event
|
||||||
|
*/
|
||||||
|
notifyViaDB (event, value) {
|
||||||
|
this.listener.publish('wiki', {
|
||||||
|
source: WIKI.INSTANCE_ID,
|
||||||
|
event,
|
||||||
|
value
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+18
-1
@@ -1,5 +1,5 @@
|
|||||||
const _ = require('lodash')
|
const _ = require('lodash')
|
||||||
const EventEmitter = require('events')
|
const EventEmitter = require('eventemitter2').EventEmitter2
|
||||||
|
|
||||||
/* global WIKI */
|
/* global WIKI */
|
||||||
|
|
||||||
@@ -37,6 +37,7 @@ module.exports = {
|
|||||||
WIKI.servers = require('./servers')
|
WIKI.servers = require('./servers')
|
||||||
WIKI.sideloader = require('./sideloader').init()
|
WIKI.sideloader = require('./sideloader').init()
|
||||||
WIKI.events = new EventEmitter()
|
WIKI.events = new EventEmitter()
|
||||||
|
await WIKI.models.subscribeToNotifications()
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
WIKI.logger.error(err)
|
WIKI.logger.error(err)
|
||||||
process.exit(1)
|
process.exit(1)
|
||||||
@@ -91,5 +92,21 @@ module.exports = {
|
|||||||
WIKI.logger.warn(err)
|
WIKI.logger.warn(err)
|
||||||
WIKI.telemetry.sendError(err)
|
WIKI.telemetry.sendError(err)
|
||||||
})
|
})
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* Graceful shutdown
|
||||||
|
*/
|
||||||
|
async shutdown () {
|
||||||
|
if (WIKI.models) {
|
||||||
|
await WIKI.models.unsubscribeToNotifications()
|
||||||
|
await WIKI.models.knex.client.pool.destroy()
|
||||||
|
await WIKI.models.knex.destroy()
|
||||||
|
}
|
||||||
|
if (WIKI.scheduler) {
|
||||||
|
WIKI.scheduler.stop()
|
||||||
|
}
|
||||||
|
if (WIKI.servers) {
|
||||||
|
await WIKI.servers.stopServers()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,11 +4,13 @@
|
|||||||
// ===========================================
|
// ===========================================
|
||||||
|
|
||||||
const path = require('path')
|
const path = require('path')
|
||||||
|
const nanoid = require('nanoid')
|
||||||
|
|
||||||
let WIKI = {
|
let WIKI = {
|
||||||
IS_DEBUG: process.env.NODE_ENV === 'development',
|
IS_DEBUG: process.env.NODE_ENV === 'development',
|
||||||
IS_MASTER: true,
|
IS_MASTER: true,
|
||||||
ROOTPATH: process.cwd(),
|
ROOTPATH: process.cwd(),
|
||||||
|
INSTANCE_ID: nanoid(10),
|
||||||
SERVERPATH: path.join(process.cwd(), 'server'),
|
SERVERPATH: path.join(process.cwd(), 'server'),
|
||||||
Error: require('./helpers/error'),
|
Error: require('./helpers/error'),
|
||||||
configSvc: require('./core/config'),
|
configSvc: require('./core/config'),
|
||||||
|
|||||||
Reference in New Issue
Block a user