Могу ли я гарантировать атомное добавление в Ruby?

На основе Является ли файл append атомарным в UNIX? и другими источниками, это похоже на современный Linux, я могу открыть файл в режиме добавления и написать небольшие фрагменты (< PIPE_BUF) к нему из нескольких процессов, не беспокоясь о разрыве.

Ограничены ли эти пределы Ruby с помощью syswrite? В частности, для этого кода:

f = File.new('...', 'a')
f.syswrite("short string\n")

Можно ли ожидать, что запись не будет чередоваться с другим процессом, написанным одинаково? Или есть какая-то буферизация/потенциальное расщепление, о котором я еще не знаю?

Предполагая, что ruby >= 2.3

+4
источник поделиться
2 ответа

Я бы этого не предполагал. syswrite вызывает функцию write POSIX, которая не претендует на атомарность при работе с файлами.

Смотрите: Являются ли POSIX 'read() и write() системными вызовами Atom?

И Общие сведения о одновременной записи файлов из нескольких процессов

Tl; dr- вы должны реализовать некоторый элемент управления concurrency в вашем приложении, чтобы синхронизировать этот доступ.

+1
источник

Недавно я исследовал эту тему, чтобы реализовать File appender в Rackstash.

Вы можете найти тесты для этого в спецификации, которые я изначально адаптировал из в блоге на эту тему, чей код, к сожалению, неопровержимо, к сожалению, поскольку автор не пишет файл напрямую, а через трубку. Пожалуйста, прочитайте там комментарии.

Используя (1) современные операционные системы и (2) свои обычные локальные файловые системы, ОС гарантирует, что одновременные добавления из нескольких процессов записывают данные чередования.

Основные моменты здесь:

  • Вам нужна разумно современная операционная система. Очень старые (или экзотические) системы имели меньшие гарантии. Здесь вам может потребоваться заблокировать файл явно, например. Файл # flock.
  • Вам необходимо использовать совместимую файловую систему. Большинство локальных файловых систем, таких как HFS, APFS, NTFS и обычные файловые системы Linux, такие как ext3, ext4, btrfs,..., должны быть безопасными. SMB - одна из немногих сетевых файловых систем, которая также гарантирует это. NFS и большинство файловых систем FUSE в этом отношении небезопасны.

Обратите внимание, что этот механизм не гарантирует, что параллельные считыватели всегда читают полные записи. Хотя сами записи никогда не чередуются, читатели могут читать частичные результаты незавершенных записей.

Насколько я понимаю (и мои тесты), размер с возможностью записи не ограничивается даже PIPE_SIZE. Этот предел применяется только при записи в трубку, например, в виде сокета или, например, STDOUT вместо реального файла.

К сожалению, авторитетная информация по этой теме довольно скудная. Большинство статей (и ответы SO) на эту тему объединяют строгие добавления со случайными записями. Если вы не строго добавляете (открывая файл в режиме только для добавления), то гарантии недействительны.

Таким образом, чтобы ответить на ваш конкретный вопрос: Да, ваш код из вашего вопроса должен быть безопасным при записи в локальную файловую систему в современных операционных системах. Я думаю, что syswrite уже обходит файловый буфер. Разумеется, вы должны также установить f.sync = true перед тем, как записать его, чтобы полностью отключить любую буферизацию.

Обратите внимание, что вы все равно должны использовать Mutex (или аналогичный), если вы планируете писать в один открытый файл из нескольких потоков в вашем процессе (поскольку append-гарантии ОС действительны только для одновременной записи в разные файлы, дескрипторы, он не может различать перекрывающиеся записи одним и тем же процессом с одним и тем же файловым дескриптором).

+1
источник

Посмотрите другие вопросы по меткам или Задайте вопрос