I provide below the raw MySQL query and also the code in which I do that programatically. If two requests are being done at the same time results in the following error pattern:
SQLSTATE[40001]: Serialization failure: 1213 Deadlock found when trying to get lock; try restarting transaction (SQL:
update user_chats set updated_at = 2018-06-29 10:07:13 where id = 1
)
If I execute the same query but without transaction block around it will work without error with many concurrent calls. Why ? (The transaction aquires lock, right ?)
Is there any way to solve this without locking the entire table ? (Want to try avoid table level locks)
I know that a lock is acquired for inserting/updating/deleting tables in MySql with InnoDB but still do not understand why the deadlock happens here and how to solve it in the most efficient way.
START TRANSACTION;
insert into `user_chat_messages` (`user_chat_id`, `from_user_id`, `content`) values (1, 2, 'dfasfdfk);
update `user_chats` set `updated_at` = '2018-06-28 08:33:14' where `id` = 1;
COMMIT;
Above is the raw query, but I do it in PHP Laravel Query Builder as follows:
/**
* @param UserChatMessageEntity $message
* @return int
* @throws \Exception
*/
public function insertChatMessage(UserChatMessageEntity $message) : int
{
$this->db->beginTransaction();
try
{
$id = $this->db->table('user_chat_messages')->insertGetId([
'user_chat_id' => $message->getUserChatId(),
'from_user_id' => $message->getFromUserId(),
'content' => $message->getContent()
]
);
//TODO results in lock error if many messages are sent same time
$this->db->table('user_chats')
->where('id', $message->getUserChatId())
->update(['updated_at' => date('Y-m-d H:i:s')]);
$this->db->commit();
return $id;
}
catch (\Exception $e)
{
$this->db->rollBack();
throw $e;
}
}
DDL for the tables:
CREATE TABLE user_chat_messages
(
id INT(10) unsigned PRIMARY KEY NOT NULL AUTO_INCREMENT,
user_chat_id INT(10) unsigned NOT NULL,
from_user_id INT(10) unsigned NOT NULL,
content VARCHAR(500) NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL,
CONSTRAINT user_chat_messages_user_chat_id_foreign FOREIGN KEY (user_chat_id) REFERENCES user_chats (id),
CONSTRAINT user_chat_messages_from_user_id_foreign FOREIGN KEY (from_user_id) REFERENCES users (id)
);
CREATE INDEX user_chat_messages_from_user_id_index ON user_chat_messages (from_user_id);
CREATE INDEX user_chat_messages_user_chat_id_index ON user_chat_messages (user_chat_id);
CREATE TABLE user_chats
(
id INT(10) unsigned PRIMARY KEY NOT NULL AUTO_INCREMENT,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL
);