@ledsun blog

無味の味は佳境に入らざればすなわち知れず

Ractor#send move:true したときに出た3つのエラー

詰まっているのはワーカーでなくパイプ? - @ledsun blog で、「Ractor間のデータ受け渡し時のコピーがボトルネックになっている」という仮説を立てました。 これを確認するために、Ractor間のデータの受け渡しをmoveにしてみます。

次のようにRactor#sendとRactor.yieldを使っている場所に move: true オプションをつけていきます。

    pipe = Ractor.new do
      loop do
        msg = Ractor.receive
        Ractor.yield(msg, move: true)
      end
    end

    workers = (1..4).map do
      Ractor.new pipe do |pipe|
        while msg = pipe.take
          a, ref_text, o = msg
          aligner = TextAlignment::TextAlignment.new(ref_text, o)
          m = a.map do |annotation|
            Annotation.align_annotations!(annotation, ref_text, aligner)
          end.flatten

          Ractor.yield([m, a], move: true)
        end
      end
    end

    Ractor.make_shareable(options)
    annotations_collection_with_doc = annotations_collection_with_doc.collect do |annotations, doc|
      ref_text = doc.original_body.nil? ? doc.body : doc.original_body
      ref_text.freeze
      pipe.send([annotations, ref_text, options], move: true)
      doc
    end

これを実行すると次の3つのエラーが起きるようになりました。 実行するといずれかのエラーがおきて止まります。

安易にmoveオプションをつければいいわけではなさそうです。

パターン1 no implicit conversion of Symbol into Integer (TypeError)

14:07:32 workor.1 | #<Thread:0x00007fda6a006788 run> terminated with exception (report_on_exception is true):
14:07:32 workor.1 | /home/ledsun/pubannotation/app/models/annotation.rb:466:in `[]': no implicit conversion of Symbol into Integer (TypeError)
14:07:32 workor.1 |     from /home/ledsun/pubannotation/app/models/annotation.rb:466:in `align_annotations!'
14:07:32 workor.1 |     from /home/ledsun/pubannotation/app/models/project.rb:767:in `block (3 levels) in store_annotations_collection'
14:07:32 workor.1 |     from /home/ledsun/pubannotation/app/models/project.rb:766:in `map'
14:07:32 workor.1 |     from /home/ledsun/pubannotation/app/models/project.rb:766:in `block (2 levels) in store_annotations_collection'

Rubyで一番苦手なエラーです。 ハッシュの代わりに配列が渡されているのでしょうか? エラーが起きている箇所は次のようなソースコードです。

   def self.align_annotations!(annotations, ref_text, aligner)
        return [] unless annotations[:denotations].present? || annotations[:blocks].present?

この呼び出し元は最初のソースコードAnnotation.align_annotations!(annotation, ref_text, aligner)です。 いままでハッシュで呼び出していたはずが、配列に変わることがあるみたいです。 同じ変数に格納されている値が変わっているのでしょうか? 何が起きているのか、いまいち想像できません。

パターン2 wrong argument type false (expected Class) (TypeError)

14:21:16 workor.1 | #<Thread:0x00007fda69e0cf18 run> terminated with exception (report_on_exception is true):
14:21:16 workor.1 | <internal:ractor>:627:in `yield': wrong argument type false (expected Class) (TypeError)
14:21:16 workor.1 |     from /home/ledsun/pubannotation/app/models/project.rb:757:in `block (2 levels) in store_annotations_collection'
14:21:16 workor.1 |     from /home/ledsun/pubannotation/app/models/project.rb:755:in `loop'
14:21:16 workor.1 |     from /home/ledsun/pubannotation/app/models/project.rb:755:in `block in store_annotations_collection'
14:21:16 workor.1 | #<Thread:0x00007fda69e0ca90 run> terminated with exception (report_on_exception is true):
14:21:16 workor.1 | <internal:ractor>:694:in `take': thrown by remote Ractor. (Ractor::RemoteError)
14:21:16 workor.1 |     from /home/ledsun/pubannotation/app/models/project.rb:763:in `block (2 levels) in store_annotations_collection'
14:21:16 workor.1 | <internal:ractor>:627:in `yield': wrong argument type false (expected Class) (TypeError)
14:21:16 workor.1 |     from /home/ledsun/pubannotation/app/models/project.rb:757:in `block (2 levels) in store_annotations_collection'
14:21:16 workor.1 |     from /home/ledsun/pubannotation/app/models/project.rb:755:in `loop'
14:21:16 workor.1 |     from /home/ledsun/pubannotation/app/models/project.rb:755:in `block in store_annotations_collection'

見慣れないエラーです。 起きている箇所は最初のソースコードRactor.yield(msg, move: true)です。 pipe役のRactorがデータを送ろうとしたところで、エラーが起きているようです。

https://github.com/ruby/ruby/blob/v3_1_2/ractor.c を見てみます。

ractor_yield(rb_execution_context_t *ec, rb_ractor_t *r, VALUE obj, VALUE move)
{
    VALUE ret_r;
    ractor_select(ec, NULL, 0, obj, RTEST(move) ? true : false, &ret_r);
    return Qnil;
}

Racter.yieldの引数はobjで、型エラーに引っかかりそうもありません。 どこかでそれらしい引数で rb_raise(rb_eArgError しているのかというと、こっちもみつかりません。

このエラーはどこで何がおかしいのか、想像できません。

パターン 3 <OBJ_INFO:gc_mark_ptr@gc.c:6713> 0x00007fda6b47ec10 [2 M ] T_NONE

ながいのでgistにしました。 PubAnnotationでRactor#send move:true したときに起きたエラー · GitHub このエラーが起きたときは最終的にSIGIOTでSidekiqプロセスが終了します。 僕は、C言語レイヤーのエラーを上手く読み解けません。 gc_mark_ptr とあるので、GCの途中でエラーが起きているのでしょうか?