Class: PartitionGardener::Strategy::IntegerRange

Inherits:
Object
  • Object
show all
Includes:
Naming, CursorColumns, RequiresDefaultPartition
Defined in:
lib/partition_gardener/strategy/integer_range.rb

Constant Summary collapse

DEFAULT_ACTIVE_ID_WIDTH =
10_000_000
DEFAULT_CURRENT_BAND_SIZE =
1_000_000
DEFAULT_ARCHIVE_BAND_SIZE =
10_000_000

Instance Method Summary collapse

Methods included from CursorColumns

#cursor_columns, #partition_key_base

Methods included from RequiresDefaultPartition

#default_partition_drain_where_condition, #default_partition_required?, #rebalance_default_drain_where_condition

Methods included from Naming

current_partition_name, default_partition_name, future_partition_name, open_partition_name, rebalance_staging_partition_name

Constructor Details

#initialize(config) ⇒ IntegerRange

Returns a new instance of IntegerRange.



12
13
14
# File 'lib/partition_gardener/strategy/integer_range.rb', line 12

def initialize(config)
  @config = config
end

Instance Method Details

#active_windowObject



16
17
18
19
20
# File 'lib/partition_gardener/strategy/integer_range.rb', line 16

def active_window
  active_lo = @config.fetch(:active_id_lo) { 0 }
  active_hi = active_lo + active_id_width
  {start: active_lo, end: active_hi}
end

#archive_bucket?(bucket) ⇒ Boolean

Returns:

  • (Boolean)


128
129
130
# File 'lib/partition_gardener/strategy/integer_range.rb', line 128

def archive_bucket?(bucket)
  bucket < active_window[:start]
end

#archive_bucket_from_partition_name(partition_name) ⇒ Object



136
137
138
139
140
141
# File 'lib/partition_gardener/strategy/integer_range.rb', line 136

def archive_bucket_from_partition_name(partition_name)
  match = partition_name.match(/^#{Regexp.escape(table_name)}_ids_(\d+)_(\d+)$/)
  return unless match

  match[1].to_i
end

#attached_tail_segmentsObject



40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
# File 'lib/partition_gardener/strategy/integer_range.rb', line 40

def attached_tail_segments
  window = active_window

  Connection.attached_partitions(table_name).filter_map do |partition|
    next if partition.default
    next unless managed_tail_partition?(partition.name, window: window)

    Plan::Segment.new(
      name: partition.name,
      range_start: partition.range_start,
      range_end: partition.range_end,
      kind: segment_kind_for(partition.name, window: window)
    )
  end
end

#bucket_where_condition(bucket) ⇒ Object



122
123
124
125
126
# File 'lib/partition_gardener/strategy/integer_range.rb', line 122

def bucket_where_condition(bucket)
  partition_key_column = @config[:partition_key_column]
  end_range = bucket + current_band_size
  "#{partition_key_column} >= #{bucket} AND #{partition_key_column} < #{end_range}"
end

#build_planObject



22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
# File 'lib/partition_gardener/strategy/integer_range.rb', line 22

def build_plan
  window = active_window
  heatmap = collect_heatmap(window)
  hot_buckets = hot_buckets_in_window(heatmap, window)

  segments = Layout::ZoneSegments.build_filler_and_hot_segments(
    table_name: table_name,
    buckets: each_bucket_in_range(window[:start], window[:end]),
    hot_buckets: hot_buckets,
    active_start: window[:start],
    active_end: window[:end],
    hot_bucket_name: method(:hot_bucket_partition_name),
    bucket_end: ->(bucket) { [bucket + current_band_size, window[:end]].min }
  )

  Plan::Result.new(segments: segments, hot_buckets: hot_buckets)
end

#collect_heatmap(window) ⇒ Object



56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
# File 'lib/partition_gardener/strategy/integer_range.rb', line 56

def collect_heatmap(window)
  bucket_counts = Hash.new(0)
  dedicated_partition_counts = {}
  dedicated_partitions = hot_bucket_partitions_in_window(window).to_h

  heatmap_source_partitions(window).each do |partition_name|
    counts = counts_by_bucket_in_partition(partition_name, window)
    counts.each { |bucket, count| bucket_counts[bucket] += count }

    bucket = dedicated_partitions[partition_name]
    dedicated_partition_counts[bucket] = counts.values.sum if bucket
  end

  {
    bucket_counts: bucket_counts,
    default_rows: default_row_count,
    dedicated_partition_counts: dedicated_partition_counts
  }
end

#current_and_future_where_condition(window: active_window) ⇒ Object



117
118
119
120
# File 'lib/partition_gardener/strategy/integer_range.rb', line 117

def current_and_future_where_condition(window: active_window)
  partition_key_column = @config[:partition_key_column]
  "#{partition_key_column} >= #{window[:start]}"
end

#current_band_sizeObject



156
157
158
# File 'lib/partition_gardener/strategy/integer_range.rb', line 156

def current_band_size
  @config.fetch(:current_band_size, DEFAULT_CURRENT_BAND_SIZE)
end

#future_bucket?(bucket) ⇒ Boolean

Returns:

  • (Boolean)


132
133
134
# File 'lib/partition_gardener/strategy/integer_range.rb', line 132

def future_bucket?(bucket)
  bucket >= active_window[:end]
end

#hot_bucket_partition_name(bucket) ⇒ Object



151
152
153
154
# File 'lib/partition_gardener/strategy/integer_range.rb', line 151

def hot_bucket_partition_name(bucket)
  end_range = [bucket + current_band_size, active_window[:end]].min
  "#{table_name}_ids_#{bucket}_#{end_range}"
end

#hot_buckets_in_window(heatmap, window) ⇒ Object



76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
# File 'lib/partition_gardener/strategy/integer_range.rb', line 76

def hot_buckets_in_window(heatmap, window)
  hot_buckets = Set.new

  heatmap[:bucket_counts].each do |bucket, row_count|
    next unless bucket_in_window?(bucket, window)
    next unless row_count >= split_row_threshold

    hot_buckets << bucket
  end

  dedicated_counts = heatmap.fetch(:dedicated_partition_counts, {})
  hot_bucket_partitions_in_window(window).each do |_partition_name, bucket|
    row_count = dedicated_counts[bucket]
    next if row_count.nil?

    if row_count >= split_row_threshold
      hot_buckets << bucket
    else
      hot_buckets.delete(bucket)
    end
  end

  hot_buckets.sort
end

#managed_tail_partition_names(window: active_window) ⇒ Object



101
102
103
104
105
106
107
108
# File 'lib/partition_gardener/strategy/integer_range.rb', line 101

def managed_tail_partition_names(window: active_window)
  Connection.attached_partitions(table_name).filter_map do |partition|
    next if partition.default
    next unless managed_tail_partition?(partition.name, window: window)

    partition.name
  end
end

#segment_for_values_clause(segment) ⇒ Object



143
144
145
146
147
148
149
# File 'lib/partition_gardener/strategy/integer_range.rb', line 143

def segment_for_values_clause(segment)
  if segment.range_end == :max
    "FROM (#{segment.range_start}) TO (MAXVALUE)"
  else
    "FROM (#{segment.range_start}) TO (#{segment.range_end})"
  end
end

#tail_slot_name?(partition_name) ⇒ Boolean

Returns:

  • (Boolean)


110
111
112
113
114
115
# File 'lib/partition_gardener/strategy/integer_range.rb', line 110

def tail_slot_name?(partition_name)
  partition_name == current_partition_name(table_name) ||
    partition_name == open_partition_name(table_name) ||
    partition_name == future_partition_name(table_name) ||
    partition_name.match?(/^#{Regexp.escape(table_name)}_open_\d+$/)
end