-
Notifications
You must be signed in to change notification settings - Fork 4.8k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
FileStream.Flush(true) doesn't flush device buffer on macOS, while it does so on Windows #28444
Comments
From what I can tell, |
I'll take a look at this one. As far as I see, even though a simple fsync with the F_FULLFSYNC parameter could be enough, although fnctl provides a better guarantee. According to OSX' man fsync man page:
Since we don't know what application is being written, I tend to agree on @maxim-saplin's thoughts on using fcntl. One footnote there is that fcntl may not guarantee this on APFS and that it may have a performance impact.
Are we sure we want to include this? Or maybe make it optional? Then there's the question if we're going to solve it in the Unix interop using an |
I think it is the right thing to do, but I'm totally open to counter arguments. Consistency with legacy behavior here has been the main design goal.
Maybe in the future we should allow dialing this in?
That would be where I would start. It's the common pattern. I presume we can look for |
Has this caused real problems for apps using .NET Core? We shouldn't make it significantly slower to achieve "consistency with legacy behavior" that's affecting very few people. |
@stephentoub it did cause trouble with my perf. measuring app when on Windows I noticed that Flush() and Flush(true) show 10x difference in performance while there was not difference on macOS. Though I might agree that scenarios where this inconsistency can be critical are very rare. If the IO part would require solid overhaul fixing this issue is not worth introducing new ones. |
This is a key part of my concern: it's not actually pay for play as you suggest, because FlushAsync() does this. If we change that, such that the only way you pay this cost is if you opt in via Flush(true), then I'm fine with it. But that's obviously a behavioral change as well. |
@maxim-saplin @stephentoub imho this change should make it even though it makes it slower. Setting the true bit, means you probably do want to ensure it's synced to disk and don't want the 'loosey goosey' promise that OSX currently makes with an fsync. I'd say that if people already expect that performance hit (because they explicitly chose to set the bit), it won't be a very big issue. Maybe the behavior change can be implemented when FileStream is revised as @JeremyKuhne implicated would happen? |
As I mentioned, I'm ok with the perf hit on macOS when explicitly calling Flush(true). I'm more concerned about it when just using FlushAsync(). |
Ah, I missed that one. Yes, that would be an issue - especially if you're actually awaiting that flush. What do you propose here? We could create a new explicit FlushAsync(FlushToDisk) version but that would imply changing the existing behavior. This wouldn't be my first choice. The other option I see is to change the InterOp behavior of Flush(true). This however would imply either changing behavior for all UNIX systems to use Fcntl in favor of FSync or moving the function to a Linux and OSX specific version. Option two would be my preferred option. |
The viable options as I see them are:
For (2), the reasoning descriped in the comment in the src for why it's ok to do so with FlushAsync() but not Flush() seems flawed. And as to my knowledge there's no asynchronous equivalent at the OS level exposed for flushToDisk==true, I wouldn't want to expose a FlushAsync(true)… if a developer wants to Task.Run(() => Flush(true)), they can do so. My preference is (2). We just need to convince ourselves that we're ok in a major release taking the behavioral change that FlushAsync no longer has the stronger flushToDisk semantics. |
Right, am back home. Gave it some thought after seeing your response and I guess that might be the best option. I am living a bit in the past where I'm still weary of breaking changes but with .NET core that really isn't the same anymore :) I've implemented the change but am seeing some unexpected test failures which I'm trying to pinpoint. Will update as soon as I've solved that. |
@ptoonen are you still working on it ? |
@Anipik yes I am, but am only slowly making progress here. One issue at a time. I hope to have it working somewhere within the next two weeks. |
okay sounds good |
@ptoonen how is it going? Just wondering whether it misses 3.0 at this point. |
I’m going to give it one more go on Sunday but keep running into new issues as soon as I’ve fixed one. I’m starting to wonder whether it’s my device...
On 31 May 2019, at 17:44, Dan Moseley <notifications@github.com<mailto:notifications@github.com>> wrote:
@ptoonen<https://github.com/ptoonen> how is it going? Just wondering whether it misses 3.0 at this point.
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub<https://github.com/dotnet/corefx/issues/34640?email_source=notifications&email_token=AB5JXOOR74QBKXVJS3QDL23PYFBUFA5CNFSM4GQURUD2YY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW2ZLOORPWSZGODWVS5KY#issuecomment-497757867>, or mute the thread<https://github.com/notifications/unsubscribe-auth/AB5JXONYCRKAVLVS4H4EYFDPYFBUFANCNFSM4GQURUDQ>.
|
@stephentoub you had a concern above about (2) being potentially a breaking change. We do have one more preview left in which breaking changes are possible - Preview 7. But it's the last one. Say we take this change in a week, would you feel that we have sufficient runway to validate this change is not unacceptably breaking before final release? |
@danmosemsft, it's "breaking" from a machine-wide reliability perspective: we'd stop forcing a flush to disk in a case where we were previously doing so. No amount of previews are going to tell us whether that's a problem or not. We just have to make a call. I'm ok with it. I think it's a mistake that FlushAsync was previously doing the same thing as Flush(true) when Flush() is equivalent to Flush(false). I will go ahead and make the FlushAsync change now, separating it from this issue. |
I did not succeed today. Although the change seems incredibly easy (and I think it may be), there are some tests that fail where I don't expect them to. If anyone else has any ideas, I'd gladly hear them. Otherwise I'm afraid I'm going to have to let this one go. |
@ptoonen, which tests? Can you share your branch? Someone might be able to pick up where you left off. Thanks! |
@ptoonen are you still working on this? Do you have a branch you can share? |
@ptoonen I'll remove the assignment. If you get a chance to take a look at this again, feel free to reassign the issue to yourself. |
@stephentoub I would personally prefer suggestion 3 over 2; that is keep using fsync for FlushAsync() and change Flush(true) to use fcntl(F_FULLFSYNC). Wouldn't be better to just avoid the breaking change in FlushAsync? I guess the downside would be that we wouldn't be consistent between sync and async. |
The consistency is key. Stream.Flush() / FlushAsync() should be functionally equivalent except for the one piece that one does sync writes and the other async writes. They shouldn't have different levels of strength / reliability. But more importantly, FlushAsync() doing the equivalent of Flush(true) (which is not part of Stream) means it's failing at its one goal, to be async. |
Actually, I already fixed that part, a year and a half ago: This issue is now just about making Flush(true) behave however is desired. |
Actual:
FileStream.Flush(OS) calls fsync() on macOS, on Windows it calls FlushFileBuffers().
fsync() - doesn't force device's write buffer flush, Window's FlushFileBuffers() does.
Expected:
macOS's implementation of the Flush() method forces device's buffer flush via fcntl(F_FULLFSYNC)
The text was updated successfully, but these errors were encountered: