Created function ensuring datafile integrity

pull/2/head
Louis Chatriot 11 years ago
parent 9ee4c8abf9
commit adea00478c
  1. 7
      lib/datastore.js
  2. 82
      lib/persistence.js
  3. 4
      test/persistence.test.js
  4. 2
      test_lac/loadAndCrash.test.js

@ -542,4 +542,11 @@ Datastore.prototype.remove = function () {
};
var fs = require('fs');
console.log(fs.statSync('workspace/test.db'));
module.exports = Datastore;

@ -25,11 +25,11 @@ function Persistence (options) {
this.filename = this.db.filename;
if (!this.inMemoryOnly && this.filename) {
if (this.filename.charAt(this.filename.length - 1) === '~') {
throw "The datafile name can't end with a ~, which is reserved for automatic backup files";
} else {
this.tempFilename = this.filename + '~';
}
if (this.filename.charAt(this.filename.length - 1) === '~') {
throw "The datafile name can't end with a ~, which is reserved for automatic backup files";
} else {
this.tempFilename = this.filename + '~';
}
}
// For NW apps, store data in the same directory where NW stores application data
@ -93,6 +93,7 @@ Persistence.getNWAppFilename = function (appName, relativeFilename) {
Persistence.prototype.persistCachedDatabase = function (cb) {
var callback = cb || function () {}
, toPersist = ''
, self = this
;
if (this.inMemoryOnly) { return callback(null); }
@ -101,7 +102,30 @@ Persistence.prototype.persistCachedDatabase = function (cb) {
toPersist += model.serialize(doc) + '\n';
});
fs.writeFile(this.filename, toPersist, function (err) { return callback(err); });
async.waterfall([
function (cb) {
fs.exists(self.tempFilename, function (exists) {
if (exists) {
fs.unlink(self.tempFilename, function (err) { return cb(err); });
} else {
return cb();
}
});
}
, function (cb) {
console.log("==== 3");
fs.rename(self.filename, self.tempFilename, function (err) {
console.log("==== 4");
if (err) { return cb(err); }
fs.writeFile(self.filename, toPersist, function (err) {
console.log("==== 5");
if (err) { return cb(err); }
fs.unlink(self.tempFilename, function (err) { return cb(err); });
});
});
}
], function (err) { return callback(err); })
};
@ -198,6 +222,52 @@ Persistence.treatRawData = function (rawData) {
};
/**
* Ensure that this.filename contains the most up-to-date version of the data
* Even if a loadDatabase crashed before
*/
Persistence.prototype.ensureDatafileIntegrity = function (callback) {
var self = this ;
fs.exists(self.filename, function (filenameExists) {
fs.exists(self.tempFilename, function (tempFilenameExists) {
// Normal case
if (filenameExists && !tempFilenameExists) {
return callback();
}
// Process crashed right after renaming filename
if (!filenameExists && tempFilenameExists) {
return fs.rename(self.tempFilename, self.filename, function (err) { return callback(err); });
}
// No file exists, create empty datafile
if (!filenameExists && !tempFilenameExists) {
return fs.writeFile(self.filename, '', 'utf8', function (err) { callback(err); });
}
// Process crashed after or during write to datafile
// If datafile is not empty, it means process crashed after the write so we use it
// If it is empty, we don't know whether the database was emptied or we had a crash during write. The safest option is to use the temp datafile
if (filenameExists && tempFilenameExists) {
return fs.stat(self.filename, function (err, stats) {
if (err) { return callback(err); }
if (stats.size > 0) {
fs.unlink(self.tempFilename, function (err) { return callback(err); });
} else {
fs.unlink(self.tempFilename, function (err) {
if (err) { return callback(err); }
fs.rename(self.tempFilename, self.filename, function (err) { return callback(err); });
});
}
});
}
});
});
};
/**
* Load the database
* This means pulling data out of the data file or creating it if it doesn't exist

@ -278,7 +278,9 @@ describe('Persistence', function () {
cp.on('message', function (msg) {
// Let the child process enough time to crash
setTimeout(function () {
fs.readFileSync('workspace/lac.db', 'utf8').length.should.not.equal(0);
// fs.readFileSync('workspace/lac.db', 'utf8').length.should.not.equal(0);
console.log(fs.readFileSync('workspace/lac.db').length);
console.log(fs.readFileSync('workspace/lac.db~').length);
done();
}, 100);
});

@ -6,6 +6,6 @@ var Nedb = require('../lib/datastore.js')
setTimeout(function() {
process.send('crash');
process.exit();
}, 100);
}, 1880);
db.loadDatabase();
Loading…
Cancel
Save