2

Railsの起動時に、アプリの存続期間中実行されるスレッドを作成しようとしています。奇妙なことに、実行中の別のスレッドで既にこれが機能していました。その (作業中の) コードをコピーし、それを新しいスレッドの新しいコードのボイラープレートとして使用しました。しかし、スレッドは起動しません。

  • コードは config/initializers にあります (これは正しい場所ですか?)。
  • ファイルは最後に実行されるように「z_...」で始まる名前が付けられます。
  • レール 3.2.6

コードの一般的な構造は次のとおりです。

class Blah
  def self.the_thread
    The_Model.transaction do
    # do some database stuff
    # ...
  end

  TheThread = Thread.new do
    while true do
      the_thread
      sleep 5.seconds
    end
  end
end

Rails コンソールを開いて Blah::TheThread を調べると、一度も実行されていないように見えるデッド スレッドが明らかになります。Railsコンソールを開いたときにメソッドを実行でき、問題なく動作するため、クラスの宣言にもメソッドにもエラーはないようです。また、レールコンソールにスレッドを生成する上記の正確なコードを手動で入力すると (TheThread = Thread new do ...)、問題なく動作します (5 秒ごとにウェイクアップし、そのことを実行し、再びスリープします)。

繰り返しますが、奇妙なことに、Rails で単純なスレッドを生成するためのこの正確なボイラープレートは、以前はまったく同じアプリケーションで機能していました。

私が奇妙な問題であると考えるものについて、誰かが何らかの洞察を持っているなら、私は完全に耳を傾けます.

ありがとう。

編集: 新しい情報 - トランザクション呼び出し (および一致する終了) をコメントアウトしたところ、正常に動作します。ただし、トランザクションが何を妨げているのかはわかりません。他のカスタム初期化スクリプトを削除しましたが、ディレクトリ内の他のスクリプトは、デフォルトのものか、devise に属するものだけです。私はそれらを熟読しましたが、取引が行われているのを見ませんでした。

さらに新しい情報。スレッド コードの最後に「TheThread.abort_on_exception = true」を追加しました。Rails を起動すると、「ArgumentError: 閉じたデータベースで準備が呼び出されました: ロールバック トランザクション (ActiveRecord::StatementInvalid)」という例外が発生します。エラーは sqlite3 gem で発生しています。私は sqlite3 gem バージョン 1.3.6 と sqlite-ruby gem バージョン 1.3.3 を持っています。エラーをグーグルで調べたところ、同じエラーについて不平を言っている投稿がいくつか見つかりましたが、解決策はありませんでした。

これはもはやスレッドの問題ではなく、データベースの問題であると考えていますが、この投稿のタイトルを変更するには遅すぎます。

これだけ: スレッド コードの先頭 (while ループに入る直前) に「sleep 15.seconds」を配置すると、問題が解決されます。しかし、問題の原因と、「ハッキングされていない」解決策が何であるかを知りたいです。何らかの理由で、このコードの実行時にデータベースの準備ができていません。私の初期化コードは単に間違った場所にありますか?

4

1 に答える 1

0

これを実装する別の方法を検討することをお勧めします。本番環境に移行するときにインスタンスごとに 1 つの余分なスレッドが本当に必要でない限り(3 ~ 4 台のシン / モングレルなどのサーバーをセットアップすることを想像してください - それぞれに余分なスレッドがあります)、それについてもっと良い方法があります。

オプション 1 - 最も単純な方法は、タスクを実行するコントローラー アクションを作成し、wget と cron を使用してそのアクションを定期的にトリガーすることです。

20 * * * *  /usr/bin/wget --quiet -O - 'http://www.mydomain.com/my_controller/my_action'

ここでの問題は、認証/アクセス制御に対処する必要があることです-ランダムなユーザーがジョブをトリガーできるかどうかを本当に気にしない限り(率直に言って、非常に単純なジョブの場合があります)。

オプション 2 - Clockwork / Sidekiqまたは BackgroundRb を使用するか、バックグラウンドでタスクを実行するその他の方法を使用します。私はこれを本番アプリで実行しましたが、非常にうまく機能します。私は Clockwork と Sidekiq を選択しました。スケジュールされたタスクは次のようになります。

app/workers/daily_worker.rb:

class DailyWorker
  include Sidekiq::Worker

  def perform
    # do stuff here
  end
end

時計仕掛け.rb:

require 'clockwork'
require 'sidekiq'

Dir["app/workers/*"].each {|f| load f }

module Clockwork
  every 1.day, 'daily.jobs', :at => "00:30" do
    DailyWorker.perform_async
  end
end

私にはこれが最善の解決策のように思えました。すべてを起動して実行するのに約 15 分しかかかりませんでした。

于 2013-01-22T18:14:44.383 に答える