mirror of
https://github.com/Mibew/mibew.git
synced 2025-01-31 13:24:41 +03:00
Prevent race condition in old threads closing
This commit is contained in:
parent
b5020645be
commit
96c9bf2a93
@ -373,64 +373,80 @@ class Thread
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Close all old threads that were not closed by some reasons
|
* Close all old threads that were not closed by some reasons.
|
||||||
*/
|
*/
|
||||||
public static function closeOldThreads()
|
public static function closeOldThreads()
|
||||||
{
|
{
|
||||||
if (Settings::get('thread_lifetime') == 0) {
|
if (Settings::get('thread_lifetime') == 0) {
|
||||||
|
// Threads live forever.
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
$db = Database::getInstance();
|
// We need to run only one instance of cleaning process. Use a lock with
|
||||||
|
// last run timestamp.
|
||||||
|
$lock_timestamp = (int)Settings::get('_threads_close_old_lock_time', 0);
|
||||||
|
$is_lock_free = !$lock_timestamp
|
||||||
|
// Lock cannot be got for more than 5 minutes. If such situation
|
||||||
|
// take place we have a deadlock.
|
||||||
|
|| (time() - $lock_timestamp) > 5 * 60;
|
||||||
|
|
||||||
$query = "UPDATE {thread} SET "
|
if ($is_lock_free) {
|
||||||
. "lrevision = :next_revision, "
|
// Get the lock
|
||||||
. "dtmmodified = :now, "
|
Settings::set('_threads_close_old_lock_time', time());
|
||||||
. "dtmclosed = :now, "
|
|
||||||
. "istate = :state_closed "
|
|
||||||
. "WHERE istate <> :state_closed "
|
|
||||||
. "AND istate <> :state_left "
|
|
||||||
// Check created timestamp
|
|
||||||
. "AND ABS(:now - dtmcreated) > :thread_lifetime "
|
|
||||||
// Check pings
|
|
||||||
. "AND ( "
|
|
||||||
. "( "
|
|
||||||
// Both user and operator have no connection problems.
|
|
||||||
// Check all pings.
|
|
||||||
. "lastpingagent <> 0 "
|
|
||||||
. "AND lastpinguser <> 0 "
|
|
||||||
. "AND ABS(:now - lastpinguser) > :thread_lifetime "
|
|
||||||
. "AND ABS(:now - lastpingagent) > :thread_lifetime "
|
|
||||||
. ") OR ( "
|
|
||||||
// Only operator have connection problems.
|
|
||||||
// Check user's ping.
|
|
||||||
. "lastpingagent = 0 "
|
|
||||||
. "AND lastpinguser <> 0 "
|
|
||||||
. "AND ABS(:now - lastpinguser) > :thread_lifetime "
|
|
||||||
. ") OR ( "
|
|
||||||
// Only user have connection problems.
|
|
||||||
// Check operator's ping.
|
|
||||||
. "lastpinguser = 0 "
|
|
||||||
. "AND lastpingagent <> 0 "
|
|
||||||
. "AND ABS(:now - lastpingagent) > :thread_lifetime "
|
|
||||||
. ") OR ( "
|
|
||||||
// Both user and operator have connection problems.
|
|
||||||
// Just close thread.
|
|
||||||
. "lastpinguser = 0 "
|
|
||||||
. "AND lastpingagent = 0 "
|
|
||||||
. ") "
|
|
||||||
. ")";
|
|
||||||
|
|
||||||
$db->query(
|
$query = "UPDATE {thread} SET "
|
||||||
$query,
|
. "lrevision = :next_revision, "
|
||||||
array(
|
. "dtmmodified = :now, "
|
||||||
':next_revision' => self::nextRevision(),
|
. "dtmclosed = :now, "
|
||||||
':now' => time(),
|
. "istate = :state_closed "
|
||||||
':state_closed' => self::STATE_CLOSED,
|
. "WHERE istate <> :state_closed "
|
||||||
':state_left' => self::STATE_LEFT,
|
. "AND istate <> :state_left "
|
||||||
':thread_lifetime' => Settings::get('thread_lifetime'),
|
// Check created timestamp
|
||||||
)
|
. "AND ABS(:now - dtmcreated) > :thread_lifetime "
|
||||||
);
|
// Check pings
|
||||||
|
. "AND ( "
|
||||||
|
. "( "
|
||||||
|
// Both user and operator have no connection
|
||||||
|
// problems. Check all pings.
|
||||||
|
. "lastpingagent <> 0 "
|
||||||
|
. "AND lastpinguser <> 0 "
|
||||||
|
. "AND ABS(:now - lastpinguser) > :thread_lifetime "
|
||||||
|
. "AND ABS(:now - lastpingagent) > :thread_lifetime "
|
||||||
|
. ") OR ( "
|
||||||
|
// Only operator have connection problems.
|
||||||
|
// Check user's ping.
|
||||||
|
. "lastpingagent = 0 "
|
||||||
|
. "AND lastpinguser <> 0 "
|
||||||
|
. "AND ABS(:now - lastpinguser) > :thread_lifetime "
|
||||||
|
. ") OR ( "
|
||||||
|
// Only user have connection problems.
|
||||||
|
// Check operator's ping.
|
||||||
|
. "lastpinguser = 0 "
|
||||||
|
. "AND lastpingagent <> 0 "
|
||||||
|
. "AND ABS(:now - lastpingagent) > :thread_lifetime "
|
||||||
|
. ") OR ( "
|
||||||
|
// Both user and operator have connection problems.
|
||||||
|
// Just close thread.
|
||||||
|
. "lastpinguser = 0 "
|
||||||
|
. "AND lastpingagent = 0 "
|
||||||
|
. ") "
|
||||||
|
. ")";
|
||||||
|
|
||||||
|
// Perform the cleaning
|
||||||
|
Database::getInstance()->query(
|
||||||
|
$query,
|
||||||
|
array(
|
||||||
|
':next_revision' => self::nextRevision(),
|
||||||
|
':now' => time(),
|
||||||
|
':state_closed' => self::STATE_CLOSED,
|
||||||
|
':state_left' => self::STATE_LEFT,
|
||||||
|
':thread_lifetime' => Settings::get('thread_lifetime'),
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
// Release the lock
|
||||||
|
Settings::set('_threads_close_old_lock_time', '0');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
Loading…
Reference in New Issue
Block a user