Class: ActiveRecord::Materialized::PartitionState

Inherits:
Object
  • Object
show all
Extended by:
T::Sig
Defined in:
lib/activerecord/materialized/partition_state.rb

Overview

Tracks which partitions of a cold view have been materialized (“fresh”) so a read can decide whether a partition is served from the cache or read through to the source. Warm views are fully materialized and ignore this.

Constant Summary collapse

KeyTuple =
T.type_alias { T::Array[T.untyped] }

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(view_class) ⇒ PartitionState

Returns a new instance of PartitionState.



15
16
17
# File 'lib/activerecord/materialized/partition_state.rb', line 15

def initialize(view_class)
  @view_class = view_class
end

Class Method Details

.cartesian(value_lists) ⇒ Object



86
87
88
89
90
91
92
# File 'lib/activerecord/materialized/partition_state.rb', line 86

def self.cartesian(value_lists)
  return nil if value_lists.any?(&:empty?)

  value_lists.reduce([[]]) do |tuples, values|
    tuples.flat_map { |tuple| values.map { |value| tuple + [value] } }
  end
end

.key_value_lists(conditions, group_keys) ⇒ Object



78
79
80
81
82
83
# File 'lib/activerecord/materialized/partition_state.rb', line 78

def self.key_value_lists(conditions, group_keys)
  normalized = conditions.transform_keys(&:to_s)
  return nil unless normalized.keys.sort == group_keys.sort

  group_keys.map { |column| Array(normalized.fetch(column)).map(&:to_s) }
end

.keys_from(view_class, args) ⇒ Object



55
56
57
58
59
60
61
62
63
64
# File 'lib/activerecord/materialized/partition_state.rb', line 55

def self.keys_from(view_class, args)
  conditions = single_hash(args)
  return nil if conditions.nil?

  group_keys = view_class.maintenance_key_columns
  return nil if group_keys.empty?

  value_lists = key_value_lists(conditions, group_keys)
  value_lists.nil? ? nil : cartesian(value_lists)
end

.single_hash(args) ⇒ Object



67
68
69
70
71
72
# File 'lib/activerecord/materialized/partition_state.rb', line 67

def self.single_hash(args)
  return nil unless args.length == 1

  conditions = args.fetch(0)
  conditions.is_a?(Hash) ? conditions : nil
end

Instance Method Details

#all_fresh?(key_tuples) ⇒ Boolean

Returns:

  • (Boolean)


20
21
22
23
24
25
26
# File 'lib/activerecord/materialized/partition_state.rb', line 20

def all_fresh?(key_tuples)
  return false if key_tuples.empty?

  ensure_table!
  serialized = key_tuples.map { |tuple| serialize(tuple) }.uniq
  scope.where(partition_key: serialized).count == serialized.size
end

#mark_fresh!(key_tuples) ⇒ Object



29
30
31
32
33
34
35
36
# File 'lib/activerecord/materialized/partition_state.rb', line 29

def mark_fresh!(key_tuples)
  return if key_tuples.empty?

  ensure_table!
  key_tuples.uniq.each do |tuple|
    PartitionRecord.create_or_find_by(view_name: view_key, partition_key: serialize(tuple))
  end
end

#mark_stale!(key_tuples) ⇒ Object



39
40
41
42
43
44
# File 'lib/activerecord/materialized/partition_state.rb', line 39

def mark_stale!(key_tuples)
  return if key_tuples.empty?

  ensure_table!
  scope.where(partition_key: key_tuples.map { |tuple| serialize(tuple) }).delete_all
end

#reset!Object



47
48
49
50
# File 'lib/activerecord/materialized/partition_state.rb', line 47

def reset!
  ensure_table!
  scope.delete_all
end