1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

【Ruby/Rails】スレッドエラー(ThreadError)をリトライする機構

Last updated at Posted at 2020-08-23

概要

Thread.new()を使いマルチスレッド処理してたところ、Herokuにてリソース不足エラーが発生。

can't create Thread: Resource temporarily unavailable (ThreadError)

スレッドエラーのハンドリングと、ベストエフォートでスレッドを使うリトライ機構を作成した。


ちなみにHerokuで実行できるプロセス・スレッド数はかなり限られているので、ローカル環境との差異に注意。

2020/08現在

free, hobby and standard-1x dynos support no more than 256
standard-2x and private-s dynos support no more than 512
performance-m and private-m dynos support no more than 16384
performance-l and private-l dynos support no more than 32768

ローカル環境がMacOSであれば、sysctl kern.num_taskthreadsというコマンドで1プロセスあたりの最大スレッド数が調べられる。

$ sysctl kern.num_taskthreads
kern.num_taskthreads: 4096

注意

通常より少し負荷がかかったらスレッドが不足するような処理は、まず設計ミスしている可能性が高い。

別サーバーに処理を委譲できないか、APIの仕様的に少ないリクエスト数で済ませる方法はないかなど、立てなければいけないスレッドの数を減らす方法を先に考える。

コード

def retry_threads(times: 3)
  try = 0
  begin
    try += 1
    Thread.new { yield }
  rescue ThreadError
    sleep(1 * try)
    retry if try < times
    raise
  end
end

使えるスレッドがない場合、数秒待ちリトライする。

1回目のリトライでは1秒、2回目のリトライでは2秒...と待ちの秒数を可変にし、タイムロスを防ぐ。

待ち時間をマイクロ秒にして、細かくリトライを管理するやり方もあり。


実際の使用方法

def heavy_task(url)
  # 重い処理
end

# urls = ["...","...",...]
threads = []
urls.all each do |url|
  threads << retry_threads{ heavy_task(url) }
end
threads.each(&:join)

ベンチマーク

このリトライ機構で、スレッドを立てる許容度が実際どれぐらい上がったのか調査した。

条件

  • 10秒かかる処理がリトライ対象
  • 1秒づつリトライ待ち時間を増やす
  • リトライは3回まで
  • ローカル環境で実行(MacOS Catalina)
    • 最大スレッド数: 4096
  • 処理できたタスク数をベンチマークの値にする

計測コード

def heavy_task
  sleep(10)
end

def retry_threads(times: 3)
  try = 0
  begin
    try += 1
    Thread.new { yield }
  rescue ThreadError
    sleep(1 * try)
    retry if try < times
    p $count
    raise
  end
end

def no_retry_threads()
  begin
    Thread.new { yield }
  rescue ThreadError
    p $count
    raise
  end
end

$count = 0

# retryなし
loop do
  no_retry_threads{ heavy_task }
  $count += 1
end

# retryあり
loop do
  retry_threads{ heavy_task }
  $count += 1
end

結果

ローカル環境(最大スレッド数: 4096)で、10秒かかるタスクを処理した数の値。

リトライなし リトライあり
4094 212888

参考

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?