generated from MokoConsulting/Template-Joomla
fix: review #19 — WebhookChannel conversation race condition (FOR UPDATE transaction)
This commit is contained in:
@@ -82,29 +82,41 @@ class WebhookChannelHelper
|
||||
{
|
||||
$db = Factory::getContainer()->get(DatabaseInterface::class);
|
||||
|
||||
$db->setQuery($db->getQuery(true)
|
||||
->select('id')
|
||||
->from('#__mokosuitesupport_conversations')
|
||||
->where('channel_user_id = ' . $db->quote($channelUserId))
|
||||
->where($db->quoteName('channel') . ' = ' . $db->quote($channel))
|
||||
->where($db->quoteName('status') . ' IN (' . $db->quote('open') . ',' . $db->quote('assigned') . ')')
|
||||
->order('started_at DESC'), 0, 1);
|
||||
$existingId = (int) $db->loadResult();
|
||||
$db->transactionStart();
|
||||
|
||||
if ($existingId) {
|
||||
return $existingId;
|
||||
try {
|
||||
// Lock to prevent duplicate conversation creation from concurrent webhooks
|
||||
$db->setQuery(
|
||||
'SELECT id FROM #__mokosuitesupport_conversations'
|
||||
. ' WHERE channel_user_id = ' . $db->quote($channelUserId)
|
||||
. ' AND ' . $db->quoteName('channel') . ' = ' . $db->quote($channel)
|
||||
. ' AND ' . $db->quoteName('status') . ' IN (' . $db->quote('open') . ',' . $db->quote('assigned') . ')'
|
||||
. ' ORDER BY started_at DESC LIMIT 1'
|
||||
. ' FOR UPDATE'
|
||||
);
|
||||
$existingId = (int) $db->loadResult();
|
||||
|
||||
if ($existingId) {
|
||||
$db->transactionCommit();
|
||||
return $existingId;
|
||||
}
|
||||
|
||||
$result = ConversationHelper::create($channel, null, $channelUserId);
|
||||
$convId = $result->conversation_id;
|
||||
|
||||
// Store channel user ID atomically
|
||||
$db->setQuery($db->getQuery(true)
|
||||
->update('#__mokosuitesupport_conversations')
|
||||
->set('channel_user_id = ' . $db->quote($channelUserId))
|
||||
->where('id = ' . (int) $convId));
|
||||
$db->execute();
|
||||
|
||||
$db->transactionCommit();
|
||||
|
||||
return $convId;
|
||||
} catch (\Throwable $e) {
|
||||
$db->transactionRollback();
|
||||
throw $e;
|
||||
}
|
||||
|
||||
$result = ConversationHelper::create($channel, null, $channelUserId);
|
||||
$convId = $result->conversation_id;
|
||||
|
||||
// Store channel user ID
|
||||
$db->setQuery($db->getQuery(true)
|
||||
->update('#__mokosuitesupport_conversations')
|
||||
->set('channel_user_id = ' . $db->quote($channelUserId))
|
||||
->where('id = ' . (int) $convId));
|
||||
$db->execute();
|
||||
|
||||
return $convId;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user