Get Even More Visitors To Your Blog, Upgrade To A Business Listing >>

How can I synchronize access to a file within a single process in node.js?

How can I synchronize access to a file within a single process in node.js?

Problem

I have a Web server that reads and writes to a data file on disk. I'd like a file only be written to in a single Web request.

Here's an example program that illustrates my problem. It keeps a state file in "/tmp/rw.txt" and increments the integer contents on each Web hit. Running this program, and then running something like ab -n 10000 -c 1000 http://localhost:3000/, shows that the same value is read from the file by multiple hits, and it's written multiple times.

NOTE: I know about flock() and fs-ext. However, flock() will lock the file to the current process; since all the access here is in the same process, flock() doesn't work (and complicates this example considerably).

Also note that I'd usually use express, async, etc. to get most of this done; just sticking to the basics for the sake of example.

var http = require("http"),
    fs = require("fs");

var stateFile = "/tmp/rw.txt";

var server = http.createServer(function(req, res) {

    var writeNum = function(num) {
        var ns = num.toString(10);
        console.log("Writing " + ns);
        fs.writeFile(stateFile, ns, function(err) {
            if (err) {
                res.writeHead(500, {"Content-Type": "text/plain"});
                res.end(err.message);
            } else {
                res.writeHead(200, {"Content-Type": "text/plain"});
                res.end(ns);
            }
        });
    };

    switch (req.url) {
    case "/reset":
        writeNum(0);
        break;
    case "/":
        fs.readFile(stateFile, function(err, data) {
            if (err && err.code == "ENOENT") {
                // First time, set it to zero
                writeNum(0);
            } else if (err) {
                res.writeHead(500, {"Content-Type": "text/plain"});
                res.end(err.message);
            } else {
                writeNum(parseInt(data, 10) + 1);
            }
        });
        break;
    default:
        res.writeHead(404, {"Content-Type": "text/plain"});
        res.end("No such resource: " + req.url);
    }
});

server.listen(3000);
Problem courtesy of: Evan P.

Solution

I wasn't able to find a library that did what I want, so I created one here:

https://npmjs.org/package/schlock

Here's the above example program using the read/write locking. I also used "Step" to make the whole thing more readable.

var http = require("http"),
    fs = require("fs"),
    Schlock = require("schlock"),
    Step = require("step");

var stateFile = "/tmp/rw.txt";

var schlock = new Schlock();

var server = http.createServer(function(req, res) {

    var num;

    Step(
        function() {
            schlock.writeLock(stateFile, this);
        },
        function(err) {
            if (err) throw err;
            fs.readFile(stateFile, this);
        },
        function(err, data) {
            if (err && err.code == "ENOENT") {
                num = 0;
            } else if (err) {
                throw err;
            } else {
                num = parseInt(data, 10) + 1;
            }
            fs.writeFile(stateFile, num.toString(10), this);
        },
        function(err) {
            if (err) throw err;
            schlock.writeUnlock(stateFile, this);
        },
        function(err) {
            if (err) {
                res.writeHead(500, {"Content-Type": "text/plain"});
                res.end(err.message);
            } else {
                res.writeHead(200, {"Content-Type": "text/plain"});
                res.end(num.toString(10));
            }
        }
    );
});

server.listen(3000);
Solution courtesy of: Evan P.

Discussion

View additional discussion.



This post first appeared on Node.js Recipes, please read the originial post: here

Share the post

How can I synchronize access to a file within a single process in node.js?

×

Subscribe to Node.js Recipes

Get updates delivered right to your inbox!

Thank you for your subscription

×