Module: Legion::Data::PartitionManager

Defined in:
lib/legion/data/partition_manager.rb

Constant Summary collapse

NOT_POSTGRES =
{ skipped: true, reason: 'not_postgres' }.freeze

Class Method Summary collapse

Class Method Details

.drop_old_partitions(table:, retention_months: 24) ⇒ Object



44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
# File 'lib/legion/data/partition_manager.rb', line 44

def drop_old_partitions(table:, retention_months: 24)
  return NOT_POSTGRES unless postgres?

  cutoff = advance_months(Date.today, -retention_months)
  dropped = []
  retained = []

  partition_names_for(table).each do |part|
    part_date = parse_partition_date(part)
    next unless part_date

    if part_date < cutoff
      Legion::Data.connection.run("DROP TABLE #{part}")
      log_info("Dropped partition #{part}") if logging?
      dropped << part
    else
      retained << part
    end
  end

  { dropped: dropped, retained: retained }
rescue StandardError => e
  log_warn("drop_old_partitions failed for #{table}: #{e.message}") if logging?
  { dropped: [], retained: [], error: e.message }
end

.ensure_partitions(table:, months_ahead: 3) ⇒ Object



9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
# File 'lib/legion/data/partition_manager.rb', line 9

def ensure_partitions(table:, months_ahead: 3)
  return NOT_POSTGRES unless postgres?

  created = []
  existing = []
  base = Date.today

  months_ahead.times do |i|
    target = advance_months(base, i)
    partition = partition_name(table, target)
    from_str  = target.strftime('%Y-%m-%d')
    to_str    = advance_months(target, 1).strftime('%Y-%m-%d')

    ddl = "CREATE TABLE IF NOT EXISTS #{partition} " \
          "PARTITION OF #{table} " \
          "FOR VALUES FROM ('#{from_str}') TO ('#{to_str}')"

    before_count = partition_names_for(table).size
    Legion::Data.connection.run(ddl)
    after_count = partition_names_for(table).size

    if after_count > before_count
      log_info("Created partition #{partition}") if logging?
      created << partition
    else
      existing << partition
    end
  end

  { created: created, existing: existing }
rescue StandardError => e
  log_warn("ensure_partitions failed for #{table}: #{e.message}") if logging?
  { created: [], existing: [], error: e.message }
end

.list_partitions(table:) ⇒ Object



70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
# File 'lib/legion/data/partition_manager.rb', line 70

def list_partitions(table:)
  return NOT_POSTGRES unless postgres?

  sql = <<~SQL
    SELECT c.relname AS name,
           pg_get_expr(c.relpartbound, c.oid) AS bound
    FROM   pg_inherits i
    JOIN   pg_class    p ON p.oid = i.inhparent
    JOIN   pg_class    c ON c.oid = i.inhrelid
    WHERE  p.relname = '#{table}'
    ORDER  BY c.relname
  SQL

  Legion::Data.connection.fetch(sql).map do |row|
    from_val, to_val = parse_bound(row[:bound])
    { name: row[:name], from: from_val, to: to_val }
  end
rescue StandardError => e
  log_warn("list_partitions failed for #{table}: #{e.message}") if logging?
  []
end