38 ms·
> Any dev who actually RTFM'd and followed POSIX specs would have been fine. So I'm trying to do that [1] and it seems to me the documentation directly implies
by mehrdadn 8y ago
> Any dev who actually RTFM'd and followed POSIX specs would have been fine.
So I'm trying to do that [1] and it seems to me the documentation directly implies that a second successful call to fsync() necessarily implies that all data was transferred, even if the previous call to fsync() had failed.
I say this because the sentence says "all data for the open file descriptor" is to be transferred, not merely "all data written since the previous fsync to this file descriptor". It follows that any data not transferred in the previous call due to an error ("outstanding I/O operations are not guaranteed to have been completed") must now be transferred if this call is successful.
What am I missing?
[1] https://pubs.opengroup.org/onlinepubs/009695399/functions/fsync.html https://pubs.opengroup.org/onlinepubs/009695399/functions/fs...
- hyc_symas 8y agoTechnically, there is nothing besides "all data written since the previous fsync" since the previous fsync already wrote all the outstanding data at that point in time. (Even if it actually failed.) I.e., there is never any to-be-written data remaining after fsync returns. Everything that was queued to be flushed was flushed and dequeued. Whether any particular write failed or not doesn't change that fact.
- mehrdadn 8y ago> the previous fsync already wrote all the outstanding data at that point in time. (Even if it actually failed.) I'm sorry, but this... just doesn't make sense. An event can't both happen and also fail to happen. Also, the queue business is an implementation detail. Our debate is over what the spec mandates, not how a particular implementation behaves.
- hyc_symas 8y agoThe fsync() function shall request that all data for the open file descriptor named by fildes is to be transferred to the storage device associated with the file described by fildes. The nature of the transfer is implementation-defined. The fsync() function shall not return until the system has completed that action or until an error is detected. All of the data was transferred to the device. The device may have failed to persist some or all of what was transferred, but it all got transferred. There's no mention of the OS retrying, or leaving the system in a state that a subsequent fsync can retry from where it left off. So you can't assume anything along those lines.
- mehrdadn 8y agoSo you're suggesting "storage device" here isn't the same thing as "storage medium"? i.e. that merely transferring the data to a volatile buffer on the device is also considered being transferred to the "storage device", rather than to the (nonvolatile) storage medium? By that logic even after a successful call you can't rely on the data being persisted, which goes squarely against the entire point of the function and its documentation: "The fsync() function is intended to force a physical write of data from the buffer cache, and to assure that after a system crash or other failure that all data up to the time of the fsync() call is recorded on the disk." So this interpretation is wrong...
- hyc_symas 8y agoNonsense. If the device returns "success" then everything was persisted. If it returns an error, then some/all of it was not persisted. There is no way for you to determine whether it is some or all or which. The only safe action for a user is to assume all of it failed. And by the way, there are devices out there that lie, and claim the data is successfully persisted even though it only made it into a volatile cache.
- mehrdadn 8y agoEverything you said in this comment is consistent with what I've been saying. Nowhere did I suggest the user can assume data was written if an fsync call fails. I'm saying if an fsync call fails, the documentation of fsync (which I quoted multiple times) implies the next call will attempt to write data that wasn't successful it written on the last call.
- hyc_symas 8y agoYeah, and we still disagree there. Nothing in the doc implies that just calling fsync() again will do anything useful, there is no implication of retryability.
- 8y ago
- geocar 8y ago> I'm sorry, but this... just doesn't make sense. An event can't both happen and also fail to happen. Consider a multithreaded application where a and b point to the same file. Note this isn't exactly what Postgres does: T1 T2 -------------------- -------------------- write(a,x) start write(a,x) success fsync(a) start read(b,x) start read(b,x) success fsync(a) failed write(b,y) start write(b,y) success fsync(b) start fsync(b) success y has a data dependancy on x (for example: table metadata), and the fsync() for y succeeded, so is T2 given to expect that x was recorded on disk correctly?
- mehrdadn 8y agoNo? It says "all data for the open file descriptor", not "all data for all open file descriptors".
- geocar 8y agoThe file descriptors point to the same file and can even be the same descriptor. Maybe this is clearer/simpler, writing x and y to the same offset: T1 T2 -------------------- -------------------- write(b,y) start write(b,y) success write(a,x) start write(a,x) success fsync(a) start fsync(a) failed fsync(b) start fsync(b) success At this point, T2 is probably surprised to discover the original contents of the file on the disk. Even after a successful fsync()!
- mehrdadn 8y agoI'm confused, where is the reading happening that is causing T2 to be surprised? But in any case, I'm saying that if a and b are the same descriptor, then fsync(b)'s success should imply both a and b are written to disk, by the specification of fsync. I don't see where you're seeing a contradiction.