Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Read and write file atomically

Tags:

ruby

atomic

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.

like image 362
Phrogz Avatar asked Apr 17 '15 16:04

Phrogz


2 Answers

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
like image 56
Sergio Tulentsev Avatar answered Nov 09 '22 09:11

Sergio Tulentsev


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.

like image 45
Nakilon Avatar answered Nov 09 '22 10:11

Nakilon