Live data from Hacker News

Unix’s file durability problem

utcc.utoronto.ca

91–100 of 161 posts

Re: Unix’s file durability problem

#91
post #70

Earlier quoted context omitted.

Er, yes, that's the point.

So the reliability and storage semantics bottom out at the filesystem after all. It's not as simple as 'I'll use SQLite: problem solved'. Not for the authors of SQLite, for sure.

The point is: the authors of SQLite solved that problem. Properly. For many more OSes than you or I will ever see. And they test it.

Re: Unix’s file durability problem

#92
post #41
post #18

I think the man page http://linux.die.net/man/2/fsync answers the questions the article asks about fsync(). fsync() = YES metadata, YES data, NO dir entry fdatasync() = NO metadata, YES data, NO dir entry fsync() of dir = NO metadata, NO data, YES dir entry

For Linux, but it's not portable across all Unixes. fsync is allowed to do nothing (1) and does not need to work on directories (2). On Mac OSX, fsync will not flush the disk cache, which can lead to data loss (3). (1) http://pubs.opengroup.org/onlinepubs/9699919799/functions/fs... (2) http://austingroupbugs.net/view.php?id=672 (3) https://developer.apple.com/library/mac/documentation/Darwin...

fsync(2) on OS X and other Darwins also does not sync metadata. You need to use fcntl(2) with F_FULLFSYNC for that.

Re: Unix’s file durability problem

#93
post #41

Earlier quoted context omitted.

For Linux, but it's not portable across all Unixes. fsync is allowed to do nothing (1) and does not need to work on directories (2). On Mac OSX, fsync will not flush the disk cache, which can lead to data loss (3). (1) http://pubs.opengroup.org/onlinepubs/9699919799/functions/fs... (2) http://austingroupbugs.net/view.php?id=672 (3) https://developer.apple.com/library/mac/documentation/Darwin...

fsync(2) on OS X and other Darwins also does not sync metadata. You need to use fcntl(2) with F_FULLFSYNC for that.

It looks like doing `sysctl -w kern.always_do_fullfsync=1` Will produce a sane behavior:

https://opensource.apple.com/source/xnu/xnu-2422.1.72/bsd/hf...

Alternatively, you could just use the ZFS driver for Mac OS X. It will do fsync properly.

Re: Unix’s file durability problem

#94
post #54

I've suggested an approach to this before. There should be several types of files. - "Unit" files commit when closed properly (this does not include a program exit without close), and then replace the old version of the file. The file system should guarantee that, after a crash, you have either the old version or the new complete version. This should be the default when a file is opened via "creat()" - "Temp" files a…

> "Log" files can only be appended. Writers cannot seek.

I think the semantic you want for log files is: write atomically at offset X, but only if the size of the file is currently X.

If the write fails due to a file size mismatch, you are racing with someone else. You can either read the other writer's data before you try writing again or just fail immediately if races shouldn't happen.

Re: Unix’s file durability problem

#95
post #67

Earlier quoted context omitted.

Rename isn't quite an atomic replacement. If you crash before the rename, the new file hangs around. (Hence unwanted .part files.) O_APPEND isn't airtight on all systems. On some older UNIX systems, multiple writers created with "open()" (not "dup()") do not share a file position. NTFS doesn't do append correctly. How do you guarantee that, after a crash, the end of the file is at the end of some write? By updating t…

sync_file_range(2) allows you to specify ranges to sync, instead of the whole file, but it's not that useful anyway. In most DBs, you have separate data and log. The log is always only appended to, and the nature of disks is such that grouping concurrent transactions together into a single commit is a big win regardless of how syncing works. So syncing all outstanding I/O to the log file is generally what you want to…

Actually, we made postgres use sync_file_range() for checkpointing if available in 9.6. Not for durability - there's a few to many caveats in the manpage - but to control how much work a later fsync() has to do. In many workloads checkpointing can generate a lot of writes, and the OS's writeback caching of those can generate a lot of dirty buffers in the kernel's page cache. If the kernel decides to flush those (on it's own or due to an fsync), latency for every other FS operation can skyrocket. We've seen stalls in the 10s of minutes. So we now regularly sync_file_range(SYNC_FILE_RANGE_WRITE), unless the feature is disabled of course, to control how much dirty data the kernel has.

See http://git.postgresql.org/gitweb/?p=postgresql.git;a=commit;... and http://git.postgresql.org/gitweb/?p=postgresql.git;a=commit;...

Re: Unix’s file durability problem

#96
post #42
post #37

Earlier quoted context omitted.

Microsoft actually tried something similar with WinFS [0] in the 00's, but the project failed. [0] - https://en.wikipedia.org/wiki/WinFS

Oddly, Apple's Core Data framework achieves pretty much all the goals WinFS set out to achieve—but does so sitting (for no good reason, really) on top of a regular filesystem. It'd actually be pretty easy to flip things around: give Core Data a raw block device persistence backend, and then write a filesystem driver on top of it. One of my recently-planned hobby projects was to write a FUSE filesystem for OSX that wo…

> but does so sitting (for no good reason, really) on top of a regular filesystem

You don't like files? Files are great.

Running Core Data over SQLite instead of a "real database" gets you backups for free, the iOS file-based security model, lets you delete an app's data when it goes away, network mounted home directories… really, the filesystem abstraction is pretty good still.

Re: Unix’s file durability problem

#97

Earlier quoted context omitted.

> - "Unit" files commit when closed properly (this does not include a program exit without close), and then replace the old version of the file. The file system should guarantee that, after a crash, you have either the old version or the new complete version. Incidentally, this is the way it currently is. From rename(2) : If "newpath already exists, it will be atomically replaced". Just don't forget that "replacing"…

Don't know why you're downvoted. Everything you said is correct. Additionally, in Linux, you can get asynchronous fdatasync(2) with sync_file_range(2). aio(7) is weird. It really only works with direct I/O (which is necessary much less often than people think it is), and IIRC aio_fsync(2) isn't implemented on Linux, but that doesn't matter so much because generally direct I/O implies synchronous I/O (but not always).…

AIO does not require O_DIRECT. Most Linux file systems do not implement aio_fsync because they put the writes into the page cache and block until it is flushed to disk before signaling completion. That means aio_fsync can be implemented by no-op.

Re: Unix’s file durability problem

#98

A good solution is to use SQLite. It addresses the issues (pretty much by doing all the fsync etc mentioned including on directories) and has a very comprehensive test suite. It is also used very widely on desktops, mobile devices, applications etc. https://www.sqlite.org/whentouse.html A notable quote: SQLite does not compete with client/server databases. SQLite competes with fopen().

The concept was proven before w/ RMS in OpenVMS: https://en.wikipedia.org/wiki/Files-11 It could work. Just best to have hybrids with different types of files, including those bypassing RDBMS function, so one can select proper reliability vs performance tradeoffs. SQLite might have cross-platform, FS-type API's too that I don't know about. Not sure if it's already there or be an extra development.

I really miss OpenVMS. It's a damn shame it doesn't run on x86-64. I might try to set up one of the Alpha AXP emulators that are floating around out there, if only to feed my nostalgia.

Re: Unix’s file durability problem

#99
post #54

I've suggested an approach to this before. There should be several types of files. - "Unit" files commit when closed properly (this does not include a program exit without close), and then replace the old version of the file. The file system should guarantee that, after a crash, you have either the old version or the new complete version. This should be the default when a file is opened via "creat()" - "Temp" files a…

> "Log" files can only be appended. Writers cannot seek. I think the semantic you want for log files is: write atomically at offset X, but only if the size of the file is currently X. If the write fails due to a file size mismatch, you are racing with someone else. You can either read the other writer's data before you try writing again or just fail immediately if races shouldn't happen.

I don't mean to sound dismissive, but do you have any idea of how O_APPEND interacts with PIPE_BUF? The problem you're describing is basically solved on modern POSIX OS's.

You're just proposing moving the semantics of what could be better done in kernel-space by resizing PIPE_BUF to userspace.

The way this works now is that you open a logfile with O_APPEND, any write(2) you make to the file will be atomic up to PIPE_BUF, so it doesn't matter that you have a "race" with someone else if everyone's write is Now of course if your write is bigger than PIPE_BUF you might have interleaving writes, but at the point where you're making >4K writes you're usually better off having some custom log system anyway and not rely on the kernel serializing things for you.

If you want to emulate this proposed mechanism of yours now without any syscall changes you can simply flock() the file for the duration of your write. The solution you're proposing already exists, but locking sucks more than O_APPEND + <PIPE_BUF sized writes.

Re: Unix’s file durability problem

#100
I ran across a nice paper a few years ago: Rethink the Sync.

https://www.usenix.org/legacy/event/osdi06/tech/nightingale/...

The basic idea is that the file system provides two guarantees, one boring, and one interesting. I'll illustrate using code instead of words. The boring guarantee is:

    FILE* f = fopen("autosave.bak", "w");
    fwrite(f, buffer, length); // save current document
    fclose(f);
The system will try to make the write durable within 5 seconds of executing the second line. The interesting guarantee is this:

    FILE* f = fopen("autosave.bak", "w");
    fwrite(f, buffer, length); // save current document
    puts("Saving complete!")
    fclose(f);
The system guarantees that the user will not see the message until the data is safely on disk! The way they do this is to implement a fancy dependency tracking mechanism that makes sure that the computer never generates output until the writes that the output depends on have completed.

They do a bunch of benchmarks that show that their system is almost as fast as mounting ext3 _asynchronously_. (In fact, not much worse than a RAM disk even.) Of course, they also show that in the case of power loss, their system behaves well, whereas ext3 does not, unless you turn up all the paranoia knobs to 11, and then the performance is WAY worse than their system.

I'm oversimplifying quite a bit, since this comment is already pretty long. Read the paper for details. Or ask questions here, but I'll probably forget to check the comments, because HN doesn't remind me :(

Post reply on HN