diff --git a/lib/app.js b/lib/app.js index c4419b3..2bd7466 100644 --- a/lib/app.js +++ b/lib/app.js @@ -22,10 +22,10 @@ class App { this.database = new Database(this) // Setup database instance - this.database.ensureIndexes() - - this.database.loadGraphPoints(config.graphDuration, () => { - this.database.loadRecords(callback) + this.database.ensureIndexes(() => { + this.database.loadGraphPoints(config.graphDuration, () => { + this.database.loadRecords(callback) + }) }) } diff --git a/lib/database.js b/lib/database.js index c0bfe7c..48b74d2 100644 --- a/lib/database.js +++ b/lib/database.js @@ -1,5 +1,7 @@ const sqlite = require('sqlite3') +const logger = require('./logger') + const config = require('../config') const { TimeTracker } = require('./time') @@ -28,18 +30,36 @@ class Database { // Ensure the initial tables are created // This does not created indexes since it is only inserted to this._currentDatabaseCopyInstance.serialize(() => { - this._currentDatabaseCopyInstance.run('CREATE TABLE IF NOT EXISTS pings (timestamp BIGINT NOT NULL, ip TINYTEXT, playerCount MEDIUMINT)') + this._currentDatabaseCopyInstance.run('CREATE TABLE IF NOT EXISTS pings (timestamp BIGINT NOT NULL, ip TINYTEXT, playerCount MEDIUMINT)', err => { + if (err) { + logger.log('error', 'Cannot create initial table for daily database') + throw err + } + }) }) } return this._currentDatabaseCopyInstance } - ensureIndexes () { + ensureIndexes (callback) { + const handleError = err => { + if (err) { + logger.log('error', 'Cannot create table or table index') + throw err + } + } + this._sql.serialize(() => { - this._sql.run('CREATE TABLE IF NOT EXISTS pings (timestamp BIGINT NOT NULL, ip TINYTEXT, playerCount MEDIUMINT)') - this._sql.run('CREATE INDEX IF NOT EXISTS ip_index ON pings (ip, playerCount)') - this._sql.run('CREATE INDEX IF NOT EXISTS timestamp_index on PINGS (timestamp)') + this._sql.run('CREATE TABLE IF NOT EXISTS pings (timestamp BIGINT NOT NULL, ip TINYTEXT, playerCount MEDIUMINT)', handleError) + this._sql.run('CREATE INDEX IF NOT EXISTS ip_index ON pings (ip, playerCount)', handleError) + this._sql.run('CREATE INDEX IF NOT EXISTS timestamp_index on PINGS (timestamp)', [], err => { + handleError(err) + // Queries are executed one at a time; this is the last one. + // Note that queries not scheduled directly in the callback function of + // #serialize are not necessarily serialized. + callback() + }) }) } @@ -125,13 +145,24 @@ class Database { this._sql.all('SELECT * FROM pings WHERE timestamp >= ? AND timestamp <= ?', [ startTime, endTime - ], (_, data) => callback(data)) + ], (err, data) => { + if (err) { + logger.log('error', 'Cannot get recent pings') + throw err + } + callback(data) + }) } getRecord (ip, callback) { this._sql.all('SELECT MAX(playerCount), timestamp FROM pings WHERE ip = ?', [ ip - ], (_, data) => { + ], (err, data) => { + if (err) { + logger.log('error', `Cannot get ping record for ${ip}`) + throw err + } + // For empty results, data will be length 1 with [null, null] const playerCount = data[0]['MAX(playerCount)'] const timestamp = data[0].timestamp @@ -139,10 +170,10 @@ class Database { // Allow null timestamps, the frontend will safely handle them // This allows insertion of free standing records without a known timestamp if (playerCount !== null) { - // eslint-disable-next-line standard/no-callback-literal + // eslint-disable-next-line node/no-callback-literal callback(true, playerCount, timestamp) } else { - // eslint-disable-next-line standard/no-callback-literal + // eslint-disable-next-line node/no-callback-literal callback(false) } }) @@ -161,7 +192,12 @@ class Database { _insertPingTo (ip, timestamp, unsafePlayerCount, db) { const statement = db.prepare('INSERT INTO pings (timestamp, ip, playerCount) VALUES (?, ?, ?)') - statement.run(timestamp, ip, unsafePlayerCount) + statement.run(timestamp, ip, unsafePlayerCount, err => { + if (err) { + logger.error(`Cannot insert ping record of ${ip} at ${timestamp}`) + throw err + } + }) statement.finalize() } }