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.
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.
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?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()!And FYI: Even if they're not the same descriptor, you still have risk.
No it doesn't.
The spec† says: If the fsync() function fails, outstanding I/O operations are not guaranteed to have been completed. That clearly permits the Linux behaviour; There is no "data" that hasn't been sent to the "transferred to the storage device" at this point. You can see it from the perspective of the Linux kernel:
write() = success
fsync() = error: outstanding I/O not guaranteed††.
fsync() = success: no outstanding I/O
†: http://pubs.opengroup.org/onlinepubs/9699919799/functions/fs...††: It would've been a different story if the spec said, If "the fsync() function fails, all future fsync() to the same file descriptor must fail." but it doesn't say this.
Apologies for assuming you didn't understand the behaviour when you said it "doesn't make sense": I'd assumed you meant that you were having trouble understanding the behaviour, rather than "all the application developers and unix implementors" that expect this behaviour, not understanding "the spec".
The 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.
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...
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.
"Nothing" in the doc? More like everything in the doc? It literally says "all data for the open file descriptor is to be transferred" and "If the fsync() function fails, outstanding I/O operations are not guaranteed to have been completed." Together these are literally saying that all data that wasn't transferred due to a failure in one call will be transferred in the next successful call.
I could maybe buy it if the doc described a notion of "dequeueing" that was separate from writing, but it doesn't. It just talks about queued operations completing. So either they complete successfully (and are subsequently dequeued because that is common sense) or they don't.
Like if your boss had assigned you tasks A, B, and then C, and then he ordered you to finish all your tasks, and you failed to do B, and then he made the same request again, you wouldn't then suddenly turn to him and say "I have nothing to do because I already popped B and C off my to-do list". You'd get fired for that (at least if you persisted) because it literally wouldn't make sense.
https://news.ycombinator.com/item?id=19133714
You are equating "transferred" with "completed" but they are clearly not the same.
All the data was transferred. After transfer, the data on the device may have been written or may have been lost. The OS doesn't remember what has been transferred after the transfer - there is no language about this anywhere in this text.