-
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
Allow for atomic appends to end of file #53432
Comments
Tagging subscribers to this area: @carlossanlop Issue DetailsWhile working on a new API called Moreover, we don't use these flags for runtime/src/libraries/System.Private.CoreLib/src/System/IO/Strategies/WindowsFileStreamStrategy.cs Lines 240 to 243 in 33b0f24
Lines 86 to 90 in 33b0f24
Lines 85 to 88 in 33b0f24
I did some quick web search and it turns out that it was possible with .NET Framework: https://stackoverflow.com/questions/1862309/how-can-i-do-an-atomic-write-append-in-c-or-how-do-i-get-files-opened-with-the And some of our users (@nblumhardt) are missing it in .NET Core: https://nblumhardt.com/2016/08/atomic-shared-log-file-writes/ @stephentoub @ericstj @JeremyKuhne is there any chance that you remember why we have decided to implement it in such a way?
|
.NET Framework does the same, for the constructors that exist in both places. .NET Framework also has ctors that accept a FileSystemRights, and that rights information (which has FileSystemRights.AppendData that maps to FILE_APPEND_DATA) is passed down into the CreateFile call. I assume we didn't add those constructors for layering reasons. But .NET Core does have FileSystemAclExtensions.Create which lets you create a FileStream with a FileSystemRights. So, the same APIs do the same thing in both places. And the additional functionality is available in both places, just with a ctor in netfx and a Create method in core. |
@stephentoub is correct. In general we were focused on porting and maintaining behavior early on in .NETCore. Portability and compatibility was very important. We weren't making behavior changes if we could avoid it. ACLs as a whole were not in because they didn't work well cross-plat. @mjrousos initially did a port of all the ACL APIs for Test.NET (an internal set of library implementations that were developed in order to port more test code) and that eventually became the *AccessControl packages in the compat pack. I've had some discussions with @terrajobst and @ViktorHofer lately about adding ACLs back. Now that we have the platform support features to warn people about unsupported API there's less of a downside to do this. Initially we were planning to just add all the *AccessControl packages back to the framework. Hypothetically we can add back the framework API (with SupportedOS attributes) after that, though we'd need to decide if we wanted to put platform specific APIs on our core types. That's a separate discussion though. I am not aware of anything fundamentally blocking the addition of new APIs to support Append, or enabling it through some existing API. |
@adamsitnik Has the merge of #50166 + #51111 + #52928 + #53669 + #55513 made it possible to perform atomic-file-append on Windows and Linux-platforms on Net6.0 ? (Allowing 2 processes to append concurrently to the same file) According to old scriptures then one should set both FILE_APPEND_DATA + SYNCHRONIZE and BufferSize = 1 to enable atomic-append on Windows. But according to other sources then it seems FILE_APPEND_DATA or O_APPEND is enough. |
@snakefoot #55465 was implementing the atomic append support, but it did not get merged as it would introduce a breaking change related to changing |
@adamsitnik Maybe it could be part of the |
@snakefoot @nblumhardt @iSazonov I've re-opened #55465, PTAL and let me know what do you think about #55465 (comment) |
#55465 is still locked for conversations so I leave my comment here. I think the docs say "open file for writing and limit permissions only to add new data to end file". From this I'd expect we can do some append writes before closing the file. |
Background and motivationAs of today, it's currently impossible to perform atomic file appends with .NET. When the file is being opened with existing There is no guarantee that the file length won't be modified in the meantime (EOF might change). Example: using System;
using System.IO;
using System.Text;
using System.Threading;
namespace atomicAppend
{
class Program
{
static void Main()
{
const string FilePath = "logFile.txt";
if (File.Exists(FilePath)) File.Delete(FilePath);
Thread t1 = new Thread(Log), t2 = new Thread(Log);
t1.Start(FilePath); t2.Start(FilePath);
t1.Join(); t2.Join();
}
static void Log(object filePath)
{
using (FileStream fs = new FileStream((string)filePath, FileMode.Append, FileAccess.Write, FileShare.Write, 1, false))
{
for (int i = 0; i < 10; i++)
{
fs.Write(Encoding.Unicode.GetBytes($"{DateTime.UtcNow}: thread {Thread.CurrentThread.ManagedThreadId} iteration {i}\n"));
}
}
}
}
} Sample output:
As you can see, both threads were overwriting the content instead of appending it to end of file. We would expect to have something like:
API Proposalnamespace System.IO
{
public enum FileMode
{
Append = 6,
+ AppendAtomic = 7,
}
} The main difference with
API Usageusing (FileStream fs = new FileStream(filePath, FileMode.AtomicAppend, FileAccess.Write, FileShare.Write))
{
for (int i = 0; i < 10; i++)
{
fs.Write(Encoding.Unicode.GetBytes($"{DateTime.UtcNow}: thread {Thread.CurrentThread.ManagedThreadId} iteration {i}\n"));
}
} RisksI can't see any. |
How will FileShare work with AppendAtomic? |
|
If I understand that correctly, the "normal" assumption is that Append would be atomic? I admit that a breaking change would be rather harsh if you throw exceptions for seek etc., as probably every logger in existence will then break. |
It would be doable (#55465), but at a cost of introducing a breaking change to |
My question was tricky :-). I am thinking if the weird behavior with Append comes from multi-threaded scenario we could consider add new option in FileShare enum so that lock other threads/processes if needed. |
Will it throw if the buffer size is specified? |
Throw or be ignored. The problem with buffering is that it might split large messages into chunks, and with other producers writing to the same file it may produce unexpected results.
(Unix*) - would need to check Users also seem to not want to have buffering enabled for atomic appends: https://stackoverflow.com/a/5504226/5852046 |
There is no need for that, every OS exposes a feature that allows for thread-safe atomic appends to end of file. In #55465 I've implemented that by:
|
Yes, but it is implementation details and my thoughts more about .Net behavior, e.g. Append with new FileShare option could expose new atomic append functionality without breaking change. I'm not sure we can benefit from it, but I had to mention it. |
|
Link to proposal: #53432 (comment)
Discussion
While working on a new API called
File.OpenHandle
I've realized thatFileStream
currently does not support handles that have been opened for appending (FILE_APPEND_DATA
on Windows andO_APPEND
on Unix).Moreover, we don't use these flags for
FileMode.Append
when we are opening a file from a path. Instead, we mimic the append by setting the file offset to the end of the file:runtime/src/libraries/System.Private.CoreLib/src/System/IO/Strategies/WindowsFileStreamStrategy.cs
Lines 240 to 243 in 33b0f24
runtime/src/libraries/System.Private.CoreLib/src/System/IO/Strategies/Net5CompatFileStreamStrategy.Unix.cs
Lines 86 to 90 in 33b0f24
runtime/src/libraries/System.Private.CoreLib/src/System/IO/Strategies/Net5CompatFileStreamStrategy.Windows.cs
Lines 85 to 88 in 33b0f24
I did some quick web search and it turns out that it was possible with .NET Framework: https://stackoverflow.com/questions/1862309/how-can-i-do-an-atomic-write-append-in-c-or-how-do-i-get-files-opened-with-the
And some of our users (@nblumhardt) are missing it in .NET Core: https://nblumhardt.com/2016/08/atomic-shared-log-file-writes/
@stephentoub @ericstj @JeremyKuhne is there any chance that you remember why we have decided to implement it in such a way?
cc @jozkee @carlossanlop
The text was updated successfully, but these errors were encountered: