Skip to content

Instantly share code, notes, and snippets.

@hyuni
Forked from jorangreef/test-watch.js
Last active December 19, 2015 12:17
Show Gist options
  • Save hyuni/3a676c4fa31bd7a2351f to your computer and use it in GitHub Desktop.
Save hyuni/3a676c4fa31bd7a2351f to your computer and use it in GitHub Desktop.
Test the reliability and average and max latency of the underlying OS notification system used by Node's fs.watch. Uses fsync to flush the filesystem cache to make sure these don't delay notifications (this makes little to no difference).
var Node = {
child: require('child_process'),
fs: require('fs'),
path: require('path')
};
// This will be removed and then created and then removed:
var testdirectory = 'testfswatchmisses';
if (Node.fs.remove !== undefined) throw new Error('fs.remove exists');
Node.fs.remove = function(target, end) {
Node.fs.lstat(target,
function(error, stats) {
if (error && error.code == 'ENOENT') return end();
if (error) return end(error);
if (stats.isFile() || stats.isSymbolicLink()) {
Node.fs.unlink(target, end);
} else if (stats.isDirectory()) {
Node.fs.readdir(target,
function(error, paths) {
if (error) return end(error);
var index = 0;
function remove(error) {
if (error) return end(error);
if (index === paths.length) {
return Node.fs.rmdir(target, end);
}
var path = paths[index++];
Node.fs.remove(Node.path.join(target, path), remove);
}
remove();
}
);
} else {
end('Unsupported file type: ' + target);
}
}
);
};
Node.fs.remove(testdirectory,
function(error) {
if (error) throw error;
Node.fs.mkdirSync(testdirectory);
Node.fs.mkdirSync(Node.path.join(testdirectory, 'a'));
Node.fs.mkdirSync(Node.path.join(testdirectory, 'a', 'b'));
Node.fs.mkdirSync(Node.path.join(testdirectory, 'a', 'b', 'c'));
Node.fs.mkdirSync(Node.path.join(testdirectory, 'a', 'b', 'c', 'd'));
var relative = Node.path.join('a', 'b', 'c', 'd');
var produced = {};
var observed = {};
var watch = Node.fs.watch(testdirectory, { recursive: true });
watch.on('change',
function(change, binaryPath) {
if (!observed[binaryPath]) {
observed[binaryPath] = Date.now();
}
}
);
watch.on('error',
function(error) {
console.log('ERROR ' + error);
}
);
function makeBuffer() {
var size = Math.floor(Math.random() * 1000000);
var buffer = new Buffer(size);
return buffer;
}
var number = 0;
var length = 10000;
function report() {
watch.close();
var count = 0;
var missed = 0;
var sum = 0;
var max = 0;
for (var key in produced) {
var a = produced[key];
count++;
if (observed.hasOwnProperty(key)) {
var b = observed[key];
var latency = Math.max(0, b - a);
if (latency > max) max = latency;
sum += latency;
} else {
missed++;
}
}
console.log('Produced ' + count + ' event(s)');
console.log('Missed ' + missed + ' event(s)');
console.log(Math.round(sum / (count || 1)) + 'ms average latency');
console.log(max + 'ms max latency');
Node.fs.remove(testdirectory, function() {});
}
function fsyncDirectory(path, end) {
var flags = (process.platform === 'win32') ? 'r+' : 'r';
Node.fs.open(path, flags,
function(error, fd) {
if (error) throw error;
Node.fs.fsync(fd,
function(error) {
if (error) throw error;
Node.fs.close(fd, end);
}
);
}
);
}
function rename(source) {
setTimeout(
function() {
var target = 'renamed ' + source;
produced[Node.path.join(Node.path.dirname(relative), target)] = Date.now();
source = Node.path.join(testdirectory, relative, source);
target = Node.path.join(testdirectory, Node.path.dirname(relative), target);
Node.fs.rename(source, target,
function(error) {
if (error) throw error;
fsyncDirectory(Node.path.dirname(source), function() {});
fsyncDirectory(Node.path.dirname(target), function() {});
}
);
},
Math.floor(Math.random() * 10000)
);
}
function write() {
if (number === length) return setTimeout(report, 60000);
var name = 'update ' + (++number).toString();
var target = Node.path.join(testdirectory, relative, name);
Node.fs.open(target, 'wx',
function(error, fd) {
if (error) throw error;
writeFile(fd,
function(error) {
if (error) throw error;
Node.fs.fsync(fd,
function(error) {
if (error) throw error;
produced[Node.path.join(relative, name)] = Date.now();
Node.fs.close(fd,
function(error) {
if (error) throw error;
fsyncDirectory(Node.path.dirname(target), function() {});
write();
rename(name);
}
);
}
);
}
);
}
);
}
function writeFile(fd, end) {
if (Math.random() < 0.5) return end();
var buffer = makeBuffer();
Node.fs.writeFile(fd, buffer,
function(error) {
if (error) throw error;
setTimeout(
function() {
writeFile(fd, end);
},
0
);
}
);
}
console.log('Writing and renaming ' + length + ' file(s), this may take awhile...');
write();
}
);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment