Repository navigation
fs: behaviour of readFile and writeFile with file descriptors #23433
Description
Activity
- addedfsIssues and PRs related to file-system APIs and the fs module.Issues and PRs related to file-system APIs and the fs module.discussIssues opened for discussion and feedback.Issues opened for discussion and feedback.
on Oct 11, 2018 Another option:
- Remove fd support, its just a helper function for a not so oftenly used use-case, and there are valid reasons to support both seeking and non-seeking, so whatever we choose someone will need to write their own wrapper to make functions that work as they expect. Let there be an npm package to do this, or several.
See also #22554 for various behaviour of
fs.writeFile()variants (sync / callback / promise ). Promise variant adds even more discrepancy: complete rewriting vs positional rewriting vs appending.As for a use case for
fs.writeFile(fd). Say, I have a long process with many iterative tasks. After each task, the script writes a current task identifier to a state file, to save a progress and to be able to restore the state. This identifier should be overwritten, not appended as in a log file, so that it can be easily required not by reading and parsing all the huge log file, but via one smallfs.readFile()call. Using a path would have an overhead of a constant file reopening. But we cannot use a file descriptor in this case for now.Reacted by Sakthipriyan Vairamani@sam-github I included that option in the OP.
@vsemozhetbyt I have included that issue also in the list of references.
Let's start recording what we think would be the expected behaviour of
readFile&writeFilein the table in OP.Reacted by Vse Mozhe ButyI checked few other languages to see how they handle file descriptors.
Python
- Different functions to deal with file descriptors.
- Positional reads and writes have separate functions.
- No read all or write all functions with file descriptors.
Other languages which I checked were Ruby, Java, C, and C++. They all provide their own abstractions over file descriptors. Using file descriptors are not recommended, so not a lot of fd based functions are available in them.
I think step zero should be to update the documentation to specify the current behavior. If we change that behavior, fine, but there should be no reason to not have correct docs while that is being discussed. Is there already a PR open to update the docs? If not, I'm happy to do it or let @thefourtheye or anyone else do it.
Would it be a bit confusing to state 3 different behaviors in docs? Especially callback/sync fd variant (merging) vs promise fd variant (appending)?
Would it be a bit confusing to state 3 different behaviors in docs? Especially callback/sync fd variant (merging) vs promise fd variant (appending)?
It will be a bit confusing, but hopefully less confusing than the current situation of having the same behavior but incorrectly documented.
Reacted by Vse Mozhe Buty, Daniel Bevenius and Michael DawsonI'm for deprecating
readFile(fd)andwriteFile(fd), if it does not break on CITGM. If somebody is deailing with file descriptors, I think they can use the barebone methodsread()andwrite().Reacted by Sakthipriyan Vairamani- addedtsc-agendaIssues and PRs to discuss during Technical Steering Committee meetings.Issues and PRs to discuss during Technical Steering Committee meetings.
on Oct 24, 2018 Adding
tsc-agendalabel per discussion in the TSC meeting today where conclusion was "Get more engagement, particularly from @nodejs/tsc, but leave on agenda in case no consensus is reached."Reacted by Sakthipriyan VairamaniI find all four proposed options acceptable (assuming Option 1 involves documenting the current behavior, which given the open PR, it seems like it does).
26 remaining items
I'd be OK with going the option 1 + warning route through a single major cycle for a pure option 1. I'm concerned about performance implications though, if we need to reach down in to do an lseek to figure out whether a warning is warranted, can we put that in the path in such a way as to not make reads slower? Since here's a bunch of layers of indirection between
writeFile()and the actual operations perhaps this is just going to add too much complication?
Still also fine with just a pure option 1 though, that seems to be consensus here unless anyone else wants to jump to the option 1 + warn bandwagon and put together a PR that implements it?That can't be from our JS api, but perhaps we could use lseek/_lseek internally and at least emit the warning on supported platforms (if not all platforms have that)?
@ChALkeR Seek is not supported by libuv. @bnoordhuis's comment in this thread
lseek() is unpredictable with concurrent readers or writers. The libuv way is to use positional reads and writes
kind of gives a reason why it is not included in libuv. Do you mean, using
*seekto get the details from our C++ layer? I believe that would be very difficult to maintain.We shouldn't emit needless warnings -- writing to an fd with zero offset is fine, that behaviour won't change and doesn't need a warning.
Yes, I agree. However, in this particular case, we don't have a straight forward to decide when to actually warn. Would it be very bad if we warn people once who use these functions with file descriptors?That said,#23709 is going to write from the current file position always, it doesn't matter what that is. So, if someone is usingwriteFilewith a file descriptor, warning them saying "data is going to be written from the current file position instead of the current behaviour which is to write from position zero", would be okay, right?This seems to be progressing to a conclusion. I'm going to remove the
tsc-agendalabel (since I'm the one who added it). Feel free to re-add it if you think it should stay on or go back on the agenda.Reacted by Sakthipriyan Vairamani and Nikita Skovoroda- removedtsc-agendaIssues and PRs to discuss during Technical Steering Committee meetings.Issues and PRs to discuss during Technical Steering Committee meetings.
on Nov 13, 2018 I think seek is needed #24923
Reacted by tcme@kghost The outcome of this issue is that
readFileandwriteFilewill not seek and read/write from the current file position. You can always read and write from specific positions of a file withfs.readandfs.writefunctions.@thefourtheye Sorry for the delay 😞 .
After further thoughts about this, I withdraw my suggestion — the increased maintaining costs indeed would not pay off here.
Reacted by Sakthipriyan VairamaniOut of the 18 TSC members (including @Trott), 12 have expressed their opinions in the table in the OP. With six abstentions and nine votes for Option 1, Option 1 wins. @nodejs/tsc please let me know, if there are any objections here.
Thanks everyone for participating and driving this issue to closure.
- added a commit that references this issue
on Jan 7, 2019 - added a commit that references this issue
on Jan 14, 2019
This problem has been discussed multiple times without reaching any conclusion. That is why opening a separate issue to discuss.
Documented Expectations
As per our docs
readFileis defined asand
writeFileis defined asCurrent State of
readFile&writeFile:When a regular file path is passed:
The file is either read from the beginning or completely overwritten. This complies with the documentation.
When a file descriptor is passed:
readFileThe file is read from the current position in the file (the current position is maintained by the file descriptor itself).
readFile problem reproduction
writeFileWrites from the beginning of the file, without replacing the contents of the file. For example, if the existing content in the file is
ABCDand the newly written data is12, then the actual contents of the file after both thewriteFiles is12CD.writeFile problem reproduction
These two behaviours deviate from the documented expectations.
Cases for changing the behaviour to work from the beginning
#9671 (It presents the case with
readFile)Cases for keeping the current behaviour
Its intuitive for people to expect read/write from the current position till the end of the file. (For example, read a header from a file and then decide whether to read the rest of the file or not.)
Proposed courses of action
writeFilealso to write from the current file position just likereadFilereads from the current file position, and be done with it. (semver-major)readFileandwriteFileto work from the beginning of the file. (semver-major)a. If the file descriptor is not seekable, an error will be thrown from libuv.
This is not an exhaustive list. Please feel free to propose more ideas.
PS: This comment by @sam-github summarises my personal expectations from the API.
XXXXXXXXXX+ warn *XXXXXXX91124cc @nodejs/fs
Tagging @nodejs/tsc (Might be a bit early, but this has been discussed before. So tagging to get more inputs)
Tagging @seishun @vsemozhetbyt @sam-github @jorangreef @addaleax from the previous discussions.
If needed we can tag the collaborators to get their expectations.
Previous discussions on this topic happened in #9671, #10809, #10853, #13572,
#22554