Skip to content
This repository has been archived by the owner on Feb 26, 2024. It is now read-only.

fix:prevent hang during process exit due to tmp file cleanup #542

Merged
merged 2 commits into from
Jan 27, 2020
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
61 changes: 40 additions & 21 deletions lib/database/filedown.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ var async = require("async");
var fs = require("fs");
var path = require("path");
var tmp = require("tmp");
tmp.setGracefulCleanup();

util.inherits(FileDown, AbstractLevelDOWN);

Expand Down Expand Up @@ -37,6 +36,34 @@ const accessQueue = {
},
cache: {}
};

const fds = new Set();
const cleanup = (exit) => {
try {
fds.forEach((fdPath) => {
const [fd, path] = fdPath;
try {
fs.closeSync(fd);
} catch (e) {
// ignore
} finally {
try {
fs.unlinkSync(path);
} catch (e) {
// ignore
}
}
});
fds.clear();
} finally {
if (exit) {
process.exit(0);
}
}
};
process.on("SIGINT", cleanup);
process.on("exit", () => cleanup(false));

FileDown.prototype._put = function(key, value, options, callback) {
const lKey = path.join(this.location, key);
// This fixes an issue caused by writing AND reading the same key multiple times
Expand All @@ -52,46 +79,38 @@ FileDown.prototype._put = function(key, value, options, callback) {
// leveldb implementation that doesn't use a separate file for every key Soon(TM).
accessQueue.execute(lKey, () => {
// get a tmp file to write the contents to...
tmp.file((err, path, fd, cleanupTmpFile) => {
tmp.file({ keep: true }, (err, path, fd, cleanupTmpFile) => {
if (err) {
callback(err);
accessQueue.next(lKey);
return;
}
const pair = [fd, path];
fds.add(pair);
const cleanupAndCallback = (err) => {
err && cleanupTmpFile();
fds.delete(pair);
callback(err);
accessQueue.next(lKey);
};

// write the value to our temporary file
fs.writeFile(fd, value, "utf8", (err) => {
if (err) {
cleanupTmpFile();
callback(err);
accessQueue.next(lKey);
cleanupAndCallback(err);
return;
}

// It worked! Move the temporary file to its final destination
fs.rename(path, lKey, (err) => {
if (err) {
cleanupTmpFile();
callback(err);
accessQueue.next(lKey);
cleanupAndCallback(err);
return;
}

// make sure we close this file descriptor now that the file is no
// longer "temporary" (because we successfully moved it)
fs.close(fd, (err) => {
if (err) {
// at this point things seem to have worked, but juuuussttt in
// case `fs.close` fails, let's log some info so we can try to
// debug it
console.warn("An unexpected file descriptor close error occured: ", err);
cleanupTmpFile();
}
callback();

// if there is more work to be done on this key, do it.
accessQueue.next(lKey);
});
fs.close(fd, () => cleanupAndCallback());
});
});
});
Expand Down