From 94495f984c0a81d32b6f748fd977848dc5c6721e Mon Sep 17 00:00:00 2001
From: Grzegorz Bizon <grzesiek.bizon@gmail.com>
Date: Tue, 14 Feb 2017 12:20:02 +0100
Subject: [PATCH] Use new pipeline retry service with optimistic locking

---
 app/models/ci/pipeline.rb                 | 10 ++---
 app/services/ci/retry_build_service.rb    |  2 +
 app/services/ci/retry_pipeline_service.rb | 54 ++++++++++++++---------
 3 files changed, 39 insertions(+), 27 deletions(-)

diff --git a/app/models/ci/pipeline.rb b/app/models/ci/pipeline.rb
index 8d43f3051ee..49bd2e1d7f0 100644
--- a/app/models/ci/pipeline.rb
+++ b/app/models/ci/pipeline.rb
@@ -224,13 +224,9 @@ module Ci
         end
     end
 
-    def retry_failed(user)
-      Gitlab::OptimisticLocking.retry_lock(
-        builds.latest.failed_or_canceled) do |failed_or_canceled|
-          failed_or_canceled.select(&:retryable?).each do |build|
-            Ci::Build.retry(build, user)
-          end
-        end
+    def retry_failed(current_user)
+      Ci::RetryPipelineService.new(project, current_user)
+        .execute(self)
     end
 
     def mark_as_processable_after_stage(stage_idx)
diff --git a/app/services/ci/retry_build_service.rb b/app/services/ci/retry_build_service.rb
index e470bd6ee6a..009fbeaff20 100644
--- a/app/services/ci/retry_build_service.rb
+++ b/app/services/ci/retry_build_service.rb
@@ -1,6 +1,8 @@
 module Ci
   class RetryBuildService < ::BaseService
     def execute(build)
+      # return unless build.retryable?
+
       self.retry(build).tap do |new_build|
         MergeRequests::AddTodoWhenBuildFailsService
           .new(build.project, current_user)
diff --git a/app/services/ci/retry_pipeline_service.rb b/app/services/ci/retry_pipeline_service.rb
index b062c50e82d..1b931455fb8 100644
--- a/app/services/ci/retry_pipeline_service.rb
+++ b/app/services/ci/retry_pipeline_service.rb
@@ -7,34 +7,48 @@ module Ci
         raise Gitlab::Access::AccessDeniedError
       end
 
-      ##
-      # Reprocess builds in subsequent stages
-      #
-      pipeline.builds
-        .after_stage(resume_stage.index)
-        .failed_or_canceled.find_each do |build|
-          Ci::RetryBuildService
-            .new(project, current_user)
-            .reprocess(build)
-        end
-
-      ##
-      # Mark skipped builds as processable again
-      #
       pipeline.mark_as_processable_after_stage(resume_stage.index)
 
-      ##
-      # Retry builds in the first unsuccessful stage
-      #
-      resume_stage.builds.failed_or_canceled.find_each do |build|
-        Ci::RetryBuildService
-          .new(project, current_user)
+      retryable_builds_in_subsequent_stages do |build|
+        Ci::RetryBuildService.new(project, current_user)
+          .reprocess(build)
+      end
+
+      retryable_builds_in_first_unsuccessful_stage do |build|
+        Ci::RetryBuildService.new(project, current_user)
           .retry(build)
       end
     end
 
     private
 
+    def retryable_builds_in_subsequent_stages
+      relation = @pipeline.builds
+        .after_stage(resume_stage.index)
+        .failed_or_canceled
+
+      each_retryable_build_with_locking(relation) do |build|
+        yield build
+      end
+    end
+
+    def retryable_builds_in_first_unsuccessful_stage
+      relation = resume_stage.builds.failed_or_canceled
+
+      each_retryable_build_with_locking(relation) do |build|
+        yield build
+      end
+    end
+
+    def each_retryable_build_with_locking(relation)
+      Gitlab::OptimisticLocking.retry_lock(relation) do |builds|
+        builds.find_each do |build|
+          next unless build.retryable?
+          yield build
+        end
+      end
+    end
+
     def resume_stage
       @resume_stage ||= @pipeline.stages.find do |stage|
         stage.failed? || stage.canceled?
-- 
2.30.9