Open pushcx opened 5 years ago
Attempt at addressing this: https://github.com/lobsters/lobsters/commit/f0549673b447bba97553c346efdaf71f5194c4e0
This is still present, unfortunately. We've seen three of them on comment creation since the previous commit.
FWIW, they've entirely stopped happening in voting code since that tweak. Strong suggestion this only happens inside of transactions.
Haven't seen this in a while. Or the lobsters/lobsters#783, now that I think about it.
Dammit. We're immediately back to this.
An ActiveRecord::Deadlocked occurred in comments#create:
Mysql2::Error: Deadlock found when trying to get lock; try restarting transaction
app/models/story.rb:819:in `update_comments_count!'
-------------------------------
Request:
-------------------------------
* URL : https://lobste.rs/comments
* HTTP Method: POST
* IP address : [elided]
* Parameters : {"authenticity_token"=>"[FILTERED]", "story_id"=>"eprtpo", "_method"=>"post", "parent_comment_short_id"=>"[elided]", "comment"=>"[elided] ", "hat_id"=>"", "show_tree_lines"=>"true", "controller"=>"comments", "action"=>"create"}
* Timestamp : 2023-09-14 20:22:55 -0500
* Server : lobste.rs
* Rails root : /srv/lobste.rs/http
* Process: 1072491
I've been rerunning SHOW ENGINE INNODB STATUS;
as I get these 500s about once a week. They're always related to comment creation, and all but one have included a story or comment. My current best guess is that https://github.com/lobsters/lobsters/pull/899 introduced this. I don't remember what problem we had at the time to add the transaction. (@hmadison, do you?)
Comment
creation triggers a lot of callbacks and writes to stories
because comment scores influence story scores. Here's a quick log from dev:
Started POST "/comments" for 127.0.0.1 at 2023-10-02 18:33:14 -0500 [8/42018]
Processing by CommentsController#create as */*
Parameters: {"authenticity_token"=>"[FILTERED]", "story_id"=>"5jpbgl", "comment"=>"asdfasdf", "hat_id"=>"", "show_tree_lines"=>"true"}
User Load (0.5ms) SELECT `users`.* FROM `users` WHERE `users`.`session_token` = 'MICkvIaRlgk01ZZtjdHh8DWGaJGZ94VfZcdGddBeDBrIK5FKR3U6uYK8ZhIt' LIMIT 1 /*controller:comments,action:create*/
↳ app/controllers/application_controller.rb:46:in `authenticate_user'
Request 127.0.0.1 POST /comments user: 78 pushcx
Keystore Pluck (0.3ms) SELECT `keystores`.`value` FROM `keystores` WHERE `keystores`.`key` = 'traffic:intensity' LIMIT 1 /*controller:comments,action:create*/
↳ app/models/keystore.rb:15:in `value_for'
Story Load (0.2ms) SELECT `stories`.* FROM `stories` WHERE `stories`.`short_id` = '5jpbgl' ORDER BY `stories`.`id` ASC LIMIT 1 /*controller:comments,action:create*/
↳ app/controllers/comments_controller.rb:18:in `create'
User Load (0.2ms) SELECT `users`.* FROM `users` WHERE `users`.`id` = 16063 LIMIT 1 /*controller:comments,action:create*/
↳ app/models/story.rb:520:in `is_gone?'
Comment Load (1.6ms) SELECT `comments`.* FROM `comments` WHERE `comments`.`user_id` = 78 AND `comments`.`story_id` = 98554 AND `comments`.`parent_comment_id` IS NULL AND `comments`.`comment` = 'asdfasdf' LIMIT 1 /*controller:comments,action:create*/
↳ app/controllers/comments_controller.rb:41:in `create'
Comment Exists? (0.8ms) SELECT 1 AS one FROM `comments` WHERE `comments`.`short_id` = 'gtwfqv' LIMIT 1 /*controller:comments,action:create*/
↳ app/models/short_id.rb:40:in `valid?'
TRANSACTION (0.3ms) BEGIN /*controller:comments,action:create*/
↳ app/models/keystore.rb:50:in `block in incremented_value_for'
(2.2ms) INSERT INTO keystores (`key`, `value`) VALUES ('thread_id', 1) ON DUPLICATE KEY UPDATE `value` = `value` + 1 /*controller:comments,action:create*/
↳ app/models/keystore.rb:50:in `block in incremented_value_for'
Keystore Pluck (0.3ms) SELECT `keystores`.`value` FROM `keystores` WHERE `keystores`.`key` = 'thread_id' LIMIT 1 /*controller:comments,action:create*/
↳ app/models/keystore.rb:15:in `value_for'
TRANSACTION (6.7ms) COMMIT /*controller:comments,action:create*/
↳ app/models/keystore.rb:42:in `incremented_value_for'
TRANSACTION (0.3ms) BEGIN /*controller:comments,action:create*/
↳ app/models/short_id.rb:40:in `valid?'
Comment Exists? (0.6ms) SELECT 1 AS one FROM `comments` WHERE `comments`.`short_id` = 'w03bow' LIMIT 1 /*controller:comments,action:create*/
↳ app/models/short_id.rb:40:in `valid?'
(0.6ms) INSERT INTO keystores (`key`, `value`) VALUES ('thread_id', 1) ON DUPLICATE KEY UPDATE `value` = `value` + 1 /*controller:comments,action:create*/
↳ app/models/keystore.rb:50:in `block in incremented_value_for'
CACHE Keystore Pluck (0.0ms) SELECT `keystores`.`value` FROM `keystores` WHERE `keystores`.`key` = 'thread_id' LIMIT 1
↳ app/models/keystore.rb:15:in `value_for'
Comment Create (4.5ms) INSERT INTO `comments` (`created_at`, `updated_at`, `short_id`, `story_id`, `confidence_order`, `user_id`, `thread_id`, `comment`, `confidence`, `markeddown_comment`) VALUES ('2023-10-02 23:33:14', '2023-10-02 23:33:14', 'w03bow', 98554, x'000000', 78, 524562, 'asdfasdf', 0.1828847834138887, '<p>asdfasdf</p>\n') /*controller:comments,action:create*/
↳ app/controllers/comments_controller.rb:54:in `block in create'
Vote Load (2.7ms) SELECT `votes`.* FROM `votes` WHERE `votes`.`user_id` = 78 AND `votes`.`story_id` = 98554 AND `votes`.`comment_id` = 450516 ORDER BY `votes`.`id` ASC LIMIT 1 /*controller:comments,action:create*/
↳ app/models/vote.rb:99:in `vote_thusly_on_story_or_comment_for_user_because'
User Load (0.3ms) SELECT `users`.* FROM `users` WHERE `users`.`id` = 78 LIMIT 1 /*controller:comments,action:create*/
↳ app/models/vote.rb:120:in `vote_thusly_on_story_or_comment_for_user_because'
Story Load (0.3ms) SELECT `stories`.* FROM `stories` WHERE `stories`.`id` = 98554 LIMIT 1 /*controller:comments,action:create*/
↳ app/models/vote.rb:120:in `vote_thusly_on_story_or_comment_for_user_because'
Vote Create (0.3ms) INSERT INTO `votes` (`user_id`, `story_id`, `comment_id`, `vote`, `updated_at`) VALUES (78, 98554, 450516, 1, '2023-10-02 23:33:14') /*controller:comments,action:create*/
↳ app/models/vote.rb:120:in `vote_thusly_on_story_or_comment_for_user_because'
Comment Load (0.4ms) SELECT `comments`.* FROM `comments` INNER JOIN `stories` `story` ON `story`.`id` = `comments`.`story_id` WHERE (`story`.`merged_story_id` = 98554 OR `comments`.`story_id` = 98554) /*controller:comments,action:create*/
↳ app/models/story.rb:819:in `update_comments_count!'
Story Update (0.3ms) UPDATE `stories` SET `stories`.`comments_count` = 9 WHERE `stories`.`id` = 98554 /*controller:comments,action:create*/
↳ app/models/story.rb:819:in `update_comments_count!'
Tag Sum (0.3ms) SELECT SUM(`tags`.`hotness_mod`) FROM `tags` INNER JOIN `taggings` ON `tags`.`id` = `taggings`.`tag_id` WHERE `taggings`.`story_id` = 98554 /*controller:comments,action:create*/
↳ app/models/story.rb:341:in `calculated_hotness'
Comment Sum (0.6ms) SELECT SUM(comments.score + 1) FROM `comments` INNER JOIN `stories` `story` ON `story`.`id` = `comments`.`story_id` WHERE (`story`.`merged_story_id` = 98554 OR `comments`.`story_id` = 98554) AND `comments`.`user_id` != 16063 /*controller:comments,action:create*/
↳ app/models/story.rb:345:in `calculated_hotness'
Story Load (0.3ms) SELECT `stories`.* FROM `stories` WHERE `stories`.`merged_story_id` = 98554 /*controller:comments,action:create*/
↳ app/models/story.rb:348:in `map'
Story Update (0.9ms) UPDATE `stories` SET `stories`.`hotness` = -21402.4652598 WHERE `stories`.`id` = 98554 /*controller:comments,action:create*/
↳ app/models/story.rb:624:in `recalculate_hotness!'
(0.5ms) INSERT INTO keystores (`key`, `value`) VALUES ('user:78:comments_posted', 1) ON DUPLICATE KEY UPDATE `value` = `value` + 1 /*controller:comments,action:create*/
↳ app/models/keystore.rb:50:in `block in incremented_value_for'
Keystore Pluck (0.3ms) SELECT `keystores`.`value` FROM `keystores` WHERE `keystores`.`key` = 'user:78:comments_posted' LIMIT 1 /*controller:comments,action:create*/
↳ app/models/keystore.rb:15:in `value_for'
(0.5ms) UPDATE comments SET
score = (select coalesce(sum(vote), 0) from votes where comment_id = comments.id),
flags = (select count(*) from votes where comment_id = comments.id and vote = -1),
confidence = 0.18288478341388867,
confidence_order = concat(lpad(char(65536 - floor(((confidence - -0.2) * 65535) / 1.2) using binary), 2, '0'), char(id & 0xff using binary))
WHERE id = 450516
/*controller:comments,action:create*/
↳ app/models/comment.rb:366:in `update_score_and_recalculate!'
Tag Sum (0.3ms) SELECT SUM(`tags`.`hotness_mod`) FROM `tags` INNER JOIN `taggings` ON `tags`.`id` = `taggings`.`tag_id` WHERE `taggings`.`story_id` = 98554 /*controller:comments,action:create*/
↳ app/models/story.rb:341:in `calculated_hotness'
Comment Sum (0.3ms) SELECT SUM(comments.score + 1) FROM `comments` INNER JOIN `stories` `story` ON `story`.`id` = `comments`.`story_id` WHERE (`story`.`merged_story_id` = 98554 OR `comments`.`story_id` = 98554) AND `comments`.`user_id` != 16063 /*controller:comments,action:create*/
↳ app/models/story.rb:345:in `calculated_hotness'
Story Update (0.2ms) UPDATE `stories` SET `stories`.`hotness` = -21402.4652598 WHERE `stories`.`id` = 98554 /*controller:comments,action:create*/
↳ app/models/story.rb:624:in `recalculate_hotness!'
TRANSACTION (10.5ms) COMMIT /*controller:comments,action:create*/
↳ app/controllers/comments_controller.rb:54:in `create'
Rendered comments/_comment.html.erb (Duration: 1.5ms | Allocations: 2366)
Rendered comments/_postedreply.html.erb (Duration: 1.7ms | Allocations: 2599)
Completed 200 OK in 56ms (Views: 2.2ms | ActiveRecord: 38.2ms | Allocations: 32424)
So the deadlock is happening because Comment creation (and upvoting, or Story creation) are locking the same tables in different orders. Probably the right fix is to remove those transactions. I really don't want to try to reorganize that rat's nest of callbacks to hit tables in the same order because there's no way to enforce it, we'd surely reintroduce the deadlock before long.
My understanding is that we needed the transactions to ensure that all of the calculations worked properly.
In the last couple weeks, every couple days I get an exception from a query failing with a complaint about a deadlock:
(Headers, env vars, and traceback follow, but there's nothing interesting there.) This almost always happens on an story or comment upvote (the most common kind of write on this table), but I have seen a couple on creating a comment.
I poked around a bit and saw someone suggest running
SHOW ENGINE INNODB STATUS;
, so here's that output:Given our generally low db usage we probably should never see errors like this. Can anyone read this to guess at what's misconfigured?