現在、meritmoot.com (開発中で、ローリングリリース中)に関連するプラグインを作成しています。外部データの使用に伴い、長時間実行される Sidekiq ジョブを利用しています。しかし、何らかの理由でジョブが失敗もせず、エラー出力も表示されないまま、内部のジョブコードを繰り返し再起動してしまいます。Sidekiq において、再起動をトリガーする可能性のある見落としがあるでしょうか?
最後のジョブ(1 日 1 回実行予定のロールコール関連)では、エラーなしに以下の間隔でジョブが再起動しました:
0 時間 58 分 42 秒(最初のイテレーション開始時刻)
1 時間 30 分 12 秒(最初の再起動)
2 時間 1 分 1 秒(2 回目の再起動)
3 時間 46 分 49 秒
4 時間 17 分 11 秒
4 時間 47 分 33 秒
ジョブは完了せず、すべての進行状況が最初から再開されました。なお、内部コードの stderr と stdout をリダイレクトする独自のログ処理を実装していますが、Sidekiq に干渉するとは思いません(興味があれば確認できます。開発に非常に役立っています)。
コード内で進捗を保存することも可能ですが、オーバーヘッドが発生するため、よりシンプルなプロセスを望んでいます。Sidekiq において、コードが再起動をトリガーする要因として見落としがあるでしょうか?
Falco
(Falco)
2020 年 5 月 8 日午前 2:00
2
Sidekiqのジョブを意図的に1時間以上実行するようにしたいとお考えですか?もう少し詳しい理由を教えていただけますか?
私は外部データを大量に取り込み、自社のウェブサイトで内部保存しています。このデータは米国の議会に関する情報です
編集:公開されている法案、採決記録、議員情報など
メモリ不足にお困りですか?当社のホスティングでは、Sidekiq がメモリを過剰に使用し始めると自動的に再起動するように設定されています。
メモリとは、データベースやハードディスクのメモリのことですか?確かに、その部分を多く使っています。現在、@pfaffman さんの提案を少し改変した案を検討しています。プロセスをフォークして、メインスレッドを終了させつつ、同じコンテキストを持つ子プロセスを起動するものです(Sidekiq に対しては外部スクリプトのような扱いになります)。
https://stackoverflow.com/questions/806267/how-to-fire-and-forget-a-subprocess#806326 のパターンをテストして、問題を解決しています。
pid = Process.fork
if pid.nil? then
# 子プロセス内
exec "whatever --take-very-long"
else
# 親プロセス内
Process.detach(pid)
end
少し奇妙な問題ですが、接続している API には更新機能がないため、毎日 API の大部分を再ダウンロードしてデータを更新しています
数時間後、結果をお知らせします。
編集:また停止しました。進捗を定期的に保存し、より効率的にする方法を検討しようと思います。
sam
(Sam Saffron)
2020 年 5 月 13 日午前 2:49
8
これをやる代わりに、なぜ仕事を小さな単位に分割して実行するように教えないのでしょうか?本当に4時間もかかる必要があるのでしょうか?10件のトピックを同期し、次に別の10件を同期し、というように進めましょう。
Sidekiqには長時間実行されるジョブを終了させる機能はありませんが、アプリの再ビルドやWeb UIを介したアップグレードによってそれは行われます。
ご支援ありがとうございます。
最終的にプロセスコードを削除しました。効果的ではなかったためです。再起動ジョブは、根本的に非効率であるという問題を隠すだけでした。代わりに以下を行っています:
バルク SQL コードの作成(逐次処理よりもはるかに高速)。更新が必要な場合のみを検知し、変更されていないアイテムを PostRevisor クラスを使用して再更新するのをスキップできるようにしています
HTTP 経由でのデータ取得の効率化。永続的接続の活用や、可能な場合は圧縮されたアイテムを含むデータポイントの活用など
バルク SQL コマンドの作成により、劇的な速度向上が得られました。私が更新しているのは以下のテーブルです:
post テーブル:cooked、last_updated カラム
topic テーブル:title、last_updated カラム
次のアイデアは、PostRevisor を完全にスキップし、以下のような処理を行うことです:
データを一時的なテーブルに移動
UPDATE topics FROM temp_table
SET topics.title = temp_table.title, topics.last_updated = temp_table.last_updated
WHERE topics.id = temp_table.id AND topics.title != temp_table.title
UPDATE posts FROM temp_table
SET posts.raw = temp_table.raw, posts.last_updated = temp_table.last_updated
WHERE posts.id = temp_table.id AND posts.raw != temp_table.raw
その後、タイトルとコンテンツが変更されたため、検索の再インデックスジョブをトリガー
見落としている点はありますか?Discourse は複雑であり、PostRevisor をスキップすることで、私が経験の浅いテーブル(post_stats、post_timings、post_uploads、quoted_posts など)に触れてしまう可能性があります。ただし、システムが信頼できる予測可能なソースからこれらの改訂を取得しているため、PostRevisor が提供するすべての検証は必要ありません。この解決策は、当たるか外れるかのような気がします。
ご意見をお聞かせください。
更新について:時間とともに異常な数の更新が発生していたため、コードチェックを行っていたところ、生の JSON フォーマットで実際には更新されていないデータ項目に対して、不当な更新を引き起こす要因があることが判明しました。このエラーが修正されれば、上記の対応はおそらく不要になるでしょう: 本来はテストをすべきでした……そうすれば多くの時間を節約できたはずです。それでも、上記の方法を試してみようかと考えています。ただ、最優先事項ではありません。データ表示形式を変更した際の迅速な更新には役立ちますし、すでに実装済みなので、テストされていないだけという状況です。
一括更新コードが完成しました。より安定してから特定のブランチにプッシュすることに興味はありますか?この機能は特定のユースケースに特化していますが、タグを含む何千ものレコードを素早く更新できます。TopicsBulkAction を拡張するように構築されています。詳細な情報が必要な場合は、私が書いた README をご覧ください。
# 入力
# - cooked、post_id、topic_id、title、updated_at、tags を含むハッシュのリスト(raw は cooked を指します)
# [{post_id: #, cooked: "", topic_id: #, title: "", updated_at: date_time, tags: [{tag: "", tagGroup: ""}, ... ] } , ... ]
# - category_name、更新対象のカテゴリ名。これは検索インデックス作成に使用されます。
# リスト項目に含めるオプションのハッシュ属性:
# - raw、含まれない場合は cooked と同じになります。
# - fancy_title、含まれない場合は title と同じになります。
# - slug、含まれない場合は title から処理されます(これは URL に関連します)。
# ユースケース:非 Discourse 系のバックエンドデータソースから変更される情報を効率的に更新し、話題を定期的に更新すること。
# 情報のミラーリング更新について。なお、これは一般的な投稿やトピック作成用ではなく、トピックのタイトルとトピックの MAIN 投稿を更新するために作られています。一般的な投稿の修正については、lib/post_revisor.rb の PostRevisor を参照してください。
# - 事前に調理済み、カスタム調理済み、またはそのまま表示されることを前提としています。データは検証されません。
# - raw == cooked の場合、投稿作成時に (cook_methods: Post.cook_methods[:raw_html]) を設定する必要があります。
# これは、投稿内にカスタム HTML を表示する場合に行います。
# そうしないと、Discourse が将来再調理してしまう可能性があります。情報の信頼性と内容のエスケープ処理を確認してください。
# - 上記が理想的でない場合は、raw を含め、投稿作成時に正しい調理メソッドを設定し(システムが再調理する場合に備えて)、
# raw を選択した調理メソッドで処理し、raw と出力された cooked をハッシュに含めてください。
# - 投稿の前後の単語数の差分を記録し、それをトピックに渡すことで、word_count を追跡します。
# - タグ数も同様に追跡します。