fix: review #19 — WebhookChannel conversation race condition (FOR UPDATE transaction)
Universal: Auto Version Bump / Version Bump (push) Successful in 11s
Universal: Pre-Release / Build Pre-Release (${{ inputs.stability || github.ref_name }}) (push) Successful in 16s

This commit is contained in:
Jonathan Miller
2026-06-21 17:58:17 -05:00
parent fc50ee337a
commit 2822d0e955
@@ -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;
}
}