/app/services/ci/pipeline_processing/atomic_processing_service/status_collection.rb

http://github.com/gitlabhq/gitlabhq · Ruby · 142 lines · 95 code · 23 blank · 24 comment · 4 complexity · 325bdabcc794af4ff586f4f600759a39 MD5 · raw file

  1. # frozen_string_literal: true
  2. module Ci
  3. module PipelineProcessing
  4. class AtomicProcessingService
  5. class StatusCollection
  6. include Gitlab::Utils::StrongMemoize
  7. attr_reader :pipeline
  8. # We use these columns to perform an efficient
  9. # calculation of a status
  10. STATUSES_COLUMNS = [
  11. :id, :name, :status, :allow_failure,
  12. :stage_idx, :processed, :lock_version
  13. ].freeze
  14. def initialize(pipeline)
  15. @pipeline = pipeline
  16. @stage_statuses = {}
  17. @prior_stage_statuses = {}
  18. end
  19. # This method updates internal status for given ID
  20. def set_processable_status(id, status, lock_version)
  21. processable = all_statuses_by_id[id]
  22. return unless processable
  23. processable[:status] = status
  24. processable[:lock_version] = lock_version
  25. end
  26. # This methods gets composite status of all processables
  27. def status_of_all
  28. status_for_array(all_statuses, dag: false)
  29. end
  30. # This methods gets composite status for processables with given names
  31. def status_for_names(names, dag:)
  32. name_statuses = all_statuses_by_name.slice(*names)
  33. status_for_array(name_statuses.values, dag: dag)
  34. end
  35. # This methods gets composite status for processables before given stage
  36. def status_for_prior_stage_position(position)
  37. strong_memoize("status_for_prior_stage_position_#{position}") do
  38. stage_statuses = all_statuses_grouped_by_stage_position
  39. .select { |stage_position, _| stage_position < position }
  40. status_for_array(stage_statuses.values.flatten, dag: false)
  41. end
  42. end
  43. # This methods gets a list of processables for a given stage
  44. def created_processable_ids_for_stage_position(current_position)
  45. all_statuses_grouped_by_stage_position[current_position]
  46. .to_a
  47. .select { |processable| processable[:status] == 'created' }
  48. .map { |processable| processable[:id] }
  49. end
  50. # This methods gets composite status for processables at a given stage
  51. def status_for_stage_position(current_position)
  52. strong_memoize("status_for_stage_position_#{current_position}") do
  53. stage_statuses = all_statuses_grouped_by_stage_position[current_position].to_a
  54. status_for_array(stage_statuses.flatten, dag: false)
  55. end
  56. end
  57. # This method returns a list of all processable, that are to be processed
  58. def processing_processables
  59. all_statuses.lazy.reject { |status| status[:processed] }
  60. end
  61. private
  62. def status_for_array(statuses, dag:)
  63. # TODO: This is hack to support
  64. # the same exact behaviour for Atomic and Legacy processing
  65. # that DAG is blocked from executing if dependent is not "complete"
  66. if dag && statuses.any? { |status| HasStatus::COMPLETED_STATUSES.exclude?(status[:status]) }
  67. return 'pending'
  68. end
  69. result = Gitlab::Ci::Status::Composite
  70. .new(statuses)
  71. .status
  72. result || 'success'
  73. end
  74. def all_statuses_grouped_by_stage_position
  75. strong_memoize(:all_statuses_by_order) do
  76. all_statuses.group_by { |status| status[:stage_idx].to_i }
  77. end
  78. end
  79. def all_statuses_by_id
  80. strong_memoize(:all_statuses_by_id) do
  81. all_statuses.map do |row|
  82. [row[:id], row]
  83. end.to_h
  84. end
  85. end
  86. def all_statuses_by_name
  87. strong_memoize(:statuses_by_name) do
  88. all_statuses.map do |row|
  89. [row[:name], row]
  90. end.to_h
  91. end
  92. end
  93. # rubocop: disable CodeReuse/ActiveRecord
  94. def all_statuses
  95. # We fetch all relevant data in one go.
  96. #
  97. # This is more efficient than relying
  98. # on PostgreSQL to calculate composite status
  99. # for us
  100. #
  101. # Since we need to reprocess everything
  102. # we can fetch all of them and do processing
  103. # ourselves.
  104. strong_memoize(:all_statuses) do
  105. raw_statuses = pipeline
  106. .statuses
  107. .latest
  108. .ordered_by_stage
  109. .pluck(*STATUSES_COLUMNS)
  110. raw_statuses.map do |row|
  111. STATUSES_COLUMNS.zip(row).to_h
  112. end
  113. end
  114. end
  115. # rubocop: enable CodeReuse/ActiveRecord
  116. end
  117. end
  118. end
  119. end