Class: MergeRequest::CommitsMetadata
- Inherits:
-
ApplicationRecord
- Object
- ActiveRecord::Base
- ApplicationRecord
- MergeRequest::CommitsMetadata
- Includes:
- PartitionedTable, ShaAttribute
- Defined in:
- app/models/merge_request/commits_metadata.rb
Overview
rubocop:disable Style/ClassAndModuleChildren, Gitlab/BoundedContexts – Same as the rest of the models under MergeRequest namespace
Constant Summary
Constants inherited from ApplicationRecord
Constants included from HasCheckConstraints
HasCheckConstraints::NOT_NULL_CHECK_PATTERN
Constants included from ResetOnColumnErrors
ResetOnColumnErrors::MAX_RESET_PERIOD
Class Method Summary collapse
-
.bulk_find(project_id, shas) ⇒ Object
Finds many commits by project_id and array of SHAs in bulk.
-
.bulk_find_or_create(project_id, commit_rows) ⇒ Object
Finds or creates rows for the given project ID and commit rows.
-
.find_or_create(metadata = {}) ⇒ Object
Creates a new row, or returns an existing one if a row already exists.
- .oldest_merge_request_id_per_commit(project_id, shas) ⇒ Object
- .oldest_merge_requests_commits_from_metadata(project_id, shas) ⇒ Object
Methods inherited from ApplicationRecord
===, cached_column_list, #create_or_load_association, current_transaction, declarative_enum, default_select_columns, delete_all_returning, #deleted_from_database?, id_in, id_not_in, iid_in, nullable_column?, primary_key_in, #readable_by?, safe_ensure_unique, safe_find_or_create_by, safe_find_or_create_by!, #to_ability_name, underscore, where_exists, where_not_exists, with_fast_read_statement_timeout, without_order
Methods included from Organizations::Sharding
Methods included from ResetOnColumnErrors
#reset_on_union_error, #reset_on_unknown_attribute_error
Methods included from Gitlab::SensitiveSerializableHash
Class Method Details
.bulk_find(project_id, shas) ⇒ Object
Finds many commits by project_id and array of SHAs in bulk. The return value is an array of ID and SHA pairs.
53 54 55 56 57 58 59 60 61 62 63 |
# File 'app/models/merge_request/commits_metadata.rb', line 53 def self.bulk_find(project_id, shas) rows = [] shas.each_slice(1_000) do |slice| # rubocop:disable Database/AvoidUsingPluckWithoutLimit -- Already limited to 1K SHAs rows.concat(where(project_id: project_id, sha: slice).pluck(:id, :sha)) # rubocop:enable Database/AvoidUsingPluckWithoutLimit end rows end |
.bulk_find_or_create(project_id, commit_rows) ⇒ Object
Finds or creates rows for the given project ID and commit rows.
The commit_rows argument must be an array of hashes. Each hash should have the following keys:
-
:commit_author_id
-
:committer_id
-
:raw_sha
-
:authored_date
-
:committed_date
-
:message
The return value is a hash that maps ID of each found or created commits metadata row to a SHA.
78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 |
# File 'app/models/merge_request/commits_metadata.rb', line 78 def self.bulk_find_or_create(project_id, commit_rows) mapping = {} create = [] # rubocop:disable Database/AvoidUsingPluckWithoutLimit -- This is an array of hashes commits_shas = commit_rows.pluck(:raw_sha) # rubocop:enable Database/AvoidUsingPluckWithoutLimit # Find commits that are already existing in `merge_request_commits_metadata` # table. Store them in `mapping` hash so we can map this with rows in # `merge_request_diff_commits` table by SHA. # # `row.last` is the SHA and `row.first` is the ID. bulk_find(project_id, commits_shas).each do |row| mapping[row.last] = row.first end # Check if there are commits that don't exist yet in the mapping. It means # they're not stored in the DB yet at this point so we need to create them. commit_rows.each do |commit_row| next if mapping[commit_row[:raw_sha]] create << { project_id: project_id, commit_author_id: commit_row[:commit_author_id], committer_id: commit_row[:committer_id], sha: commit_row[:raw_sha], authored_date: commit_row[:authored_date], committed_date: commit_row[:committed_date], message: commit_row[:message] } end return mapping if create.empty? sha_attribute = Gitlab::Database::ShaAttribute.new create.each_slice(1_000) do |slice| insert_all( slice, returning: %w[id sha], unique_by: :index_merge_request_commits_metadata_on_project_id_and_sha ).each do |row| # Need to deserialize sha since it's stored as binary in the database # and we need to match by its text value. mapping[sha_attribute.deserialize(row['sha'])] = row['id'] end end # It's possible for commits to be inserted concurrently, # resulting in the above insert not returning anything. Here we get any # remaining commits that were created concurrently. # # `row.last` is the SHA and `row.first` is the ID. bulk_find(project_id, commits_shas.reject { |sha| mapping.key?(sha) }).each do |row| mapping[row.last] = row.first end mapping end |
.find_or_create(metadata = {}) ⇒ Object
Creates a new row, or returns an existing one if a row already exists.
39 40 41 42 43 44 45 46 47 48 49 |
# File 'app/models/merge_request/commits_metadata.rb', line 39 def self.find_or_create( = {}) find_or_create_by!(project_id: ['project_id'], sha: ['sha']) do |record| record.committer = ['committer'] record. = ['commit_author'] record. = ['message'] record. = ['authored_date'] record.committed_date = ['committed_date'] end rescue ActiveRecord::RecordNotUnique retry end |
.oldest_merge_request_id_per_commit(project_id, shas) ⇒ Object
139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 |
# File 'app/models/merge_request/commits_metadata.rb', line 139 def self.oldest_merge_request_id_per_commit(project_id, shas) # This method is defined here and not on MergeRequest, otherwise the SHA # values used in the WHERE below won't be encoded correctly. return [] if shas.empty? = (project_id, shas).to_a found_shas = .map(&:sha) missing_shas = shas - found_shas # We query the SHA from `merge_request_commits_metadata` table first and # fallback to querying them from `merge_request_diff_commits` if there are no matches # This is needed temporarily while `merge_request_commits_metadata_id` is not fully populated if missing_shas.any? diff_commits_results = MergeRequestDiffCommit.oldest_merge_request_id_per_commit(project_id, missing_shas) + diff_commits_results.to_a else end end |
.oldest_merge_requests_commits_from_metadata(project_id, shas) ⇒ Object
159 160 161 162 163 164 165 166 167 168 169 170 171 172 |
# File 'app/models/merge_request/commits_metadata.rb', line 159 def self.(project_id, shas) select('sha', 'MIN(merge_requests.id) AS merge_request_id') .where(project_id: project_id, sha: shas) .joins(:merge_request_diff_commits) .joins( 'INNER JOIN merge_request_diffs ON merge_request_diffs.id = merge_request_diff_commits.merge_request_diff_id' ) .joins('INNER JOIN merge_requests ON merge_requests.latest_merge_request_diff_id = merge_request_diffs.id') .where(merge_requests: { target_project_id: project_id, state_id: MergeRequest.available_states[:merged] }) .group(:sha) end |