【问题标题】:Can I guarantee an atomic append in Ruby?我可以保证 Ruby 中的原子追加吗?
【发布时间】:2018-08-14 23:58:32
【问题描述】:

基于Is file append atomic in UNIX? 和其他来源,它看起来像在现代 Linux 上,我可以在附加模式下打开一个文件并从多个进程向它写入小块 (PIPE_BUF) 而不必担心撕裂。

这些限制是否通过syswrite 扩展到 Ruby?专门针对这段代码:

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

我可以期望写入不会与其他进程以相同的方式写入交错吗?还是有一些我还不知道的缓冲/潜在分裂?

假设 ruby​​ >= 2.3

【问题讨论】:

    标签: ruby linux


    【解决方案1】:

    我最近研究了这个主题,以便在Rackstash 中实现File appender

    您可以在 specs 中找到对此的测试,我最初改编自 blog post 关于此主题的代码,不幸的是,由于作者没有直接写入文件,而是通过管道写入文件,因此不幸的是,其代码不是结论性的。请阅读那里的 cmets。

    使用 (1) 现代操作系统和 (2) 它们通常的本地文件系统,操作系统保证来自多个进程的并发追加确实写入交错数据。

    这里的要点是:

    • 您需要一个相当现代的操作系统。非常旧的(或外来的)系统的保证较少。在这里,您可能必须使用例如明确锁定文件File#flock
    • 您需要使用兼容的文件系统。大多数本地文件系统(如 HFS、APFS、NTFS)和常见的 Linux 文件系统(如 ext3、ext4、btrfs 等)应该是安全的。 SMB 是少数也能保证这一点的网络文件系统之一。 NFS 和大多数 FUSE 文件系统在这方面并不安全。

    请注意,此机制不能保证并发读取器始终读取完整写入。虽然写入本身永远不会交错,但读者可能会读取正在进行的写入的部分结果。

    就我的理解(和我的测试)而言,原子可写大小甚至不限于PIPE_SIZE。此限制仅适用于写入诸如套接字之类的管道或例如STDOUT 而不是真实文件。

    不幸的是,关于这个主题的权威信息相当稀少。关于这个主题的大多数文章(和 SO 答案)都将严格附加与随机写入混为一谈。如果不严格附加(通过以仅附加模式打开文件),则保证无效。

    因此,回答您的具体问题:是的,在现代操作系统上写入本地文件系统时,您问题中的代码应该是安全的。我认为 syswrite 已经绕过了文件缓冲区。当然,您还应该在写入之前设置f.sync = true 以完全禁用任何缓冲。

    请注意,如果您计划从进程中的多个线程写入单个打开的文件,您仍应使用 Mutex(或类似的)(因为操作系统的附加保证仅对并发写入不同文件有效 -描述符;它无法识别同一进程对同一文件描述符的重叠写入)。

    【讨论】:

      【解决方案2】:

      我不会这么认为。 syswrite 调用write POSIX 函数,该函数在处理文件时不声明原子性。

      见:Are POSIX' read() and write() system calls atomic?

      还有Understanding concurrent file writes from multiple processes

      Tl;dr- 你应该在你的应用中实现一些并发控制来同步这个访问。

      【讨论】:

      • 这就是我提到现代 linux 的原因。虽然 POSIX doesn't guarantee 什么都行,但 Linux 是 more strict:“文件偏移量的修改和写入操作作为单个原子步骤执行。”
      • @viraptor YARV 从字面上调用普通的旧write。如果你想非常确定这种行为,你可以在你需要这种行为的系统上自己编译 Ruby。
      猜你喜欢
      • 2011-02-28
      • 2019-01-17
      • 2012-04-15
      • 2016-05-19
      • 2017-03-10
      • 2011-05-12
      • 2021-06-10
      • 1970-01-01
      • 2010-12-16
      相关资源
      最近更新 更多