Class: ProjectStatistics

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

Constant Summary collapse

COLUMNS_TO_REFRESH =
[:repository_size, :wiki_size, :lfs_objects_size, :commit_count, :snippets_size].freeze
INCREMENTABLE_COLUMNS =
{
  build_artifacts_size: %i[storage_size],
  packages_size: %i[storage_size],
  pipeline_artifacts_size: %i[storage_size],
  snippets_size: %i[storage_size]
}.freeze
NAMESPACE_RELATABLE_COLUMNS =
[:repository_size, :wiki_size, :lfs_objects_size].freeze

Class Method Summary collapse

Instance Method Summary collapse

Methods included from AfterCommitQueue

#run_after_commit, #run_after_commit_or_now

Methods inherited from ApplicationRecord

at_most, id_in, id_not_in, iid_in, pluck_primary_key, primary_key_in, safe_ensure_unique, safe_find_or_create_by, safe_find_or_create_by!, underscore, without_order

Class Method Details

.columns_to_increment(key, amount) ⇒ Object


107
108
109
110
111
112
113
114
115
116
117
# File 'app/models/project_statistics.rb', line 107

def self.columns_to_increment(key, amount)
  updates = ["#{key} = COALESCE(#{key}, 0) + (#{amount})"]

  if (additional = INCREMENTABLE_COLUMNS[key])
    additional.each do |column|
      updates << "#{column} = COALESCE(#{column}, 0) + (#{amount})"
    end
  end

  update_all(updates.join(', '))
end

.increment_statistic(project_id, key, amount) ⇒ Object

Since this incremental update method does not call update_storage_size above, we have to update the storage_size here as additional column. Additional columns are updated depending on key => [columns], which allows to update statistics which are and also those which aren't included in storage_size or any other additional summary column in the future.

Raises:

  • (ArgumentError)

99
100
101
102
103
104
105
# File 'app/models/project_statistics.rb', line 99

def self.increment_statistic(project_id, key, amount)
  raise ArgumentError, "Cannot increment attribute: #{key}" unless INCREMENTABLE_COLUMNS.key?(key)
  return if amount == 0

  where(project_id: project_id)
    .columns_to_increment(key, amount)
end

Instance Method Details

#refresh!(only: []) ⇒ Object


31
32
33
34
35
36
37
38
39
40
41
42
43
# File 'app/models/project_statistics.rb', line 31

def refresh!(only: [])
  COLUMNS_TO_REFRESH.each do |column, generator|
    if only.empty? || only.include?(column)
      public_send("update_#{column}") # rubocop:disable GitlabSecurity/PublicSend
    end
  end

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

  save!
end

#snippets_sizeObject


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

def snippets_size
  super.to_i
end

#total_repository_sizeObject


27
28
29
# File 'app/models/project_statistics.rb', line 27

def total_repository_size
  repository_size + lfs_objects_size
end

#update_commit_countObject


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

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

#update_lfs_objects_sizeObject


61
62
63
# File 'app/models/project_statistics.rb', line 61

def update_lfs_objects_size
  self.lfs_objects_size = project.lfs_objects.sum(:size)
end

#update_repository_sizeObject


49
50
51
# File 'app/models/project_statistics.rb', line 49

def update_repository_size
  self.repository_size = project.repository.size * 1.megabyte
end

#update_snippets_sizeObject


57
58
59
# File 'app/models/project_statistics.rb', line 57

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

#update_storage_sizeObject


81
82
83
84
85
86
87
88
89
90
91
92
# File 'app/models/project_statistics.rb', line 81

def update_storage_size
  storage_size = repository_size + wiki_size + lfs_objects_size + build_artifacts_size + packages_size
  # The `snippets_size` column was added on 20200622095419 but db/post_migrate/20190527194900_schedule_calculate_wiki_sizes.rb
  # might try to update project statistics before the `snippets_size` column has been created.
  storage_size += snippets_size if self.class.column_names.include?('snippets_size')

  # The `pipeline_artifacts_size` column was added on 20200817142800 but db/post_migrate/20190527194900_schedule_calculate_wiki_sizes.rb
  # might try to update project statistics before the `pipeline_artifacts_size` column has been created.
  storage_size += pipeline_artifacts_size if self.class.column_names.include?('pipeline_artifacts_size')

  self.storage_size = storage_size
end

#update_wiki_sizeObject


53
54
55
# File 'app/models/project_statistics.rb', line 53

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, because `default_value_for` works with new records, not existing ones.

These two methods provide consistency and avoid returning nil.


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

def wiki_size
  super.to_i
end