Read and write file atomically

2019-02-23 11:58发布

问题:

I'd like to read and write a file atomically in Ruby between multiple independent Ruby processes (not threads).

  • I found atomic_write from ActiveSupport. This writes to a temp file, then moves it over the original and sets all permissions. However, this does not prevent the file from being read while it is being written.
  • I have not found any atomic_read. (Are file reads already atomic?)

Do I need to implement my own separate 'lock' file that I check for before reads and writes? Or is there a better mechanism already present in the file system for flagging a file as 'busy' that I could check before any read/write?


The motivation is dumb, but included here because you're going to ask about it.

I have a web application using Sinatra and served by Thin which (for its own reasons) uses a JSON file as a 'database'. Each request to the server reads the latest version of the file, makes any necessary changes, and writes out changes to the file.

This would be fine if I only had a single instance of the server running. However, I was thinking about having multiple copies of Thin running behind an Apache reverse proxy. These are discrete Ruby processes, and thus running truly in parallel.

Upon further reflection I realize that I really want to make the act of read-process-write atomic. At which point I realize that this basically forces me to process only one request at a time, and thus there's no reason to have multiple instances running. But the curiosity about atomic reads, and preventing reads during write, remains. Hence the question.

回答1:

You want to use File#flock in exclusive mode. Here's a little demo. Run this in two different terminal windows.

filename = 'test.txt'

File.open(filename, File::RDWR) do |file|
  file.flock(File::LOCK_EX)

  puts "content: #{file.read}"
  puts 'doing some heavy-lifting now'
  sleep(10)
end


回答2:

Take a look at transaction and open_and_lock_file methods in "pstore.rb" (Ruby stdlib).

YAML::Store works fine for me. So when I need to read/write atomically I (ab)use it to store data as a Hash.



标签: ruby atomic