From b592ff47f839c6167b505292cf2329120e941467 Mon Sep 17 00:00:00 2001 From: Flavio Wuensche Date: Sat, 18 Apr 2026 16:28:04 +0200 Subject: [PATCH] Fix deadlock in Api::PushesController by touching project once per push The Report after_save callback touched metric.project on every report save, hammering the same projects row N times per push transaction and causing PG::TRDeadlockDetected under concurrent pushes (CHERRY-RAILS-6Q). Move the project touch to the controller so it runs once per push. Co-Authored-By: Claude Opus 4.7 --- app/controllers/api/pushes_controller.rb | 2 ++ app/models/report.rb | 5 +---- test/controllers/api/pushes_controller_test.rb | 6 ++++++ test/models/metric_test.rb | 6 ------ 4 files changed, 9 insertions(+), 10 deletions(-) diff --git a/app/controllers/api/pushes_controller.rb b/app/controllers/api/pushes_controller.rb index 7a70c8b1..d362022e 100644 --- a/app/controllers/api/pushes_controller.rb +++ b/app/controllers/api/pushes_controller.rb @@ -34,6 +34,8 @@ def create end ) end + + current_project.touch end render json: { status: :ok }, status: :created diff --git a/app/models/report.rb b/app/models/report.rb index c59e3558..7ca34d89 100644 --- a/app/models/report.rb +++ b/app/models/report.rb @@ -3,10 +3,7 @@ class Report < ApplicationRecord belongs_to :metric - after_save do - metric.touch - metric.project.touch - end + after_save { metric.touch } has_many :occurrences, dependent: :destroy diff --git a/test/controllers/api/pushes_controller_test.rb b/test/controllers/api/pushes_controller_test.rb index fdf6e537..ce271996 100644 --- a/test/controllers/api/pushes_controller_test.rb +++ b/test/controllers/api/pushes_controller_test.rb @@ -168,6 +168,12 @@ class Api::PushesControllerTest < ActionDispatch::IntegrationTest assert_equal 24, metric_with_value.reports.last.value end + it 'bumps project.updated_at after a push' do + create(:project, name: 'cherrypush/cherry-app', user: user, updated_at: 1.week.ago) + post(api_push_path, params: { api_key: user.api_key, **payload }, as: :json) + assert_equal Date.current, Project.sole.reload.updated_at.to_date + end + it 'adds up value_by_owner to existing value_by_owner by uuid' do post(api_push_path, params: { api_key: user.api_key, **payload }, as: :json) metric_with_owners = Metric.find_by(name: 'missing coverage') diff --git a/test/models/metric_test.rb b/test/models/metric_test.rb index 48d9b295..c22febe4 100644 --- a/test/models/metric_test.rb +++ b/test/models/metric_test.rb @@ -11,7 +11,6 @@ class ProjectTest < ActiveSupport::TestCase it 'updates the updated_at field when a new report is created' do create(:report, metric: metric) assert_equal Time.current.to_date, metric.reload.updated_at.to_date - assert_equal Time.current.to_date, project.reload.updated_at.to_date end end @@ -38,11 +37,6 @@ class ProjectTest < ActiveSupport::TestCase metric.destroy! assert_equal 1.week.ago.to_date, project.reload.updated_at.to_date end - - it 'updates the project updated_at field when creating a report for an existing metric' do - create(:report, metric: metric) - assert_equal Date.current, project.reload.updated_at.to_date - end end describe 'when chart metrics are present' do