Class: PartitionGardener::Strategy::DateRange

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

Constant Summary collapse

DEFAULT_ACTIVE_MONTHS =
12
DEFAULT_ACTIVE_YEARS =
2

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) ⇒ DateRange

Returns a new instance of DateRange.



11
12
13
# File 'lib/partition_gardener/strategy/date_range.rb', line 11

def initialize(config)
  @config = config
end

Instance Method Details

#active_windowObject



15
16
17
18
19
20
21
22
# File 'lib/partition_gardener/strategy/date_range.rb', line 15

def active_window
  bucket = date_bucket
  active_start = DateBucket.beginning_of_bucket(today, bucket)
  active_span = active_span_for(bucket)
  active_end = DateBucket.add_buckets(active_start, active_span, bucket)

  {start: active_start, end: active_end}
end

#archive_bucket?(bucket) ⇒ Boolean

Returns:

  • (Boolean)


139
140
141
# File 'lib/partition_gardener/strategy/date_range.rb', line 139

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

#archive_bucket_from_partition_name(partition_name) ⇒ Object



147
148
149
# File 'lib/partition_gardener/strategy/date_range.rb', line 147

def archive_bucket_from_partition_name(partition_name)
  DateBucket.archive_bucket_from_partition_name(table_name, partition_name, date_bucket)
end

#attached_tail_segmentsObject



48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
# File 'lib/partition_gardener/strategy/date_range.rb', line 48

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_counts_in_partition(partition_name) ⇒ Object



161
162
163
# File 'lib/partition_gardener/strategy/date_range.rb', line 161

def bucket_counts_in_partition(partition_name)
  counts_by_bucket_in_partition(partition_name)
end

#bucket_where_condition(bucket) ⇒ Object



132
133
134
135
136
137
# File 'lib/partition_gardener/strategy/date_range.rb', line 132

def bucket_where_condition(bucket)
  partition_key_column = @config[:partition_key_column]
  start_range = beginning_of_bucket(bucket)
  end_range = end_of_bucket(bucket)
  "#{partition_key_column} >= #{connection.quote(start_range)}::date AND #{partition_key_column} < #{connection.quote(end_range)}::date"
end

#build_planObject



24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
# File 'lib/partition_gardener/strategy/date_range.rb', line 24

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

  segments = if year_bucket?
    Layout::CalendarYear.build_segments(
      config: @config,
      active_start: window[:start],
      active_end: window[:end],
      hot_years: hot_buckets
    )
  else
    Layout::SlidingWindow.build_segments(
      config: @config,
      active_start: window[:start],
      active_end: window[:end],
      hot_months: hot_buckets
    )
  end

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

#collect_heatmap(window) ⇒ Object



64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
# File 'lib/partition_gardener/strategy/date_range.rb', line 64

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)
    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



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

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

#future_bucket?(bucket) ⇒ Boolean

Returns:

  • (Boolean)


143
144
145
# File 'lib/partition_gardener/strategy/date_range.rb', line 143

def future_bucket?(bucket)
  beginning_of_bucket(bucket) > beginning_of_bucket(today)
end

#hot_buckets_in_window(heatmap, window) ⇒ Object



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
# File 'lib/partition_gardener/strategy/date_range.rb', line 84

def hot_buckets_in_window(heatmap, window)
  return [] if rolling_current_layout?

  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



111
112
113
114
115
116
117
118
# File 'lib/partition_gardener/strategy/date_range.rb', line 111

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



151
152
153
154
155
156
157
158
159
# File 'lib/partition_gardener/strategy/date_range.rb', line 151

def segment_for_values_clause(segment)
  if segment.range_end == :max
    "FROM ('#{segment.range_start}') TO (MAXVALUE)"
  elsif segment.hash_partition?
    "WITH (modulus #{segment.range_start[:modulus]}, remainder #{segment.range_start[:remainder]})"
  else
    "FROM ('#{segment.range_start}') TO ('#{segment.range_end}')"
  end
end

#tail_slot_name?(partition_name) ⇒ Boolean

Returns:

  • (Boolean)


120
121
122
123
124
125
# File 'lib/partition_gardener/strategy/date_range.rb', line 120

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