DEV Community

Cover image for Make Ruby code thread-safe
Vladislav Kopylov
Vladislav Kopylov

Posted on

Make Ruby code thread-safe

For example, we have ruby-code that execs some I/O operations in a row (for instance, it runs 3 queries to database).

class A
  def initialize
    @data = {}
  end

  # Run class and collect data to the hash
  def call
    do_1
    do_2
    do_3
    @data
  end

  private

  def do_1
    @data['1'] = exec_some_io
  end

  def do_2
    @data['2'] = exec_some_io
  end

  def do_3
    @data['3'] = exec_some_io
  end

  def exec_some_io
    time = (0.1..0.2).step(0.01).to_a.sample
    sleep(time)
    [1,2,3].sample
  end
end

puts A.new.call
Enter fullscreen mode Exit fullscreen mode

In order to optimise code and reduce execution time, we can rewrite it and use Thread.

class A
  def call
    [
      Thread.new { do_1 },
      Thread.new { do_2 },
      Thread.new { do_3 },
    ].map(&:join)
    @data
  end

  private

  def do_1
    @data['1'] = exec_some_io
  end

  def do_2
    @data['2'] = exec_some_io
  end

  def do_3
    @data['3'] = exec_some_io
  end
end
Enter fullscreen mode Exit fullscreen mode

Please, don't write the code like that. Firstly, methods do_1, do_2, do_3 know a lot of information about the data structure. Secondly, as we are using threads, we don't control context. It's dangerous and can produce errors in our application. In order to fix it, we have to use value method. It's easy to rewrite:

class A
  def call
    @data = {
      '1' => Thread.new { do_1 },
      '2' => Thread.new { do_2 },
      '3' => Thread.new { do_3 },
    }.transform_values(&:value)
    @data
  end

  private

  def do_1
    exec_some_io
  end

  def do_2
    exec_some_io
  end

  def do_3
    exec_some_io
  end
end
Enter fullscreen mode Exit fullscreen mode

Now, our ruby-code is thread-safe.

Top comments (0)