你好我有一个浏览器游戏,其中各种玩家安排可持续一到两个小时的动作。每分钟一次cronjob检查所有已完成的操作(endtime< = unixtime())并完成它们(给予相关玩家等奖励);
最近发生了 - 比方说 - 100个动作要完成而cronjob任务1没有在一分钟内完成,所以cronjob任务2被解雇,结果是 所有行动都完成了两次。
我怎样才能避免这种情况再次发生?我必须为会话使用特定的事务隔离代码并保留行以进行更新吗?
PHP 5.3.18 mysql是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");
答案 0 :(得分:0)
在这种情况下,我使用一个新列,例如:mark to the table 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 ";
当然会导致积分归属延迟。