Class: Gitlab::Database::Partitioning::SlidingListStrategy

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

Direct Known Subclasses

CiSlidingListStrategy

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(model, partitioning_key, next_partition_if:, detach_partition_if:, analyze_interval: nil) ⇒ SlidingListStrategy

Returns a new instance of SlidingListStrategy.



11
12
13
14
15
16
17
18
19
# File 'lib/gitlab/database/partitioning/sliding_list_strategy.rb', line 11

def initialize(model, partitioning_key, next_partition_if:, detach_partition_if:, analyze_interval: nil)
  @model = model
  @partitioning_key = partitioning_key
  @next_partition_if = next_partition_if
  @detach_partition_if = detach_partition_if
  @analyze_interval = analyze_interval

  ensure_partitioning_column_ignored_or_readonly!
end

Instance Attribute Details

#analyze_intervalObject (readonly)

Returns the value of attribute analyze_interval.



7
8
9
# File 'lib/gitlab/database/partitioning/sliding_list_strategy.rb', line 7

def analyze_interval
  @analyze_interval
end

#detach_partition_ifObject (readonly)

Returns the value of attribute detach_partition_if.



7
8
9
# File 'lib/gitlab/database/partitioning/sliding_list_strategy.rb', line 7

def detach_partition_if
  @detach_partition_if
end

#modelObject (readonly)

Returns the value of attribute model.



7
8
9
# File 'lib/gitlab/database/partitioning/sliding_list_strategy.rb', line 7

def model
  @model
end

#next_partition_ifObject (readonly)

Returns the value of attribute next_partition_if.



7
8
9
# File 'lib/gitlab/database/partitioning/sliding_list_strategy.rb', line 7

def next_partition_if
  @next_partition_if
end

#partitioning_keyObject (readonly)

Returns the value of attribute partitioning_key.



7
8
9
# File 'lib/gitlab/database/partitioning/sliding_list_strategy.rb', line 7

def partitioning_key
  @partitioning_key
end

Instance Method Details

#active_partitionObject



70
71
72
73
74
# File 'lib/gitlab/database/partitioning/sliding_list_strategy.rb', line 70

def active_partition
  # The current partitions list is sorted, so the last partition has the highest value
  # This is the only partition that receives inserts.
  current_partitions.last
end

#after_adding_partitionsObject



65
66
67
68
# File 'lib/gitlab/database/partitioning/sliding_list_strategy.rb', line 65

def after_adding_partitions
  active_value = active_partition.value
  model.connection.change_column_default(model.table_name, partitioning_key, active_value)
end

#current_partitionsObject



21
22
23
24
25
# File 'lib/gitlab/database/partitioning/sliding_list_strategy.rb', line 21

def current_partitions
  Gitlab::Database::PostgresPartition.for_parent_table(table_name).map do |partition|
    SingleNumericListPartition.from_sql(table_name, partition.name, partition.condition)
  end.sort
end

#extra_partitionsObject



45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
# File 'lib/gitlab/database/partitioning/sliding_list_strategy.rb', line 45

def extra_partitions
  possibly_extra = current_partitions[0...-1] # Never consider the most recent partition

  extra = possibly_extra.take_while { |p| detach_partition_if.call(p) }

  default_value = current_default_value
  if extra.any? { |p| p.value == default_value }
    Gitlab::AppLogger.error(
      message: "Inconsistent partition detected: partition with value #{current_default_value} should " \
                "not be deleted because it's used as the default value.",
      partition_number: current_default_value,
      table_name: model.table_name
    )

    extra = extra.reject { |p| p.value == default_value }
  end

  extra
end

#initial_partitionObject



37
38
39
# File 'lib/gitlab/database/partitioning/sliding_list_strategy.rb', line 37

def initial_partition
  SingleNumericListPartition.new(table_name, 1)
end

#missing_partitionsObject



27
28
29
30
31
32
33
34
35
# File 'lib/gitlab/database/partitioning/sliding_list_strategy.rb', line 27

def missing_partitions
  if no_partitions_exist?
    [initial_partition]
  elsif next_partition_if.call(active_partition)
    [next_partition]
  else
    []
  end
end

#next_partitionObject



41
42
43
# File 'lib/gitlab/database/partitioning/sliding_list_strategy.rb', line 41

def next_partition
  SingleNumericListPartition.new(table_name, active_partition.value + 1)
end

#no_partitions_exist?Boolean

Returns:

  • (Boolean)


76
77
78
# File 'lib/gitlab/database/partitioning/sliding_list_strategy.rb', line 76

def no_partitions_exist?
  current_partitions.empty?
end

#validate_and_fixObject



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
# File 'lib/gitlab/database/partitioning/sliding_list_strategy.rb', line 80

def validate_and_fix
  unless model.connection_db_config.name ==
      Gitlab::Database.db_config_name(Gitlab::Database::SharedModel.connection)

    Gitlab::AppLogger.warn(
      message: 'Skipping fixing column default because connections mismatch',
      event: :partition_manager_validate_and_fix_connection_mismatch,
      model_connection_name: Gitlab::Database.db_config_name(model.connection),
      shared_connection_name: Gitlab::Database.db_config_name(Gitlab::Database::SharedModel.connection)
    )

    return
  end

  return if no_partitions_exist?

  old_default_value = current_default_value
  expected_default_value = active_partition.value

  if old_default_value != expected_default_value
    with_lock_retries do
      model.connection.execute("LOCK TABLE #{model.table_name} IN ACCESS EXCLUSIVE MODE")

      old_default_value = current_default_value
      expected_default_value = active_partition.value

      if old_default_value == expected_default_value
        Gitlab::AppLogger.warn(
          message: "Table partitions or partition key default value have been changed by another process",
          table_name: table_name,
          default_value: expected_default_value
        )
        raise ActiveRecord::Rollback
      end

      model.connection.change_column_default(model.table_name, partitioning_key, expected_default_value)
      Gitlab::AppLogger.warn(
        message: "Fixed default value of sliding_list_strategy partitioning_key",
        column: partitioning_key,
        table_name: table_name,
        connection_name: model.connection.pool.db_config.name,
        old_value: old_default_value,
        new_value: expected_default_value
      )
    end
  end
end