2024年4月25日木曜日

RQ のシグナルハンドリングについて調べてみる

RQ のシグナルハンドリングについて調べてみる

概要

RQ でプロセスハンドリングの挙動を確認してみました

環境

  • macOS 11.7.10
  • Python 3.11.6
  • rq 1.16.1

サンプルコード

  • vim app.py
from datetime import timedelta

from redis import Redis
from rq import Queue
from rq_scheduler import Scheduler

from lib.util import Message

queue = Queue("default", connection=Redis(host="192.168.1.2"))
scheduler = Scheduler(queue=queue, connection=queue.connection)


scheduler.enqueue_in(timedelta(seconds=5), Message().say, msg="hello")
  • vim lib/util.py
import time


class Message:
    def say(self, **kwargs):
        print("Start say")
        time.sleep(30)
        print(kwargs)
  • pipenv run rq worker
  • pipenv run rqscheduler
  • pipenv run python app.py

10秒以内にワーカーを Ctrl+c で停止した場合

ワーカーがジョブを掴んでからワーカーのプロセスを停止しようとするとちゃんと最後まで処理が終了してからワーカーが停止しました

11:21:53 default: say(msg='hello') (4d5a4db9-e8c9-4ee3-b64c-1dd266e66068)
Start say
11:21:55 Worker 98f9aac6f73445f685d4f5588abd514f [PID 32100]: warm shut down requested
{'msg': 'hello'}
11:22:03 default: Job OK (4d5a4db9-e8c9-4ee3-b64c-1dd266e66068)
11:22:03 Result is kept for 500 seconds
11:22:03 Worker rq:worker:98f9aac6f73445f685d4f5588abd514f: stopping on request
11:22:03 Unsubscribing from channel rq:pubsub:98f9aac6f73445f685d4f5588abd514f

kill -9 した場合

ワーカーがジョブを実行中に kill -9 を送信してみました
その場合ワーカーは Warm shut down せずにすぐに終了してしまいます

12:05:54 default: say(msg='hello') (b4eac96d-9591-482c-9753-e3a9c150d83b)
Start say
zsh: killed     pipenv run rq worker

redis 外部からアクセスできない場合

Protection mode が有効になっているので一旦無効にしましょう

  • CONFIG SET protected-mode no

docker の場合

docker stop はデフォルトでは SIGTERM -> SIGKILL の順番で信号を送ります
SIGKILL までの時間は time というオプションで変更可能でデフォルトで10秒になります

ワーカーを docker 上で動かし docker stop した際のワーカーの挙動を確認します

  • vim Dockerfile
FROM python:3.11.9-bullseye

COPY . /home
WORKDIR /home

RUN pip install pipenv
RUN pipenv install

CMD ["pipenv", "run", "rq", "worker", "--url", "redis://192.168.1.2:6379"]
  • docker build -t worker .
  • docker create --name worker worker
  • docker start worker
  • docker logs -f worker

でワーカーを起動します
スケジューラとエンキューは python から行います

  • pipenv run rqscheduler
  • pipenv run python app.py

これでワーカーがジョブを実行中にワーカーコンテナを停止します

  • docker stop worker
03:28:54 default: say(msg='hello') (76dbdc1b-5af2-4fa1-b4d2-49793454b235)
03:28:57 Worker 15c934ce3084457da63ade0e89ea14b6 [PID 1]: warm shut down requested

SIGTERM を受取り Warm shut down がスタートしますが10秒後に docker が強制的に SIGKILL を送信するためワーカーはジョブの完了を待たずに終了してしました

また強制終了したジョブは FailedJobRegistry に移動するようです

StartedJobRegistry cleanup: Moving job to FailedJobRegistry (due to AbandonedJobError)

time=60 にしてみる

SITTERM -> SIGKILL の送信にかかる時間を 60 秒に伸ばしてみます

  • docker stop --time=60 worker

すると今度はちゃんとワーカーがジョブの終了を待ってから停止していることが確認できました

03:32:54 default: say(msg='hello') (8e04f278-c5b1-4c4a-9069-3105960129f4)
03:33:14 Worker faa70ceee374410ba9416f55d74f4927 [PID 1]: warm shut down requested
Start say
{'msg': 'hello'}
03:33:25 default: Job OK (8e04f278-c5b1-4c4a-9069-3105960129f4)
03:33:25 Result is kept for 500 seconds
03:33:25 Worker rq:worker:faa70ceee374410ba9416f55d74f4927: stopping on request
03:33:25 Unsubscribing from channel rq:pubsub:faa70ceee374410ba9416f55d74f4927

最後に

RQ のシグナルハンドリングについて調べてみました
基本は RQ 側で実装されているので気にすることはないですが docker などと組み合わせる場合には SIGKILL の送信タイミングについて考慮する必要がありそうです

0 件のコメント:

コメントを投稿