Flush data after both writes and fix a test

pull/2/head
Louis Chatriot 9 years ago
parent d0523a8071
commit c927524a52
  1. 19
      lib/storage.js
  2. 23
      test/db.test.js

@ -34,6 +34,17 @@ storage.ensureFileDoesntExist = function (file, callback) {
}; };
/**
* Flush data in OS buffer to storage if corresponding option is set
*/
storage.flushToStorage = function (filename, callback) {
fs.open(filename, 'r', function (err, fd) {
if (err) { return callback(err); }
fs.fsync(fd, function (err) { return callback(err); });
});
};
/** /**
* Fully write or rewrite the datafile, immune to crashes during the write operation (data will not be lost) * Fully write or rewrite the datafile, immune to crashes during the write operation (data will not be lost)
* @param {String} filename * @param {String} filename
@ -52,7 +63,10 @@ storage.crashSafeWriteFile = function (filename, data, cb) {
, function (cb) { , function (cb) {
storage.exists(filename, function (exists) { storage.exists(filename, function (exists) {
if (exists) { if (exists) {
storage.rename(filename, oldFilename, function (err) { return cb(err); }); storage.rename(filename, oldFilename, function (err) {
if (err) { return cb(err); }
storage.flushToStorage(oldFilename, cb); // Ensure filename doesn't exist due to rename atomicity
});
} else { } else {
return cb(); return cb();
} }
@ -64,8 +78,9 @@ storage.crashSafeWriteFile = function (filename, data, cb) {
, function (cb) { , function (cb) {
storage.rename(tempFilename, filename, function (err) { return cb(err); }); storage.rename(tempFilename, filename, function (err) { return cb(err); });
} }
, async.apply(storage.flushToStorage, filename)
, async.apply(storage.ensureFileDoesntExist, oldFilename) , async.apply(storage.ensureFileDoesntExist, oldFilename)
], function (err) { if (err) { return callback(err); } else { return callback(null); } }) ], function (err) { return callback(err); })
}; };

@ -285,7 +285,7 @@ describe('Database', function () {
insertedDoc.createdAt.should.equal(insertedDoc.updatedAt); insertedDoc.createdAt.should.equal(insertedDoc.updatedAt);
assert.isDefined(insertedDoc._id); assert.isDefined(insertedDoc._id);
Object.keys(insertedDoc).length.should.equal(4); Object.keys(insertedDoc).length.should.equal(4);
assert.isBelow(Math.abs(insertedDoc.createdAt.getTime() - beginning), 15); // No more than 15ms should have elapsed assert.isBelow(Math.abs(insertedDoc.createdAt.getTime() - beginning), 30); // No more than 30ms should have elapsed (worst case, if there is a flush)
// Modifying results of insert doesn't change the cache // Modifying results of insert doesn't change the cache
insertedDoc.bloup = "another"; insertedDoc.bloup = "another";
@ -332,7 +332,7 @@ describe('Database', function () {
d.insert(newDoc, function (err, insertedDoc) { d.insert(newDoc, function (err, insertedDoc) {
Object.keys(insertedDoc).length.should.equal(4); Object.keys(insertedDoc).length.should.equal(4);
insertedDoc.createdAt.getTime().should.equal(234); // Not modified insertedDoc.createdAt.getTime().should.equal(234); // Not modified
assert.isBelow(insertedDoc.updatedAt.getTime() - beginning, 15); // Created assert.isBelow(insertedDoc.updatedAt.getTime() - beginning, 30); // Created
d.find({}, function (err, docs) { d.find({}, function (err, docs) {
assert.deepEqual(insertedDoc, docs[0]); assert.deepEqual(insertedDoc, docs[0]);
@ -354,7 +354,7 @@ describe('Database', function () {
d.insert(newDoc, function (err, insertedDoc) { d.insert(newDoc, function (err, insertedDoc) {
Object.keys(insertedDoc).length.should.equal(4); Object.keys(insertedDoc).length.should.equal(4);
insertedDoc.updatedAt.getTime().should.equal(234); // Not modified insertedDoc.updatedAt.getTime().should.equal(234); // Not modified
assert.isBelow(insertedDoc.createdAt.getTime() - beginning, 15); // Created assert.isBelow(insertedDoc.createdAt.getTime() - beginning, 30); // Created
d.find({}, function (err, docs) { d.find({}, function (err, docs) {
assert.deepEqual(insertedDoc, docs[0]); assert.deepEqual(insertedDoc, docs[0]);
@ -961,8 +961,8 @@ describe('Database', function () {
var beginning = Date.now(); var beginning = Date.now();
d = new Datastore({ filename: testDb, autoload: true, timestampData: true }); d = new Datastore({ filename: testDb, autoload: true, timestampData: true });
d.insert({ hello: 'world' }, function (err, insertedDoc) { d.insert({ hello: 'world' }, function (err, insertedDoc) {
assert.isBelow(insertedDoc.updatedAt.getTime() - beginning, 15); assert.isBelow(insertedDoc.updatedAt.getTime() - beginning, 30);
assert.isBelow(insertedDoc.createdAt.getTime() - beginning, 15); assert.isBelow(insertedDoc.createdAt.getTime() - beginning, 30);
Object.keys(insertedDoc).length.should.equal(4); Object.keys(insertedDoc).length.should.equal(4);
// Wait 100ms before performing the update // Wait 100ms before performing the update
@ -976,7 +976,7 @@ describe('Database', function () {
docs[0].createdAt.should.equal(insertedDoc.createdAt); docs[0].createdAt.should.equal(insertedDoc.createdAt);
docs[0].hello.should.equal('mars'); docs[0].hello.should.equal('mars');
assert.isAbove(docs[0].updatedAt.getTime() - beginning, 99); // updatedAt modified assert.isAbove(docs[0].updatedAt.getTime() - beginning, 99); // updatedAt modified
assert.isBelow(docs[0].updatedAt.getTime() - step1, 15); // updatedAt modified assert.isBelow(docs[0].updatedAt.getTime() - step1, 30); // updatedAt modified
done(); done();
}); });
@ -1815,19 +1815,19 @@ describe('Database', function () {
; ;
d.getAllData().length.should.equal(0); d.getAllData().length.should.equal(0);
d.ensureIndex({ fieldName: 'z' }, function () {
d.ensureIndex({ fieldName: 'z' }); d.ensureIndex({ fieldName: 'a' }, function () {
d.ensureIndex({ fieldName: 'a' });
d.indexes.a.tree.getNumberOfKeys().should.equal(0); d.indexes.a.tree.getNumberOfKeys().should.equal(0);
d.indexes.z.tree.getNumberOfKeys().should.equal(0); d.indexes.z.tree.getNumberOfKeys().should.equal(0);
fs.writeFile(testDb, rawData, 'utf8', function () { fs.writeFile(testDb, rawData, 'utf8', function () {
d.loadDatabase(function () { d.loadDatabase(function (err) {
var doc1 = _.find(d.getAllData(), function (doc) { return doc.z === "1"; }) var doc1 = _.find(d.getAllData(), function (doc) { return doc.z === "1"; })
, doc2 = _.find(d.getAllData(), function (doc) { return doc.z === "2"; }) , doc2 = _.find(d.getAllData(), function (doc) { return doc.z === "2"; })
, doc3 = _.find(d.getAllData(), function (doc) { return doc.z === "3"; }) , doc3 = _.find(d.getAllData(), function (doc) { return doc.z === "3"; })
; ;
assert.isNull(err);
d.getAllData().length.should.equal(3); d.getAllData().length.should.equal(3);
d.indexes.z.tree.getNumberOfKeys().should.equal(3); d.indexes.z.tree.getNumberOfKeys().should.equal(3);
@ -1845,6 +1845,9 @@ describe('Database', function () {
}); });
}); });
});
});
it('If a unique constraint is not respected, database loading will not work and no data will be inserted', function (done) { it('If a unique constraint is not respected, database loading will not work and no data will be inserted', function (done) {
var now = new Date() var now = new Date()
, rawData = model.serialize({ _id: "aaa", z: "1", a: 2, ages: [1, 5, 12] }) + '\n' + , rawData = model.serialize({ _id: "aaa", z: "1", a: 2, ages: [1, 5, 12] }) + '\n' +

Loading…
Cancel
Save