summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLester Caine <lester@lsces.co.uk>2026-05-21 12:50:09 +0100
committerLester Caine <lester@lsces.co.uk>2026-05-21 12:50:09 +0100
commit0b5a8ac7b3bc8422f01e959b522d041ea021324a (patch)
treeddfed395918275a8abc7af795f71acc60c49d42c
parentb5dcd70d18485812eb58e8e4e91bad1c2cc11e43 (diff)
downloadliberty-0b5a8ac7b3bc8422f01e959b522d041ea021324a.tar.gz
liberty-0b5a8ac7b3bc8422f01e959b522d041ea021324a.tar.bz2
liberty-0b5a8ac7b3bc8422f01e959b522d041ea021324a.zip
Fix addHit() race condition and stuck Firebird transaction
Replaced SELECT-then-INSERT/UPDATE with UPDATE-first pattern: attempt UPDATE and only INSERT if zero rows were affected. Eliminates the race window where two concurrent requests both see no row and both try to INSERT, which left an uncommitted Firebird transaction blocking reads. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
-rwxr-xr-xincludes/classes/LibertyContent.php18
1 files changed, 8 insertions, 10 deletions
diff --git a/includes/classes/LibertyContent.php b/includes/classes/LibertyContent.php
index 52fe1af..932cc71 100755
--- a/includes/classes/LibertyContent.php
+++ b/includes/classes/LibertyContent.php
@@ -1824,17 +1824,15 @@ class LibertyContent extends LibertyBase implements BitCacheable {
global $gBitUser,$gBitSystem;
if( empty( $_REQUEST['post_comment_submit'] ) && empty( $_REQUEST['post_comment_request'] ) ) {
if( BitBase::verifyId( $this->mContentId ) && (( $gBitUser->isRegistered() && !$this->isOwner() ) || ( $gBitUser->getField( 'user_id' ) == ANONYMOUS_USER_ID )) && ( $gBitSystem->isFeatureActive( 'users_count_admin_pageviews' ) || !$gBitUser->isAdmin() ) ) {
- if( $this->mDb->getOne( "SELECT `content_id` FROM `".BIT_DB_PREFIX."liberty_content_hits` WHERE `content_id`=?", [ $this->mContentId ])) {
- $query = "UPDATE `".BIT_DB_PREFIX."liberty_content_hits` SET `hits`=`hits`+1, `last_hit`= ? WHERE `content_id` = ?";
- } else {
- $query = "INSERT INTO `".BIT_DB_PREFIX."liberty_content_hits` ( `hits`, `last_hit`, `content_id` ) VALUES ( ?,?,? )";
- $bindVars[] = 1;
+ $now = $gBitSystem->getUTCTime();
+ try {
+ $this->mDb->query( "UPDATE `".BIT_DB_PREFIX."liberty_content_hits` SET `hits`=`hits`+1, `last_hit`=? WHERE `content_id`=?", [ $now, $this->mContentId ] );
+ if( $this->mDb->Affected_Rows() == 0 ) {
+ $this->mDb->query( "INSERT INTO `".BIT_DB_PREFIX."liberty_content_hits` (`hits`,`last_hit`,`content_id`) VALUES (1,?,?)", [ $now, $this->mContentId ] );
+ }
+ } catch( \Exception $e ) {
+ // Race: concurrent request inserted between our UPDATE (0 rows) and INSERT — harmless
}
- $bindVars[] = $gBitSystem->getUTCTime();
- $bindVars[] = $this->mContentId;
- $this->StartTrans();
- $result = $this->mDb->query( $query, $bindVars );
- $this->CompleteTrans();
}
}
return true;