Merge pull request #425 from dalcde/sequelize

Improve handling of termination signals
This commit is contained in:
David Mehren 2020-06-28 14:20:10 +02:00 committed by GitHub
commit c3a79fee9d
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 26 additions and 9 deletions

View file

@ -25,7 +25,7 @@ import { addNonceToLocals, computeDirectives } from './csp'
import { errors } from './errors' import { errors } from './errors'
import { logger } from './logger' import { logger } from './logger'
import { Revision, sequelize } from './models' import { Revision, sequelize } from './models'
import { realtime } from './realtime' import { realtime, State } from './realtime'
import { handleTermSignals } from './utils/functions' import { handleTermSignals } from './utils/functions'
import { AuthRouter, BaseRouter, HistoryRouter, ImageRouter, NoteRouter, StatusRouter, UserRouter } from './web/' import { AuthRouter, BaseRouter, HistoryRouter, ImageRouter, NoteRouter, StatusRouter, UserRouter } from './web/'
import { tooBusy, checkURI, redirectWithoutTrailingSlashes, codiMDVersion } from './web/middleware' import { tooBusy, checkURI, redirectWithoutTrailingSlashes, codiMDVersion } from './web/middleware'
@ -253,7 +253,7 @@ function startListen (): void {
const listenCallback = function (): void { const listenCallback = function (): void {
const schema = config.useSSL ? 'HTTPS' : 'HTTP' const schema = config.useSSL ? 'HTTPS' : 'HTTP'
logger.info('%s Server listening at %s', schema, address) logger.info('%s Server listening at %s', schema, address)
realtime.maintenance = false realtime.state = State.Running
} }
const unixCallback = function (): void { const unixCallback = function (): void {

View file

@ -20,6 +20,11 @@ export type SocketWithNoteId = Socket & { noteId: string }
const chance = new Chance() const chance = new Chance()
export enum State {
Starting,
Running,
Stopping
}
/* eslint-disable @typescript-eslint/no-use-before-define */ /* eslint-disable @typescript-eslint/no-use-before-define */
const realtime: { const realtime: {
onAuthorizeSuccess: (data, accept) => void; onAuthorizeSuccess: (data, accept) => void;
@ -27,7 +32,7 @@ const realtime: {
io: SocketIO.Server; isReady: () => boolean; io: SocketIO.Server; isReady: () => boolean;
connection: (socket: SocketWithNoteId) => void; connection: (socket: SocketWithNoteId) => void;
secure: (socket: SocketIO.Socket, next: (err?: Error) => void) => void; secure: (socket: SocketIO.Socket, next: (err?: Error) => void) => void;
getStatus: (callback) => void; maintenance: boolean; getStatus: (callback) => void; state: State;
} = { } = {
io: SocketIO(), io: SocketIO(),
onAuthorizeSuccess: onAuthorizeSuccess, onAuthorizeSuccess: onAuthorizeSuccess,
@ -36,7 +41,7 @@ const realtime: {
connection: connection, connection: connection,
getStatus: getStatus, getStatus: getStatus,
isReady: isReady, isReady: isReady,
maintenance: true state: State.Starting
} }
/* eslint-enable @typescript-eslint/no-use-before-define */ /* eslint-enable @typescript-eslint/no-use-before-define */
@ -751,7 +756,7 @@ function updateUserData (socket: Socket, user): void {
} }
function connection (socket: SocketWithNoteId): void { function connection (socket: SocketWithNoteId): void {
if (realtime.maintenance) return if (realtime.state !== State.Running) return
parseNoteIdFromSocket(socket, function (err, noteId) { parseNoteIdFromSocket(socket, function (err, noteId) {
if (err) { if (err) {
return failConnection(500, err, socket) return failConnection(500, err, socket)

View file

@ -2,7 +2,7 @@ import fs from 'fs'
import { config } from '../config' import { config } from '../config'
import { logger } from '../logger' import { logger } from '../logger'
import { Revision } from '../models' import { Revision } from '../models'
import { realtime } from '../realtime' import { realtime, State } from '../realtime'
/* /*
Converts a map from string to something into a plain JS object for transmitting via a websocket Converts a map from string to something into a plain JS object for transmitting via a websocket
@ -51,8 +51,15 @@ export function processData<T> (data: T, _default: T, process?: (T) => T): T | u
} }
export function handleTermSignals (io): void { export function handleTermSignals (io): void {
if (realtime.state === State.Starting) {
process.exit(0)
}
if (realtime.state === State.Stopping) {
// The function is already running. Do nothing
return
}
logger.info('CodiMD has been killed by signal, try to exit gracefully...') logger.info('CodiMD has been killed by signal, try to exit gracefully...')
realtime.maintenance = true realtime.state = State.Stopping
// disconnect all socket.io clients // disconnect all socket.io clients
Object.keys(io.sockets.sockets).forEach(function (key) { Object.keys(io.sockets.sockets).forEach(function (key) {
const socket = io.sockets.sockets[key] const socket = io.sockets.sockets[key]
@ -72,7 +79,7 @@ export function handleTermSignals (io): void {
if (realtime.isReady()) { if (realtime.isReady()) {
Revision.checkAllNotesRevision(function (err, notes) { Revision.checkAllNotesRevision(function (err, notes) {
if (err) { if (err) {
return logger.error(err) return logger.error('Error while writing changes to database. We will abort after trying for 30 seconds.\n' + err)
} }
if (!notes || notes.length <= 0) { if (!notes || notes.length <= 0) {
clearInterval(checkCleanTimer) clearInterval(checkCleanTimer)
@ -80,5 +87,10 @@ export function handleTermSignals (io): void {
} }
}) })
} }
}, 100) }, 500)
setTimeout(function () {
logger.error('Failed to write changes to database. Aborting')
clearInterval(checkCleanTimer)
process.exit(1)
}, 30000)
} }