diff --git a/test_lac/loadAndCrash.test.js b/test_lac/loadAndCrash.test.js new file mode 100644 index 0000000..cbe9bc3 --- /dev/null +++ b/test_lac/loadAndCrash.test.js @@ -0,0 +1,123 @@ +/** + * Load and modify part of fs to ensure writeFile will crash after writing 5000 bytes + */ +var fs = require('fs'); + +function rethrow() { + // Only enable in debug mode. A backtrace uses ~1000 bytes of heap space and + // is fairly slow to generate. + if (DEBUG) { + var backtrace = new Error(); + return function(err) { + if (err) { + backtrace.stack = err.name + ': ' + err.message + + backtrace.stack.substr(backtrace.name.length); + throw backtrace; + } + }; + } + + return function(err) { + if (err) { + throw err; // Forgot a callback but don't know where? Use NODE_DEBUG=fs + } + }; +} + +function maybeCallback(cb) { + return typeof cb === 'function' ? cb : rethrow(); +} + +function isFd(path) { + return (path >>> 0) === path; +} + +function assertEncoding(encoding) { + if (encoding && !Buffer.isEncoding(encoding)) { + throw new Error('Unknown encoding: ' + encoding); + } +} + +var onePassDone = false; +function writeAll(fd, isUserFd, buffer, offset, length, position, callback_) { + var callback = maybeCallback(arguments[arguments.length - 1]); + + if (onePassDone) { process.exit(1); } // Crash on purpose before rewrite done + var l = Math.min(5000, length); // Force write by chunks of 5000 bytes to ensure data will be incomplete on crash + + // write(fd, buffer, offset, length, position, callback) + fs.write(fd, buffer, offset, l, position, function(writeErr, written) { + if (writeErr) { + if (isUserFd) { + if (callback) callback(writeErr); + } else { + fs.close(fd, function() { + if (callback) callback(writeErr); + }); + } + } else { + onePassDone = true; + if (written === length) { + if (isUserFd) { + if (callback) callback(null); + } else { + fs.close(fd, callback); + } + } else { + offset += written; + length -= written; + if (position !== null) { + position += written; + } + writeAll(fd, isUserFd, buffer, offset, length, position, callback); + } + } + }); +} + +fs.writeFile = function(path, data, options, callback_) { + var callback = maybeCallback(arguments[arguments.length - 1]); + + if (!options || typeof options === 'function') { + options = { encoding: 'utf8', mode: 438, flag: 'w' }; // Mode 438 == 0o666 (compatibility with older Node releases) + } else if (typeof options === 'string') { + options = { encoding: options, mode: 438, flag: 'w' }; // Mode 438 == 0o666 (compatibility with older Node releases) + } else if (typeof options !== 'object') { + throwOptionsError(options); + } + + assertEncoding(options.encoding); + + var flag = options.flag || 'w'; + + if (isFd(path)) { + writeFd(path, true); + return; + } + + fs.open(path, flag, options.mode, function(openErr, fd) { + if (openErr) { + if (callback) callback(openErr); + } else { + writeFd(fd, false); + } + }); + + function writeFd(fd, isUserFd) { + var buffer = (data instanceof Buffer) ? data : new Buffer('' + data, + options.encoding || 'utf8'); + var position = /a/.test(flag) ? null : 0; + + writeAll(fd, isUserFd, buffer, 0, buffer.length, position, callback); + } +}; + + + + +// End of fs modification +var Nedb = require('../lib/datastore.js') + , db = new Nedb({ filename: 'workspace/lac.db' }) + ; + +db.loadDatabase(); diff --git a/test_lac/openFds.test.js b/test_lac/openFds.test.js new file mode 100644 index 0000000..e023c5b --- /dev/null +++ b/test_lac/openFds.test.js @@ -0,0 +1,67 @@ +var fs = require('fs') + , child_process = require('child_process') + , [AsyncWaterfall, AsyncWhilst] = [require('async/waterfall'), require('async/whilst')] + , Nedb = require('../nedb/datastore') + , db = new Nedb({ filename: './workspace/openfds.db', autoload: true }) + , N = 64 // Half the allowed file descriptors + , i, fds + ; + +function multipleOpen (filename, N, callback) { + AsyncWhilst( function () { return i < N; } + , function (cb) { + fs.open(filename, 'r', function (err, fd) { + i ++; + if (fd) { fds.push(fd); } + return cb(err); + }); + } + , callback); +} + +AsyncWaterfall([ + // Check that ulimit has been set to the correct value + function (cb) { + i = 0; + fds = []; + multipleOpen('./test_lac/openFdsTestFile', 2 * N + 1, function (err) { + if (!err) { console.log("No error occured while opening a file too many times"); } + fds.forEach(function (fd) { fs.closeSync(fd); }); + return cb(); + }) + } +, function (cb) { + i = 0; + fds = []; + multipleOpen('./test_lac/openFdsTestFile2', N, function (err) { + if (err) { console.log('An unexpected error occured when opening file not too many times: ' + err); } + fds.forEach(function (fd) { fs.closeSync(fd); }); + return cb(); + }) + } + // Then actually test NeDB persistence +, function () { + db.remove({}, { multi: true }, function (err) { + if (err) { console.log(err); } + db.insert({ hello: 'world' }, function (err) { + if (err) { console.log(err); } + + i = 0; + AsyncWhilst( function () { return i < 2 * N + 1; } + , function (cb) { + db.persistence.persistCachedDatabase(function (err) { + if (err) { return cb(err); } + i ++; + return cb(); + }); + } + , function (err) { + if (err) { console.log("Got unexpected error during one peresistence operation: " + err); } + } + ); + + }); + }); + } +]); + diff --git a/test_lac/openFdsLaunch.sh b/test_lac/openFdsLaunch.sh new file mode 100755 index 0000000..495c8a3 --- /dev/null +++ b/test_lac/openFdsLaunch.sh @@ -0,0 +1,2 @@ +ulimit -n 128 +node ./test_lac/openFds.test.js diff --git a/test_lac/openFdsTestFile b/test_lac/openFdsTestFile new file mode 100644 index 0000000..fcd87b0 --- /dev/null +++ b/test_lac/openFdsTestFile @@ -0,0 +1 @@ +Random stuff diff --git a/test_lac/openFdsTestFile2 b/test_lac/openFdsTestFile2 new file mode 100644 index 0000000..3124b92 --- /dev/null +++ b/test_lac/openFdsTestFile2 @@ -0,0 +1 @@ +Some other random stuff