2016-09-06 3 views
3

私はSymfonyにアプリケーションから電子メール/通知を送る必要のあるアプリケーションを持っています。 電子メール/通知送信プロセスには時間がかかるので、キューに入れてキューを定期的に処理することに決めました。したがって、私は電子メール/通知ディスパッチを含むリクエストの応答時間を短縮することができます。PHPでのハンドルキューの競合条件Symfony with MySQL Database

キューを処理するCron Job(PHPスクリプト - Symfonyルート)は、30秒ごとに実行され、キューテーブルからすべてのデータを取得して送信を開始すると、未送信の電子メール/通知があるかどうかを確認します。電子メール/通知が送信されると、キューテーブルの行ステータスフラグが更新され、送信されたことが示されます。

現在、送信に30秒以上かかる可能性のあるメールがキューに追加されています。別のCronジョブも実行を開始し、キューから電子メールを送信し始めます。その結果、電子メール/通知の重複が発生します。

次のようにメールキューのための私のテーブル構造がある

次のようにこの問題を解決するために

|-------------------------------------| 
| id | email | body | status | sentat | 
|-------------------------------------| 

私のアイデアは、次のとおりです。

  1. はcronジョブが実行されているデータベース内のフラグを設定し、フラグが設定されていると他のCronジョブを進めるべきではありません。
  2. ステータスをすべてのレコードの「送信済み」として更新し、電子メール/通知の送信を開始します。

私の質問は、キューを処理する効率的なアプローチはありますか?このような特定の作業を行うSymfonyバンドル/フィーチャーはありますか?ご提案について

+1

ワーカーのキューイングに興味がある場合は、[Beanstalkd](https://github.com/pda/pheanstalk)を試してみてください。 GentmanとRabbitMQも一般的なメールキューサービスのためには洗練されていますが、新興企業、上司、runit、そしてGearmanとRabbitMQもあります。 –

答えて

2

私の質問は、キューを処理する効率的なアプローチはありますか?このような特定の作業を行うSymfonyバンドル/フィーチャーはありますか?

enqueue-bundledoctrine dbal transportを使用できます。

すでに競合状態などが処理されています。

2

  1. 何cronジョブ・プロセスが(何らかの理由で)死ぬとフラグをクリーンアップすることができない場合は?フラグは良い考えではない、と私は思う。この方法に従うには、ブール値を使用するのではなく、プロセスIDまたはタイムスタンプのどちらかを使用して、プロセスがまだ生きているかどうかを確認するか、またはクリーンアップせずに疑わしく長い時間前に起動したかどうかを確認してください。

  2. 同じ質問:プロセスが終了するとどうなりますか? は、が送信される前に送信済みとしてマークする必要はありません。

私はおそらく、二つのフィールドを使用したいと思います。(したがって、このレコードをスキップする他のプロセスを伝える)「進行中の送信」としてレコードをマークするための1と「正常に完了し送信」としてそれをマークするための別のものを。私は両方にタイムスタンプを書いて、 "進行中の送信"が過去にX秒以上経過したレコードを(自動的にまたは手動で)見つけることができます。これは、死んだプロセスの指標になります。

+0

私は両方のアイデアと全く同じだと思っていたので、実装しませんでした。しかし、2つのフィールドでは、手作業によるチェックが不要なテクニックがあります。手動チェック後に送信されることによる遅延は非常に重要なので、 –

+1

2つの可能なアプローチ1.レコードが進行中であるとマークされていることをcronジョブが検出したとき> 5分。前(またはそれが適切であると思うどんな時でも)、それが印が付けられていないかのように扱います。 2.タイムスタンプを使用せず、プロセスID。プロセスが存在しないプロセスIDを含む「未完成の」レコードが見つかった場合、レコードは「孤立している」ため、安全にマークを外すことができます。 – BlueM

1

データベーストランザクションはここで使用できます。残りは、データベースのロック機構と同時実行制御によって処理されます。一般的に、DML/DCL/DDLコマンドをどのように提供しても、孤立したトランザクションとして扱われます。あなたの質問で、2番目のcronジョブが1番目のcronジョブが送信されたものとして更新される前に行を読み取ると、そのメールは送信されずに送信され、もう一度送信しようとします。 2番目のcronジョブが送信されたものとして更新される前に、3番目のジョブが未送信の場合は同じ処理を行います。それはあなたのために大きな問題を引き起こす可能性があります。

どのようなアプローチをとっても、レースコンディションがあります。それで、データベースがそれを行えるようにしましょう。参照できる並行性制御メソッドは多数あります。

BEGIN_TRANSACTION 

/* Perform your actions here. N numbers of read/write */ 

END_TRANSACTION 

なお、この解決策には1つの問題があります。 1つの段階で、読み書き操作の回数が増えると、いくつかの矛盾が残っていることがわかります。

ここでは、データベースの分離レベルがあります。これは、2つのトランザクションが互いに隔離されているかどうか、およびそれらを同時に実行するようにスケジュールする方法を定義する要素です。

要件に応じて分離レベルを設定できます。同時性は分離レベルに反比例することに注意してください。それで、あなたの読み書きのステートメントを分析し、必要なレベルを見つけてください。それ以上のレベルを使用しないでください。私はあなたがここにあなたのデータベース操作を投稿することができた場合

http://www.ibm.com/developerworks/data/zones/informix/library/techarticle/db_isolevels.html

Difference between read commit and repeatable read

http://dev.mysql.com/doc/refman/5.7/en/innodb-transaction-isolation-levels.htm

助けるかもしれないいくつかのリンクを与えています。可能性のある分離レベルをお勧めします