Class: ProjectStatistics

Inherits:
ApplicationRecord show all
Includes:
AfterCommitQueue, CounterAttribute
Defined in:
app/models/project_statistics.rb

Constant Summary collapse

COLUMNS_TO_REFRESH =
[:repository_size, :wiki_size, :lfs_objects_size, :commit_count, :snippets_size, :uploads_size, :container_registry_size].freeze
INCREMENTABLE_COLUMNS =
[
  :pipeline_artifacts_size,
  :snippets_size
].freeze
NAMESPACE_RELATABLE_COLUMNS =
[:repository_size, :wiki_size, :lfs_objects_size, :uploads_size, :container_registry_size].freeze
STORAGE_SIZE_COMPONENTS =
[
  :repository_size,
  :wiki_size,
  :lfs_objects_size,
  :build_artifacts_size,
  :packages_size,
  :snippets_size,
  :uploads_size
].freeze

Constants included from Gitlab::ExclusiveLeaseHelpers

Gitlab::ExclusiveLeaseHelpers::FailedToObtainLockError

Constants inherited from ApplicationRecord

ApplicationRecord::MAX_PLUCK

Constants included from ResetOnUnionError

ResetOnUnionError::MAX_RESET_PERIOD

Class Method Summary collapse

Instance Method Summary collapse

Methods included from CounterAttribute

#bulk_increment_counter, #counter, #counter_attribute_enabled?, #current_counter, #execute_after_commit_callbacks, #finalize_refresh, #increment_amount, #increment_counter, #initiate_refresh!, #update_counters_with_lease

Methods included from AfterCommitQueue

#run_after_commit, #run_after_commit_or_now

Methods included from Gitlab::ExclusiveLeaseHelpers

#in_lock

Methods inherited from ApplicationRecord

cached_column_list, #create_or_load_association, declarative_enum, default_select_columns, id_in, id_not_in, iid_in, pluck_primary_key, 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 SensitiveSerializableHash

#serializable_hash

Class Method Details

.bulk_increment_statistic(project, key, increments) ⇒ Object



128
129
130
131
132
133
134
# File 'app/models/project_statistics.rb', line 128

def self.bulk_increment_statistic(project, key, increments)
  return if project.pending_delete?

  project.statistics.try do |project_statistics|
    project_statistics.bulk_increment_statistic(key, increments)
  end
end

.increment_statistic(project, key, increment) ⇒ Object

For counter attributes, storage_size will be refreshed after the counter is flushed, through counter_attribute_after_commit

For non-counter attributes, storage_size is updated depending on key => [columns] in INCREMENTABLE_COLUMNS



120
121
122
123
124
125
126
# File 'app/models/project_statistics.rb', line 120

def self.increment_statistic(project, key, increment)
  return if project.pending_delete?

  project.statistics.try do |project_statistics|
    project_statistics.increment_statistic(key, increment)
  end
end

Instance Method Details

#bulk_increment_statistic(key, increments) ⇒ Object

Raises:

  • (ArgumentError)


142
143
144
145
146
# File 'app/models/project_statistics.rb', line 142

def bulk_increment_statistic(key, increments)
  raise ArgumentError, "Cannot increment attribute: #{key}" unless incrementable_attribute?(key)

  bulk_increment_counter(key, increments)
end

#increment_statistic(key, increment) ⇒ Object

Raises:

  • (ArgumentError)


136
137
138
139
140
# File 'app/models/project_statistics.rb', line 136

def increment_statistic(key, increment)
  raise ArgumentError, "Cannot increment attribute: #{key}" unless incrementable_attribute?(key)

  increment_counter(key, increment)
end

#refresh!(only: []) ⇒ Object



48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
# File 'app/models/project_statistics.rb', line 48

def refresh!(only: [])
  return if Gitlab::Database.read_only?

  columns_to_update = only.empty? ? COLUMNS_TO_REFRESH : COLUMNS_TO_REFRESH & only
  columns_to_update.each do |column|
    public_send("update_#{column}") # rubocop:disable GitlabSecurity/PublicSend
  end

  if only.empty? || only.any? { |column| NAMESPACE_RELATABLE_COLUMNS.include?(column) }
    schedule_namespace_aggregation_worker
  end

  detect_race_on_record(log_fields: { caller: __method__, attributes: columns_to_update }) do
    save!
  end
end

#refresh_storage_size!Object

Since this incremental update method does not update the storage_size directly, we have to update the storage_size separately in an after_commit action.



110
111
112
113
114
# File 'app/models/project_statistics.rb', line 110

def refresh_storage_size!
  detect_race_on_record(log_fields: { caller: __method__, attributes: :storage_size }) do
    self.class.where(id: id).update_all("storage_size = #{storage_size_sum}")
  end
end

#snippets_sizeObject



104
105
106
# File 'app/models/project_statistics.rb', line 104

def snippets_size
  super.to_i
end

#total_repository_sizeObject



44
45
46
# File 'app/models/project_statistics.rb', line 44

def total_repository_size
  repository_size + lfs_objects_size
end

#update_commit_countObject



65
66
67
# File 'app/models/project_statistics.rb', line 65

def update_commit_count
  self.commit_count = project.repository.commit_count
end

#update_container_registry_sizeObject



89
90
91
# File 'app/models/project_statistics.rb', line 89

def update_container_registry_size
  self.container_registry_size = project.container_repositories_size || 0
end

#update_lfs_objects_sizeObject



81
82
83
# File 'app/models/project_statistics.rb', line 81

def update_lfs_objects_size
  self.lfs_objects_size = LfsObject.joins(:lfs_objects_projects).where(lfs_objects_projects: { project_id: project.id }).sum(:size)
end

#update_repository_sizeObject



69
70
71
# File 'app/models/project_statistics.rb', line 69

def update_repository_size
  self.repository_size = project.repository.recent_objects_size.megabytes
end

#update_snippets_sizeObject



77
78
79
# File 'app/models/project_statistics.rb', line 77

def update_snippets_size
  self.snippets_size = project.snippets.with_statistics.sum(:repository_size)
end

#update_uploads_sizeObject



85
86
87
# File 'app/models/project_statistics.rb', line 85

def update_uploads_size
  self.uploads_size = project.uploads.sum(:size)
end

#update_wiki_sizeObject



73
74
75
# File 'app/models/project_statistics.rb', line 73

def update_wiki_size
  self.wiki_size = project.wiki.repository.size * 1.megabyte
end

#wiki_sizeObject

‘wiki_size` and `snippets_size` have no default value in the database and the column can be nil. This means that, when the columns were added, all rows had nil values on them. Therefore, any call to any of those methods will return nil instead of 0.

These two methods provide consistency and avoid returning nil.



100
101
102
# File 'app/models/project_statistics.rb', line 100

def wiki_size
  super.to_i
end