Skip to content

add fs.mkstemp (lib_uv recently landed uv_fs_mkstemp #21) #33890

@kaizhu256

Description

@kaizhu256

Is your feature request related to a problem? Please describe.
Please describe the problem you are trying to solve.
i have a build-system (in debian-docker under windows) that async-updates files w/ multiple child_processes doing fs.writeFile, fs.unlink, fs.link.
there are occasional race-conditions (i can't discern how) where one child_process writing to a file while another child_process unlinking-and-hardlinking ends up with file unlinked.

Describe the solution you'd like
Please describe the desired behavior.
mkstemp would make file-writes "atomic" by writing to tmpfile and then "atomically" rename it to destination. this might resolve the race condition from my multiple child_processes and allow the final hardlink to succeed.

ideal solution of atomic file-write with mkstemp:

/*jslint browser, node*/
/*global os*/
function fsWriteFileWithMkdirpAtomic(pathname, data, onError) {
/*
 * this file with async-atomically write <data> to <pathname> with auto mkdir -p
 */
    let fs;
    let path;
    let throwOnError;
    throwOnError = function (err) {
    /*
     * this function will throw <err> if exists
     * we typically want program to crash on <err> in ci/build use-case
     */
        if (err) {
            throw err;
        }
    };
    try {
        fs = require("fs");
        path = require("path");
        // win32-compat
        pathname = path.resolve(pathname);
    } catch (ignore) {
        // do nothing in browser-env
        onError();
        return;
    }
    fs.mkstemp(os.tmpdir(), function (err, tmpfile) {
        throwOnError(err);
        fs.writeFile(tmpfile, data, function (err) {
            throwOnError(err);
            // fs.rename attempt #1
            fs.rename(tmpfile, pathname, function (err) {
                if (!err) {
                    onError();
                    return;
                }
                // mkdir -p
                fs.mkdirp(path.dirname(pathname), {
                    recursive: true
                }, function (ignore) {
                    // fs.rename attempt #2
                    fs.rename(tmpfile, pathname, function (err) {
                        throwOnError(err);
                        onError();
                    });
                });
            });
        });
    });
}
fsWriteFileWithMkdirpAtomic("aa/bb/cc/dd/foo", "hello world", function () {
    console.log("successfully wrote file atomically with no race-condition");
});

Describe alternatives you've considered
Please describe alternative solutions or features you have considered.

  • use a random generator to create tmpfile in os.tmpdir(), which is not idead for obvious reasons.
  • or use fs.mkdtemp and deal with cleaninup tmpdir after atomic-write, which again, not ideal.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions