diff --git a/browser-version/browser-specific/lib/storage.js b/browser-version/browser-specific/lib/storage.js index 86b46400..8d94a71f 100755 --- a/browser-version/browser-specific/lib/storage.js +++ b/browser-version/browser-specific/lib/storage.js @@ -81,6 +81,10 @@ function ensureDatafileIntegrity (filename, callback) { return callback(null); } +// Nothing to do, no data corruption possible in the brower +function waitForLock (filename, callback) { + return callback(null); +} // Interface module.exports.exists = exists; @@ -92,4 +96,4 @@ module.exports.readFile = readFile; module.exports.unlink = unlink; module.exports.mkdirp = mkdirp; module.exports.ensureDatafileIntegrity = ensureDatafileIntegrity; - +module.exports.waitForLock = waitForLock; diff --git a/browser-version/out/nedb.js b/browser-version/out/nedb.js index e9655bf9..762150ad 100755 --- a/browser-version/out/nedb.js +++ b/browser-version/out/nedb.js @@ -3382,6 +3382,11 @@ function ensureDatafileIntegrity (filename, callback) { return callback(null); } +// Nothing to do, no data corruption possible in the brower +function waitForLock (filename, callback) { + return callback(null); +} + // Interface module.exports.exists = exists; @@ -3393,6 +3398,7 @@ module.exports.readFile = readFile; module.exports.unlink = unlink; module.exports.mkdirp = mkdirp; module.exports.ensureDatafileIntegrity = ensureDatafileIntegrity; +module.exports.waitForLock = waitForLock; },{"localforage":18}],13:[function(require,module,exports){ diff --git a/lib/storage.js b/lib/storage.js index 128f9cc0..6a4183d7 100755 --- a/lib/storage.js +++ b/lib/storage.js @@ -8,6 +8,7 @@ */ var fs = require('fs') + , lockfile = require('lockfile') , mkdirp = require('mkdirp') , async = require('async') , path = require('path') @@ -21,7 +22,8 @@ storage.unlink = fs.unlink; storage.appendFile = fs.appendFile; storage.readFile = fs.readFile; storage.mkdirp = mkdirp; - +storage.lock = lockfile.lock; +storage.unlock = lockfile.unlock; /** * Explicit name ... @@ -73,6 +75,19 @@ storage.flushToStorage = function (options, callback) { }; +storage.waitForLock = function(filename, cb) { + var callback = cb || function () {}; + + storage.lock(filename, {retries: 5, retriesWait: 1}, function(err) { + if (err) { + storage.waitForLock(filename, cb); + return; + } + + cb(); + }); +}; + /** * Fully write or rewrite the datafile, immune to crashes during the write operation (data will not be lost) * @param {String} filename @@ -81,28 +96,33 @@ storage.flushToStorage = function (options, callback) { */ storage.crashSafeWriteFile = function (filename, data, cb) { var callback = cb || function () {} - , tempFilename = filename + '~'; - - async.waterfall([ - async.apply(storage.flushToStorage, { filename: path.dirname(filename), isDir: true }) - , function (cb) { - storage.exists(filename, function (exists) { - if (exists) { - storage.flushToStorage(filename, function (err) { return cb(err); }); - } else { - return cb(); + , tempFilename = filename + '~' + , lockFilename = filename + '.lock'; + + storage.waitForLock(lockFilename, + function() { + async.waterfall([ + async.apply(storage.flushToStorage, { filename: path.dirname(filename), isDir: true }) + , function (cb) { + storage.exists(filename, function (exists) { + if (exists) { + storage.flushToStorage(filename, function (err) { return cb(err); }); + } else { + return cb(); + } + }); } - }); - } - , function (cb) { - storage.writeFile(tempFilename, data, function (err) { return cb(err); }); - } - , async.apply(storage.flushToStorage, tempFilename) - , function (cb) { - storage.rename(tempFilename, filename, function (err) { return cb(err); }); + , function (cb) { + storage.writeFile(tempFilename, data, function (err) { return cb(err); }); + } + , async.apply(storage.flushToStorage, tempFilename) + , function (cb) { + storage.rename(tempFilename, filename, function (err) { return cb(err); }); + } + , async.apply(storage.flushToStorage, { filename: path.dirname(filename), isDir: true }) + ], function (err) { storage.unlock(lockFilename); return callback(err); }) } - , async.apply(storage.flushToStorage, { filename: path.dirname(filename), isDir: true }) - ], function (err) { return callback(err); }) + ); }; @@ -112,23 +132,35 @@ storage.crashSafeWriteFile = function (filename, data, cb) { * @param {Function} callback signature: err */ storage.ensureDatafileIntegrity = function (filename, callback) { - var tempFilename = filename + '~'; - - storage.exists(filename, function (filenameExists) { - // Write was successful - if (filenameExists) { return callback(null); } - - storage.exists(tempFilename, function (oldFilenameExists) { - // New database - if (!oldFilenameExists) { - return storage.writeFile(filename, '', 'utf8', function (err) { callback(err); }); - } + var tempFilename = filename + '~' + , lockFilename = filename + '.lock'; + + storage.waitForLock(lockFilename, + function() { + storage.exists(filename, function (filenameExists) { + // Write was successful + if (filenameExists) { + return callback(null); + } - // Write failed, use old version - storage.rename(tempFilename, filename, function (err) { return callback(err); }); - }); - }); -}; + storage.exists(tempFilename, function (oldFilenameExists) { + // New database + if (!oldFilenameExists) { + return storage.writeFile(filename, '', 'utf8', function (err) { + callback(err); + }); + } + + // Write failed, use old version + storage.rename(tempFilename, filename, function (err) { + return callback(err); + }); + }); + }); + storage.unlock(lockFilename); + } + ); +} diff --git a/package.json b/package.json index 8ee7e4fb..10900753 100755 --- a/package.json +++ b/package.json @@ -23,6 +23,7 @@ "async": "0.2.10", "binary-search-tree": "0.2.5", "localforage": "^1.3.0", + "lockfile": "^1.0.4", "mkdirp": "~0.5.1", "underscore": "~1.4.4" },