You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
doushio/upkeep/backup.js

110 lines
2.6 KiB

/* Dumps the redis DB to rdb, then uploads that to S3.
* Configure below, and provide upkeep/credentials.json.
*/
var AWS = require('aws-sdk'),
config = require('../config'),
crypto = require('crypto'),
db = require('../db'),
fs = require('fs');
var BUCKET = 'backupbucket';
var ACL = 'bucket-owner-full-control';
var RDB_PATH = '/Users/lalc/dump.rdb';
var BGSAVE_TIMEOUT = 5*60;
var r, s3;
function dump_rdb(cb) {
console.log('Dumping rdb...');
r.lastsave(function (err, lastSave) {
if (err) return err;
r.bgsave(function (err) {
if (err) return err;
setTimeout(poll_for_lastsave_after.bind(null,
lastSave, 0, cb), 200);
});
});
}
function poll_for_lastsave_after(lastSave, seconds, cb) {
// Will take slightly longer than BGSAVE_TIMEOUT seconds since
// we're waiting for a lastsave command between each second
if (seconds > BGSAVE_TIMEOUT)
return cb("bgsave timeout");
r.lastsave(function (err, newSave) {
if (err)
return cb(err);
if (newSave > lastSave) {
console.log('Obtained dump after', seconds, 'sec');
check_mtime(RDB_PATH, newSave, cb);
return;
}
if (newSave < lastSave)
return cb("Time travel");
setTimeout(poll_for_lastsave_after.bind(null,
lastSave, seconds+1, cb), 1000);
});
}
function check_mtime(path, lastsave, cb) {
console.log(' redis lastsave:', lastsave);
fs.stat(path, function (err, stat) {
if (err)
return cb(err);
var mtime = Math.floor(stat.mtime.getTime() / 1000);
console.log(' ' + path + ' mtime:', mtime);
if (Math.abs(mtime - lastsave) > 1000)
return cb("Bad mtime: "+lastsave+" != "+mtime);
cb(null);
});
}
function upload(kind, filename, cb) {
var now = new Date();
var random = crypto.randomBytes(8).toString('hex');
var dir = now.getUTCFullYear() + '-' + pad2(now.getUTCMonth()+1);
var name = dir+'-'+pad2(now.getUTCDate()) + '-' + kind + '-' + random;
var key = dir + '/' + name;
var stream = fs.createReadStream(filename);
var params = {
ACL: ACL,
Bucket: BUCKET,
Key: key,
Body: stream,
};
process.stdout.write('Writing s3://'+BUCKET+'/'+key+' ...');
s3.putObject(params, function (err, data) {
if (err) {
console.log('failed!');
return cb(err);
}
console.log('done.');
console.log('ETag:', data.ETag);
cb(null);
});
}
function pad2(n) {
return (n < 10 ? '0' : '') + n;
}
if (require.main == module) {
s3 = new AWS.S3().client;
s3.config.loadFromPath('upkeep/credentials.json');
r = db.redis_client();
dump_rdb(function (err) {
if (err)
throw err;
upload('dump.rdb', RDB_PATH, function (err) {
if (err)
throw err;
process.exit(0);
});
});
}