diff --git a/src/backup.js b/src/backup.js index 5aa8412..e1ce0d9 100644 --- a/src/backup.js +++ b/src/backup.js @@ -15,16 +15,25 @@ import fs from 'fs'; import redis from 'redis'; import bluebird from 'bluebird'; +import process from 'process'; +import { spawn } from 'child_process'; + +import { DailyCron } from './utils/cron'; +import { + updateBackupRedis, + createPngBackup, + incrementialBackupRedis, +} from './core/tilesBackup'; +import canvases from './canvases.json'; + /* * use low cpu priority */ -import process from 'process'; -import { spawn } from 'child_process'; -const priority = 15; -const proc= spawn("renice", [priority, process.pid]); -proc.on('exit', function (code) { - if (code !== 0){ - console.log("renice failed with code - " +code); +const priority = 15; +const proc = spawn('renice', [priority, process.pid]); +proc.on('exit', (code) => { + if (code !== 0) { + console.log(`renice failed with code ${code}`); } console.log('Useing low cpu priority'); }); @@ -34,13 +43,6 @@ proc.on('exit', function (code) { bluebird.promisifyAll(redis.RedisClient.prototype); bluebird.promisifyAll(redis.Multi.prototype); -import canvases from './canvases.json'; -import { - updateBackupRedis, - createPngBackup, - incrementialBackupRedis, -} from './core/tilesBackup'; - const { CANVAS_REDIS_URL, BACKUP_REDIS_URL, @@ -64,23 +66,60 @@ backupRedis.on('error', () => { }); -function dailyBackup() { +function getDateFolder() { if (!fs.existsSync(BACKUP_DIR)) { - throw new Error(`Backup directory ${backupDir} does not exist!`); + throw new Error(`Backup directory ${BACKUP_DIR} does not exist!`); + } + const date = new Date(); + // eslint-disable-next-line max-len + const dayDir = `${date.getFullYear()}${date.getMonth() + 1}${date.getDate()}`; + const backupDir = `${BACKUP_DIR}/${dayDir}`; + return backupDir; +} + +function dailyBackup() { + const backupDir = getDateFolder(); + if (!fs.existsSync(backupDir)) { + fs.mkdirSync(backupDir); } backupRedis.flushall('ASYNC', async () => { - const date = new Date(); - const dayDir = `${date.getFullYear()}${date.getMonth() + 1}${date.getDate()}`; - const backupDir = `${BACKUP_DIR}/${dayDir}`; if (!fs.existsSync(backupDir)) { fs.mkdirSync(backupDir); } await updateBackupRedis(canvasRedis, backupRedis, canvases); await createPngBackup(backupRedis, canvases, backupDir); - await incrementialBackupRedis(canvasRedis, backupRedis, canvases, backupDir); - console.log(`Daily backup ${dayDir} done`); + console.log('Daily backup done'); }); } -dailyBackup(); +function incrementialBackup() { + const backupDir = getDateFolder(); + if (!fs.existsSync(backupDir)) { + fs.mkdirSync(backupDir); + } + incrementialBackupRedis( + canvasRedis, + backupRedis, + canvases, + backupDir, + ); +} + +async function startCronJobs() { + if (!fs.existsSync(BACKUP_DIR)) { + throw new Error(`Backup directory ${BACKUP_DIR} does not exist!`); + } + const backupDir = getDateFolder(); + if (!fs.existsSync(backupDir)) { + await dailyBackup(); + } + + console.log( + 'Creating one full PNG backup per day and incremential backups every hour.', + ); + DailyCron.hook(dailyBackup); + setInterval(incrementialBackup, 15 * 60 * 1000); +} + +startCronJobs(); diff --git a/src/core/tilesBackup.js b/src/core/tilesBackup.js index 58f61c7..e53bc6c 100644 --- a/src/core/tilesBackup.js +++ b/src/core/tilesBackup.js @@ -72,8 +72,8 @@ export async function incrementialBackupRedis( if (!fs.existsSync(canvasBackupDir)) { fs.mkdirSync(canvasBackupDir); } - const hourOfDay = new Date().getHours(); - const canvasTileBackupDir = `${canvasBackupDir}/${hourOfDay}`; + const date = new Date(); + const canvasTileBackupDir = `${canvasBackupDir}/${date.getHours()}${date.getMinutes()}`; if (!fs.existsSync(canvasTileBackupDir)) { fs.mkdirSync(canvasTileBackupDir); } diff --git a/src/utils/cron.js b/src/utils/cron.js index 73ee4ca..fe40856 100644 --- a/src/utils/cron.js +++ b/src/utils/cron.js @@ -9,27 +9,27 @@ import { HOUR } from '../core/constants'; import logger from '../core/logger'; class Cron { - last_run: number; + lastRun: number; interval: number; functions: Array; timeout; // interval = how many hours between runs - // last_run = when this cron job was last run - constructor(interval: number, last_run: number = 0) { - this.check_for_execution = this.check_for_execution.bind(this); + // lastRun = when this cron job was last run + constructor(interval: number, lastRun: number = 0) { + this.checkForExecution = this.checkForExecution.bind(this); this.interval = interval; - this.last_run = last_run; + this.lastRun = lastRun; this.functions = []; - this.timeout = setInterval(this.check_for_execution, HOUR); + this.timeout = setInterval(this.checkForExecution, HOUR); } - check_for_execution() { - const cur_time = Date.now(); - if (cur_time > this.last_run + this.interval * HOUR) { + checkForExecution() { + const curTime = Date.now(); + if (curTime > this.lastRun + this.interval * HOUR) { logger.info(`Run cron events for interval: ${this.interval}h`); - this.last_run = cur_time; + this.lastRun = curTime; this.functions.forEach(async (item) => { item(); }); @@ -42,19 +42,19 @@ class Cron { } -function initialize_daily_cron() { +function initializeDailyCron() { const now = new Date(); // make it first run at midnight - const last_run = now.getTime() - now.getHours() * HOUR; - const cron = new Cron(24, last_run); + const lastRun = now.getTime() - now.getHours() * HOUR; + const cron = new Cron(24, lastRun); return cron; } -function initialize_hourly_cron() { +function initializeHourlyCron() { const cron = new Cron(1, Date.now()); return cron; } -export const DailyCron = initialize_daily_cron(); +export const DailyCron = initializeDailyCron(); -export const HourlyCron = initialize_hourly_cron(); +export const HourlyCron = initializeHourlyCron(); diff --git a/tools/copy.js b/tools/copy.js index 08f52dd..775bbd7 100644 --- a/tools/copy.js +++ b/tools/copy.js @@ -15,6 +15,7 @@ async function copy() { copyFile('LICENSE', 'build/LICENSE'), copyDir('public', 'build/public'), copyFile('tools/example-ecosystem.yml', 'build/ecosystem.example.yml'), + copyFile('tools/example-ecosystem-backup.yml', 'build/ecosystem-backup.example.yml'), ]); } diff --git a/tools/example-ecosystem-backup.yml b/tools/example-ecosystem-backup.yml new file mode 100644 index 0000000..3451f27 --- /dev/null +++ b/tools/example-ecosystem-backup.yml @@ -0,0 +1,8 @@ +apps: + - script : ./backup.js + name : 'backup' + node_args: --nouse-idle-notification --expose-gc + env: + CANVAS_REDIS_URL: 'redis://localhost:6379' + BACKUP_REDIS_URL: 'redis://localhost:6380' + BACKUP_DIR: '/backups' diff --git a/tools/webpack.config.js b/tools/webpack.config.js index 4622c79..20fdb59 100644 --- a/tools/webpack.config.js +++ b/tools/webpack.config.js @@ -299,12 +299,13 @@ const webConfig = { entry: { web: ['./src/web.js'], + backup: ['./src/backup.js'], }, output: { ...config.output, path: path.resolve(__dirname, '../build'), - filename: './web.js', + filename: '[name].js', libraryTarget: 'commonjs2', },