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
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions