Class: Gitlab::Database::BatchAverageCounter

Inherits:
Object
  • Object
show all
Defined in:
lib/gitlab/database/batch_average_counter.rb

Constant Summary collapse

COLUMN_FALLBACK =
0
DEFAULT_BATCH_SIZE =
1_000
FALLBACK =
-1
MAX_ALLOWED_LOOPS =
10_000
OFFSET_BY_ONE =
1
SLEEP_TIME_IN_SECONDS =

10 msec sleep

0.01

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(relation, column) ⇒ BatchAverageCounter

Returns a new instance of BatchAverageCounter.



15
16
17
18
# File 'lib/gitlab/database/batch_average_counter.rb', line 15

def initialize(relation, column)
  @relation = relation
  @column = wrap_column(relation, column)
end

Instance Attribute Details

#columnObject (readonly)

Returns the value of attribute column.



13
14
15
# File 'lib/gitlab/database/batch_average_counter.rb', line 13

def column
  @column
end

#relationObject (readonly)

Returns the value of attribute relation.



13
14
15
# File 'lib/gitlab/database/batch_average_counter.rb', line 13

def relation
  @relation
end

Instance Method Details

#count(batch_size: nil) ⇒ Object



20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
# File 'lib/gitlab/database/batch_average_counter.rb', line 20

def count(batch_size: nil)
  raise 'BatchAverageCounter can not be run inside a transaction' if transaction_open?

  batch_size = batch_size.presence || DEFAULT_BATCH_SIZE

  start  = column_start
  finish = column_finish

  total_sum = 0
  total_records = 0

  batch_start = start

  while batch_start < finish
    begin
      batch_end      = [batch_start + batch_size, finish].min
      batch_relation = build_relation_batch(batch_start, batch_end)

      # We use `sum` and `count` instead of `average` here to not run into an "average of averages"
      # problem as batches will have different sizes, so we are essentially summing up the values for
      # each batch separately, and then dividing that result on the total number of records.
      batch_sum, batch_count = batch_relation.pick(column.sum, column.count)

      total_sum     += batch_sum.to_i
      total_records += batch_count

      batch_start = batch_end
    rescue ActiveRecord::QueryCanceled => error # rubocop:disable Database/RescueQueryCanceled
      # retry with a safe batch size & warmer cache
      if batch_size >= 2 * DEFAULT_BATCH_SIZE
        batch_size /= 2
      else
        log_canceled_batch_fetch(batch_start, batch_relation.to_sql, error)

        return FALLBACK
      end
    end

    sleep(SLEEP_TIME_IN_SECONDS)
  end

  return FALLBACK if total_records == 0

  total_sum.to_f / total_records
end