5

Question

How can I update a file atomically without requiring my program to wait for slow physical media (such as with fsync)?

My hope is that the OS could "buffer" the typical fsync and rename operations in RAM, and then write them to disk in the proper order whenever it is convenient.


Background

I am developing software that runs in a custom embedded Linux environment with an ext4 filesystem. The program makes periodic updates to a file on disk. I need to maintain the integrity of this file without sacrificing application performance.

From what I have read, the accepted practice for safely updating a file is as follows:

  1. create a new temp file
  2. write data to the temp file
  3. fsync() the temp file
  4. rename the temp file to the appropriate name
  5. fsync() the containing directory

This process makes sense to me, but in my particular application, I would like to avoid a blocking call to fsync(). I don't care when the data is written to disk, so long as the file is always in a valid state. If the file is out-of-date, that is OK.


What I've Learned So Far

It seems that there is already quite a bit of discussion around ext4 and the proper use of fsync. If I understand correctly, I might be able to forgo the use of fsync if auto_da_alloc is enabled for my filesystem (link), but I'm not convinced that is the best solution.

Peteman720
  • 51
  • 3
  • If you don't care when the changes will be written out, then don't call `fsync` - after all, its whole *point* is to notify you (by virtue of unblocking the thread when done) that the writing is finished. If `fsync` were really needed for crash safety, how would such scheme handle a crash right before the call to `fsync`? – user4815162342 Aug 06 '19 at 20:17
  • i did not understand your question. As soon as you ask an operating system to write something, it will eventually write it. Crash of the application could prevent from all writes to finish. If you want to guarantee that your data is written before you continue the application, than you must block the application (use fsync or whatever). Which scheme you are interested int? – Serge Aug 06 '19 at 20:20
  • @user4815162342 It was my understanding that `ext4` was (originally) susceptible to data-loss when `fsync` is not used due to the **delayed allocation** mechanism [link](https://www.thomas-krenn.com/en/wiki/Ext4_Filesystem#Data_Loss_with_Applications_that_do_not_use_fsync()_correctly). – Peteman720 Aug 06 '19 at 20:23
  • @Serge In my case, the data does not need to be written before the application continues. My concern is that the file remains valid (out-of-date is OK) even if power is lost during a write operation. – Peteman720 Aug 06 '19 at 20:30
  • 1
    Create another thread. Call fsync in another thread. Notify the calling thread after fsync. There is also posix aio. – KamilCuk Aug 06 '19 at 20:32
  • 2
    @Peteman720 In this case i would follow the procedure you described in your post. You can probably run all those steps from a thread, to minimize blocking effect. – Serge Aug 06 '19 at 20:45
  • @KamilCuk I was hoping to avoid the added complexity of another thread, but perhaps that is my only feasible option. – Peteman720 Aug 06 '19 at 20:45
  • 2
    There is `aio_fsync`. And glibcs implementation of `aio_fsync` just does the same work for you - `aio_fsync` creates another thread, calls `fsync` in that thread and `raise`s a signal for you. – KamilCuk Aug 06 '19 at 20:48
  • @Peteman720 If ext4 is susceptible to data loss if fsync is not called, what happens if the application (or the whole system) crashes before being able to call it? The page you linked doesn't provide details, unfortunately. – user4815162342 Aug 06 '19 at 21:15
  • @user4815162342 Good question! If we are following the 5 steps that I listed above, then I believe that only the _temporary_ file would be lost if the program crashed before `fsync`. Corruption of the _real_ file would occur if the `rename` took place before all of the new data was written to disk: 1.) new temp file created (empty on disk). 2.) new data in RAM (waiting to go to disk). 3.) `rename` points the _real_ path the _temp_ file on disk. 4.) power loss! 5.) reboot, to find that the _real_ path points to a file on disk that is 0 bytes. That's my understanding anyway. – Peteman720 Aug 06 '19 at 21:31
  • 2
    @Peteman720 So, the point of `fsync` is to prevent the program from doing something unsafe (execute `rename`) before its time (all the new data was written to disk). The waiting is not just a nuisance, it's a crucial part of the algorithm. If the you don't plan to wait for the fsync in step 3 to finish, it doesn't make much sense to call it in the first place. – user4815162342 Aug 07 '19 at 07:10
  • @user4815162342 Yes, it is essential that the new data is written to disk (`fsync`) before the `rename` operation is completed on disk, but I would rather not force my program to wait for this whole process. My hope is that the OS could "buffer" the `fsync` and `rename` operations in RAM, and then write them to disk _in the proper order_ whenever it is convenient. My purpose with this question was to ask whether such a thing is possible on Linux with ext4. – Peteman720 Aug 07 '19 at 13:23
  • 1
    I think you should mention this "buffering" in the question, because it's far from obvious that that's the kind of thing you're looking for. I don't think it exists on linux or other Unix-like systems. – user4815162342 Aug 07 '19 at 16:46

1 Answers1

0

I've found that Linux file systems are remarkably resilient and reliable. I'd be suspicious discussions of ext4 issues that are mostly from 2009. Linux info goes stale.

That said, if the requirement is to guarantee that you use the latest correct version of the file, add a step to check that the file is correct. One way is to use Linux tools to generate a hash of the file. Maybe there's a faster approach that works with your specific data, like looking for a specific file length.

With that in place, your algorithm could be:

  1. Rename the current file to indicate that it is an old copy.
  2. Write the new file
  3. Calculate the hash of the new file

Now when you boot, or whenever you use the file, you can check the hash and if that fails, revert to the latest old copy.

The advantage to this plan is that the hash will protect you from any source of corruption to the file, not just ext4 file system issues.

sander
  • 61
  • 1