diff --git a/src/batch.js b/src/batch.js index 475c4aa7ca..f15d06a453 100644 --- a/src/batch.js +++ b/src/batch.js @@ -21,6 +21,17 @@ exports.processSortedSet = function (setKey, process, options, callback) { return callback(new Error('[[error:process-not-a-function]]')); } + // Progress bar handling (upgrade scripts) + if (options.progress) { + db.sortedSetCard(setKey, function (err, total) { + if (err) { + // Unable to get total, do nothing. + } else { + options.progress.total = total; + } + }); + } + // use the fast path if possible if (db.processSortedSet && typeof options.doneIf !== 'function' && !utils.isNumber(options.alwaysStartAt)) { return db.processSortedSet(setKey, process, options.batch || DEFAULT_BATCH_SIZE, callback); @@ -53,6 +64,7 @@ exports.processSortedSet = function (setKey, process, options, callback) { } start += utils.isNumber(options.alwaysStartAt) ? options.alwaysStartAt : batch + 1; stop = start + batch; + next(); }); }); diff --git a/src/upgrade.js b/src/upgrade.js index e23fde22d1..c149dc4bbd 100644 --- a/src/upgrade.js +++ b/src/upgrade.js @@ -7,7 +7,7 @@ var path = require('path'); var semver = require('semver'); var db = require('./database'); -var utils = require('../public/src/utils'); +var file = require('../src/file'); /* * Need to write an upgrade script for NodeBB? Cool. @@ -23,7 +23,7 @@ var Upgrade = {}; Upgrade.getAll = function (callback) { async.waterfall([ - async.apply(utils.walk, path.join(__dirname, './upgrades')), + async.apply(file.walk, path.join(__dirname, './upgrades')), function (files, next) { // Sort the upgrade scripts based on version var versionA; @@ -93,7 +93,7 @@ Upgrade.runSingle = function (query, callback) { process.stdout.write('\nParsing upgrade scripts... '); async.waterfall([ - async.apply(utils.walk, path.join(__dirname, './upgrades')), + async.apply(file.walk, path.join(__dirname, './upgrades')), function (files, next) { next(null, files.filter(function (file) { return path.basename(file, '.js') === query; @@ -123,6 +123,13 @@ Upgrade.process = function (files, skipCount, callback) { var scriptExport = require(file); var date = new Date(scriptExport.timestamp); var version = path.dirname(file).split('/').pop(); + var progress = { + current: 0, + total: 0, + incr: Upgrade.incrementProgress, + script: scriptExport, + date: date, + }; process.stdout.write(' → '.white + String('[' + [date.getUTCFullYear(), date.getUTCMonth() + 1, date.getUTCDate()].join('/') + '] ').gray + String(scriptExport.name).reset + '... '); @@ -134,11 +141,20 @@ Upgrade.process = function (files, skipCount, callback) { } // Do the upgrade... - scriptExport.method(function (err) { + scriptExport.method.bind({ + progress: progress, + })(function (err) { if (err) { process.stdout.write('error\n'.red); return next(err); } + + if (progress.total > 0) { + process.stdout.clearLine(); + process.stdout.cursorTo(0); + process.stdout.write(' → '.white + String('[' + [date.getUTCFullYear(), date.getUTCMonth() + 1, date.getUTCDate()].join('/') + '] ').gray + String(scriptExport.name).reset + '... '); + } + process.stdout.write('OK\n'.green); // Record success in schemaLog db.sortedSetAdd('schemaLog', Date.now(), path.basename(file, '.js'), next); @@ -152,4 +168,23 @@ Upgrade.process = function (files, skipCount, callback) { ], callback); }; +Upgrade.incrementProgress = function () { + this.current += 1; + + // Redraw the progress bar + var percentage = 0; + var filled = 0; + var unfilled = 15; + if (this.total) { + percentage = Math.floor((this.current / this.total) * 100) + '%'; + filled = Math.floor((this.current / this.total) * 15); + unfilled = 15 - filled; + } + process.stdout.clearLine(); + process.stdout.cursorTo(0); + + process.stdout.write(' → '.white + String('[' + [this.date.getUTCFullYear(), this.date.getUTCMonth() + 1, this.date.getUTCDate()].join('/') + '] ').gray + String(this.script.name).reset + '... '); + process.stdout.write('[' + (filled ? new Array(filled).join('#') : '') + new Array(unfilled).join(' ') + '] (' + this.current + '/' + (this.total || '??') + ') ' + percentage); +}; + module.exports = Upgrade; diff --git a/src/upgrades/1.0.0/user_best_posts.js b/src/upgrades/1.0.0/user_best_posts.js index a832a71254..560ef7b29c 100644 --- a/src/upgrades/1.0.0/user_best_posts.js +++ b/src/upgrades/1.0.0/user_best_posts.js @@ -12,6 +12,7 @@ module.exports = { timestamp: Date.UTC(2016, 0, 14), method: function (callback) { var batch = require('../../batch'); + var progress = this.progress; batch.processSortedSet('posts:pid', function (ids, next) { async.eachSeries(ids, function (id, next) { @@ -24,8 +25,11 @@ module.exports = { } winston.verbose('processing pid: ' + postData.pid + ' uid: ' + postData.uid + ' votes: ' + postData.votes); db.sortedSetAdd('uid:' + postData.uid + ':posts:votes', postData.votes, postData.pid, next); + progress.incr(); }); }, next); - }, {}, callback); + }, { + progress: progress, + }, callback); }, }; diff --git a/src/upgrades/1.1.0/separate_upvote_downvote.js b/src/upgrades/1.1.0/separate_upvote_downvote.js index c397a36a7b..3f78667dc5 100644 --- a/src/upgrades/1.1.0/separate_upvote_downvote.js +++ b/src/upgrades/1.1.0/separate_upvote_downvote.js @@ -14,6 +14,8 @@ module.exports = { var batch = require('../../batch'); var posts = require('../../posts'); var count = 0; + var progress = this.progress; + batch.processSortedSet('posts:pid', function (pids, next) { winston.verbose('upgraded ' + count + ' posts'); count += pids.length; @@ -43,8 +45,12 @@ module.exports = { } else { next(); } + + progress.incr(); }, next); }, next); - }, {}, callback); + }, { + progress: progress, + }, callback); }, }; diff --git a/src/upgrades/1.3.0/favourites_to_bookmarks.js b/src/upgrades/1.3.0/favourites_to_bookmarks.js index 0888e69653..065f02c94a 100644 --- a/src/upgrades/1.3.0/favourites_to_bookmarks.js +++ b/src/upgrades/1.3.0/favourites_to_bookmarks.js @@ -10,11 +10,15 @@ module.exports = { name: 'Favourites to Bookmarks', timestamp: Date.UTC(2016, 9, 8), method: function (callback) { + var progress = this.progress; + function upgradePosts(next) { var batch = require('../../batch'); batch.processSortedSet('posts:pid', function (ids, next) { async.each(ids, function (id, next) { + progress.incr(); + async.waterfall([ function (next) { db.rename('pid:' + id + ':users_favourited', 'pid:' + id + ':users_bookmarked', next); @@ -34,7 +38,9 @@ module.exports = { }, ], next); }, next); - }, {}, next); + }, { + progress: progress, + }, next); } function upgradeUsers(next) { diff --git a/src/upgrades/1.3.0/sorted_sets_for_post_replies.js b/src/upgrades/1.3.0/sorted_sets_for_post_replies.js index b3ecd25f0a..1cce287829 100644 --- a/src/upgrades/1.3.0/sorted_sets_for_post_replies.js +++ b/src/upgrades/1.3.0/sorted_sets_for_post_replies.js @@ -13,12 +13,16 @@ module.exports = { method: function (callback) { var posts = require('../../posts'); var batch = require('../../batch'); + var progress = this.progress; + batch.processSortedSet('posts:pid', function (ids, next) { posts.getPostsFields(ids, ['pid', 'toPid', 'timestamp'], function (err, data) { if (err) { return next(err); } + progress.incr(); + async.eachSeries(data, function (postData, next) { if (!parseInt(postData.toPid, 10)) { return next(null); @@ -30,6 +34,8 @@ module.exports = { ], next); }, next); }); + }, { + progress: progress, }, callback); }, }; diff --git a/src/upgrades/1.5.0/flags_refactor.js b/src/upgrades/1.5.0/flags_refactor.js index 7aff6bcfe7..08fc1831d8 100644 --- a/src/upgrades/1.5.0/flags_refactor.js +++ b/src/upgrades/1.5.0/flags_refactor.js @@ -13,6 +13,7 @@ module.exports = { var batch = require('../../batch'); var posts = require('../../posts'); var flags = require('../../flags'); + var progress = this.progress; batch.processSortedSet('posts:pid', function (ids, next) { posts.getPostsByPids(ids, 1, function (err, posts) { @@ -80,10 +81,14 @@ module.exports = { } else { next(err); } + + progress.incr(); }); }); }, next); }); + }, { + progress: this.progress, }, callback); }, }; diff --git a/src/upgrades/1.5.0/moderation_history_refactor.js b/src/upgrades/1.5.0/moderation_history_refactor.js index 73b4e8f6d7..f98e1c62db 100644 --- a/src/upgrades/1.5.0/moderation_history_refactor.js +++ b/src/upgrades/1.5.0/moderation_history_refactor.js @@ -11,6 +11,8 @@ module.exports = { name: 'Update moderation notes to zset', timestamp: Date.UTC(2017, 2, 22), method: function (callback) { + var progress = this.progress; + batch.processSortedSet('users:joindate', function (ids, next) { async.each(ids, function (uid, next) { db.getObjectField('user:' + uid, 'moderationNote', function (err, moderationNote) { @@ -22,9 +24,13 @@ module.exports = { note: moderationNote, timestamp: Date.now(), }; + + progress.incr(); db.sortedSetAdd('uid:' + uid + ':moderation:notes', note.timestamp, JSON.stringify(note), next); }); }, next); + }, { + progress: this.progress, }, callback); }, }; diff --git a/src/upgrades/1.5.0/post_votes_zset.js b/src/upgrades/1.5.0/post_votes_zset.js index d989602e23..7feabe8169 100644 --- a/src/upgrades/1.5.0/post_votes_zset.js +++ b/src/upgrades/1.5.0/post_votes_zset.js @@ -10,6 +10,8 @@ module.exports = { name: 'New sorted set posts:votes', timestamp: Date.UTC(2017, 1, 27), method: function (callback) { + var progress = this.progress; + require('../../batch').processSortedSet('posts:pid', function (pids, next) { async.each(pids, function (pid, next) { db.getObjectFields('post:' + pid, ['upvotes', 'downvotes'], function (err, postData) { @@ -17,10 +19,13 @@ module.exports = { return next(err); } + progress.incr(); var votes = parseInt(postData.upvotes || 0, 10) - parseInt(postData.downvotes || 0, 10); db.sortedSetAdd('posts:votes', votes, pid, next); }); }, next); - }, {}, callback); + }, { + progress: this.progress, + }, callback); }, };