TupleSpaceやTupleSpaceProxyのメソッドにすると、リモートでrinda_evalするミスがおきやすい(自分ではまった)のでmodule_functionにすることにした。
require 'drb/drb' require 'rinda/rinda' module Rinda module_function def rinda_eval(ts) ts = DRbObject.new(ts) unless DRbObject === ts pid = fork do Thread.current['DRb'] = nil DRb.stop_service DRb.start_service place = TupleSpaceProxy.new(ts) tuple = yield(place) place.write(tuple) rescue nil end Process.detach(pid) end end
rinda_evalをつかって、大きくなったRBTreeをサブプロセスに分割していく遊びをしてみる。大きな集合を1つのプロセスで管理するなんて不安だ!という心配性な人にはうれしいかもしれない。
MultiRBTreeにsplit(名前に問題があるけど他に思いつかなかった)メソッドを追加。指定された要素数をローカルに残して、あまりをサブプロセスに管理してもらう。となりあったノードはそれぞれ@leftと@rightで参照できる。
require 'rbtree' require 'rinda/tuplespace' require 'rinda_eval' class MultiRBTree def split(size, place=nil) key = Time.now.to_f pid = Process.pid place = Rinda::TupleSpace.new unless place prev = DRbObject.new(self) Rinda::rinda_eval(place) do |ts| @left = prev size.times do self.shift end ts.write([:rbtree, key, pid, DRbObject.new(self)]) ts.read([:shutdown]) rescue exit [:done] end (self.size - size).times do self.pop end tuple = place.take([:rbtree, key, pid, nil]) @right.left = tuple[3] if @right @right = tuple[3] end attr_accessor :left, :right end if __FILE__ == $0 ts = Rinda::TupleSpace.new DRb.start_service rbt = MultiRBTree.new 5000.times do |n| rbt[(n % 49).to_s] = n end ary = [rbt] ary << ary.last.split(1000, ts) ary << ary.last.split(1000, ts) ary << ary.last.split(1000, ts) ary << ary[1].split(500) ary.each do |x| p [x.first, x.size, (x.__drburi rescue [:local, DRb.uri]), (x.left.last rescue nil), (x.right.first rescue nil)] end cur = ary[0] while cur p [cur.first, cur.last] cur = cur.right end ts.write([:shutdown]) end
この例だと自分と子供、孫、ひ孫で分担してMultiRBTreeを管理します。起動よりも終了が難しいな。