From fbe5bf762d616c8bbe6e824bd0713e20f0751b74 Mon Sep 17 00:00:00 2001 From: Stan Hu Date: Fri, 25 Sep 2015 01:14:54 -0700 Subject: [PATCH 1/2] Fix bug where projects would appear to be stuck in the forked import state A race condition existed between when Rails committed the `import_status` to `started` and when the Sidekiq worker forked a project. If this fork was quick, it's possible that the worker would attempt to move into the `finished` state before the `started` state took effect. As mentioned in https://github.com/mperham/sidekiq/wiki/Problems-and-Troubleshooting#cannot-find-modelname-with-id12345, we should delay the worker to ensure the DB has a chance to update. Closes #2736 --- CHANGELOG | 1 + app/models/project.rb | 4 +++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/CHANGELOG b/CHANGELOG index 20b6dce1764..256e445d7df 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,6 +1,7 @@ Please view this file on the master branch, on stable branches it's out of date. v 8.1.0 (unreleased) + - Fix bug where projects would appear to be stuck in the forked import state (Stan Hu) - Show CI status on all pages where commits list is rendered - Automatically enable CI when push .gitlab-ci.yml file to repository - Move CI charts to project graphs area diff --git a/app/models/project.rb b/app/models/project.rb index a7ea1236b86..5deddb2fbc4 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -276,8 +276,10 @@ class Project < ActiveRecord::Base end def add_import_job + # Schedule these jobs after 2 seconds to ensure DB changes to import_status + # are saved by the time the workers start if forked? - unless RepositoryForkWorker.perform_async(id, forked_from_project.path_with_namespace, self.namespace.path) + unless RepositoryForkWorker.perform_in(2.seconds, id, forked_from_project.path_with_namespace, self.namespace.path) import_fail end else From bd2991b4618528a719b03c46ceb7befb230f0dd9 Mon Sep 17 00:00:00 2001 From: Stan Hu Date: Fri, 25 Sep 2015 01:46:13 -0700 Subject: [PATCH 2/2] Use after_commit_queue to schedule import job immediately --- Gemfile | 2 ++ Gemfile.lock | 3 +++ app/models/project.rb | 13 ++++++++----- 3 files changed, 13 insertions(+), 5 deletions(-) diff --git a/Gemfile b/Gemfile index ad9fef38da1..6950091b2b0 100644 --- a/Gemfile +++ b/Gemfile @@ -121,6 +121,8 @@ end # State machine gem "state_machine", '~> 1.2.0' +# Run events after state machine commits +gem 'after_commit_queue' # Issue tags gem 'acts-as-taggable-on', '~> 3.4' diff --git a/Gemfile.lock b/Gemfile.lock index e306c1f56e6..4386c6b9abb 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -42,6 +42,8 @@ GEM acts-as-taggable-on (3.5.0) activerecord (>= 3.2, < 5) addressable (2.3.8) + after_commit_queue (1.1.0) + rails (>= 3.0) annotate (2.6.10) activerecord (>= 3.2, <= 4.3) rake (~> 10.4) @@ -787,6 +789,7 @@ DEPENDENCIES activerecord-session_store (~> 0.1.0) acts-as-taggable-on (~> 3.4) addressable (~> 2.3.8) + after_commit_queue annotate (~> 2.6.0) asana (~> 0.0.6) asciidoctor (~> 1.5.2) diff --git a/app/models/project.rb b/app/models/project.rb index 5deddb2fbc4..e912c48467d 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -39,6 +39,7 @@ class Project < ActiveRecord::Base include Gitlab::VisibilityLevel include Referable include Sortable + include AfterCommitQueue extend Gitlab::ConfigHelper extend Enumerize @@ -191,7 +192,7 @@ class Project < ActiveRecord::Base state :finished state :failed - after_transition any => :started, do: :add_import_job + after_transition any => :started, do: :schedule_add_import_job after_transition any => :finished, do: :clear_import_data end @@ -275,15 +276,17 @@ class Project < ActiveRecord::Base id && persisted? end + def schedule_add_import_job + run_after_commit(:add_import_job) + end + def add_import_job - # Schedule these jobs after 2 seconds to ensure DB changes to import_status - # are saved by the time the workers start if forked? - unless RepositoryForkWorker.perform_in(2.seconds, id, forked_from_project.path_with_namespace, self.namespace.path) + unless RepositoryForkWorker.perform_async(id, forked_from_project.path_with_namespace, self.namespace.path) import_fail end else - RepositoryImportWorker.perform_in(2.seconds, id) + RepositoryImportWorker.perform_async(id) end end