0

状況: cron ジョブを作成しました。顧客が入力した注文を API に追加することでした。1分ごとに実行するように設定しました。

問題: 保留中の注文が多すぎると、最初の cron ジョブが終了する前に 2 番目の cron ジョブが呼び出されることがありました。その結果、API を介して送信された注文が重複していました。

解決策: データベースに cron_jobs という名前のテーブルを作成しました。ID とステータスの 2 つの列があります。cron ジョブが実行されると、ステータスが 0 かどうかがチェックされます。ステータスが 0 の場合、ステータスを 1 として更新します。プロセスを完了し、ステータスを再び 0 としてマークします。ジョブのステータスが 1 (すでに実行されていることを意味します) で、再度ヒットした場合、操作は次のようになります。終了しました。

予期しないトラブル: この解決策は論理的に正しいようです。しかし、それを実行してみると、驚くべき結果が得られました。ジョブが実行されると、そのステータスが更新されます。しかし、再度呼び出されると、データベースはステータス 0 を返し、実行を続けます。問題を調査するのに十分な時間を確保するために、スリープ メソッドも使用しました。しかし、データベースは、phpmyadmin を使用して表示できる値よりも常に間違った値を返します。

私が愚かな過ちを犯したかもしれないとは思わないでください。ファイルの 2 番目のインスタンスは、最初のインスタンスが完全に呼び出される前に、常に正しくない値をフェッチします。 私のコードを見てください:

<?php
$Output = mysql_fetch_assoc(mysql_query("select status from jobs where ID='1'"));
if($Output['status'] == '0')
{
    mysql_fetch_assoc(mysql_query("update jobs set status='1' where ID = '1'"));
    mysql_fetch_assoc(mysql_query("update jobs set invoked = invoked+1 where ID = '1'"));
    echo 'Executed';
}
else
{
    echo 'Error: Another cron job already in process. Operation terminated!';
    die();
}

/*I perform some lengthy tasks here*/

    /*Sleep function called to check whether another instance of the program works while this one is in progress */
    sleep(60);

    /*Task is complete. We are marking 0 as status so that another instance is allowed to work on.*/

    mysql_fetch_assoc(mysql_query("update jobs set status='0' where ID = '1'"));
?>

観察 1 : 2 番目のインスタンスを別のブラウザーまたは別のコンピューターで実行すると、完全に機能します。

観察 2 : 実際に複数回呼び出されているかどうかを調査しようとしました。テーブルに「No_Of_Times_Invoked」という別の列を作成しました。そして、同じブラウザから使用すると、コードが実際に失敗することがわかりました。

4

1 に答える 1

1

クエリSELECTは table を使用しますcron_jobs。他のすべてのクエリは を使用しますjobsSELECTクエリが失敗し、mysql_query()が返されると思いますfalse

$Output = mysql_fetch_assoc(mysql_query("select status from cron_jobs where ID='1'"));

ここでmysql_query()返すので、も返します。falsemysql_fetch_assoc()false

if($Output['status'] == '0')

$Outputですfalse。PHP では、 を使用する==と型変換が行われる場合があります。この場合、は に$Output['status'] == '0'評価されtrueます。$Output['status'](は ですがnull、 にnull == '0'評価されることに注意してください。型のジャグリング緩い比較falseの喜び。)

クエリを次のように置き換えます

SELECT `status` FROM jobs WHERE ID = '1'

正常に動作するはずです。

関数の戻り値にさらにエラー チェックを追加するかmysql_*()( を使用)、さらに良いことに、 PDO$returnValue === falseに切り替えることをお勧めします。


コメントでの議論の後に更新されました

問題は、ブラウザからテストしていることです。少なくとも Firefox 23.0.1 (私がテストに使用したもの) は、最初のリクエストが完了するまで 2 番目のリクエストを送信しません。これは、あなたが見ているすべての動作を説明します。新しいブラウザーまたはコンピューターは接続を再利用できないため、その場合は機能します。

コマンド ラインからスクリプトを 2 回 (5 秒間隔で) 起動すると、正しく動作しますError: Another cron job already in process. Operation terminated!

mysql_fetch_assoc()PHP は、各mysql_query("update ...")関数呼び出しの周りにある無効について不平を言うことに注意してください。

警告: mysql_fetch_assoc() は、パラメーター 1 がリソースであると想定します。これは、7 行目の /test/test.php で指定されたブール値です。

于 2013-08-19T19:49:46.010 に答える