0

こんにちは私はさまざまなプレーヤーがたとえば1時間または2時間続くことができるアクションをスケジュールするブラウザゲームを持っています。cronジョブは毎分、終了したすべてのアクションをチェックし(endtime <= unixtime())、それらを完了します(関連するプレーヤーに報酬を与えるなど)。

最近、完了すべきアクションが100個あり、cronjobタスク1が1分で終了しなかったため、cronjobタスク2が実行され、すべてのアクションが2回完了しました。

これが再び発生するのを防ぐにはどうすればよいですか?セッションに特定のトランザクション分離コードを使用し、更新用に行を予約する必要がありますか?

PHP5.3.18mysqlは5.5.27ですテーブルエンジンはINNODBです

毎分呼び出される現在のコードは次のとおりです。

public function complete_expired_actions ( $charflag = false )
{       
    // Verifying if there are actions to complete...

    $db = Database::instance();
    $db -> query("set autocommit = 0");
    $db -> query("begin");

    $sql = "select * from 
        character_actions
        where status = 'running' 
        and endtime <= unix_timestamp()"; 

    $result = $db -> query ( $sql ) ;               

    // try-catch. Se si verifica un errore, l' azione che commette l' errore viene rollbackata

    foreach ( $result as $row )
    {
        try 
        {

            $o = $this->factory( $row -> action );
            $o -> complete_action ( $row );


            if ($row -> cycle_flag == FALSE)
            { 
                // non aperta ad attacchi SQl-injection perchè i parametri non sono passati via request
            if ( $charflag == true )
                    kohana::log( 'info', "-> Completing action: " . $row -> id . ' - ' . $row -> action . " for char: " . $row->character_id );

                $db->query( "update character_actions set status = 'completed' where id = " . $row->id ); 
                // in ogni caso invalida la sessione!
                Cache_Model::invalidate( $row -> character_id );                                                                    
            }

            $db->query('commit');

            } catch (Kohana_Database_Exception $e)
            {
                kohana::log('error', kohana::debug( $e->getMessage() ));
                kohana::log('error', 'An error occurred, rollbacking action:' .  $row->action . '-' . $row->character_id );
                $db->query("rollback");         
            }   

    }       

    $db->query("set autocommit = 1");
4

1 に答える 1

0

この場合、新しい列を使用します。たとえば、テーブル character_actions にマークを付けます。

次に、ジョブの開始時に次のクエリを呼び出します。

$uniqueid = time();
$sql="update character_actions set mark = '$uniqueid' where status = 'running' and endtime <= unix_timestamp() AND mark is NULL "; 

その後、あなたのコードは

$sql = "select * from 
        character_actions
        where status = 'running' 
        and mark = '$uniqueid'"; 

    $result = $db -> query ( $sql ) ;   

このアプローチには限界があり、さまざまな並列作業を開始するため、マシンの速度が低下し、より多くの遅延と並列作業が発生します...

制限を導入して解決できます:

$lim= 100 ; // tune to finish the job in 60 seconds
$sql="update character_actions set mark = '$uniqueid' where status = 'running' and endtime <= unix_timestamp() AND mark is NULL limit $lim  "; 

もちろん、ポイント付与の遅延を誘発します。

于 2012-11-24T19:11:48.670 に答える