Class: ActiveRecord::Materialized::Metadata Private

Inherits:
Object
  • Object
show all
Defined in:
lib/activerecord/materialized/metadata.rb,
lib/activerecord/materialized/metadata/schema.rb,
lib/activerecord/materialized/metadata/timestamps.rb,
lib/activerecord/materialized/metadata/reconciliation.rb,
lib/activerecord/materialized/metadata/maintenance_payload.rb

Overview

This class is part of a private API. You should avoid using this class if possible, as it may be removed or be changed in the future.

Reads and writes a view's freshness metadata row (dirty, warm, last_refreshed_at, …).

Defined Under Namespace

Modules: MaintenancePayload, Reconciliation, Schema, Timestamps

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(view_class) ⇒ Metadata

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Returns a new instance of Metadata.



16
17
18
19
# File 'lib/activerecord/materialized/metadata.rb', line 16

def initialize(view_class)
  @view_class = view_class
  @schema_ensured = false
end

Instance Attribute Details

#view_classObject (readonly)

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.



14
15
16
# File 'lib/activerecord/materialized/metadata.rb', line 14

def view_class
  @view_class
end

Instance Method Details

#bump_fresh_set_generation!Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Advance the fresh-set epoch atomically so every mark stamped with an earlier value is treated as stale — used by PartitionState#reset! to invalidate a cold view's whole fresh set on a widen. Ensures the row exists first (create_or_find_by absorbs a concurrent first insert), then a single atomic SQL increment (never a read-then-write), so two concurrent resets can't lost-update the epoch — a populate that captured any earlier value still fails all_fresh?. Portable across adapters.



76
77
78
79
80
81
82
83
84
85
# File 'lib/activerecord/materialized/metadata.rb', line 76

def bump_fresh_set_generation!
  ensure_schema!
  MetadataRecord.create_or_find_by(view_name: view_class.view_key)
  connection = view_class.connection
  table = connection.quote_table_name(::ActiveRecord::Materialized.)
  connection.execute(
    "UPDATE #{table} SET fresh_set_generation = fresh_set_generation + 1 " \
    "WHERE view_name = #{connection.quote(view_class.view_key)}"
  )
end

#clear_maintenance_payload!Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.



112
113
114
# File 'lib/activerecord/materialized/metadata.rb', line 112

def clear_maintenance_payload!
  MaintenancePayload.clear!(self)
end

#dirty?Boolean

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Returns:

  • (Boolean)


52
53
54
# File 'lib/activerecord/materialized/metadata.rb', line 52

def dirty?
  !!record.dirty?
end

#ensure_schema!Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.



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

def ensure_schema!
  return if @schema_ensured

  Schema.ensure_table!(view_class)
  @schema_ensured = true
end

#fresh_set_generationObject

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

The current fresh-set epoch for this view (see PartitionState). A populate captures it before its source read; all_fresh? honours only marks stamped with the current value. (#120)



67
68
69
# File 'lib/activerecord/materialized/metadata.rb', line 67

def fresh_set_generation
  record.fresh_set_generation
end

#last_refreshed_atObject

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.



36
37
38
# File 'lib/activerecord/materialized/metadata.rb', line 36

def last_refreshed_at
  record.last_refreshed_at
end

#maintenance_payloadObject

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.



108
109
110
# File 'lib/activerecord/materialized/metadata.rb', line 108

def maintenance_payload
  MaintenancePayload.fetch(self)
end

#mark_dirty!Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.



100
101
102
# File 'lib/activerecord/materialized/metadata.rb', line 100

def mark_dirty!
  record.update!(dirty: true)
end

#mark_failed!(error) ⇒ Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.



135
136
137
138
139
140
# File 'lib/activerecord/materialized/metadata.rb', line 135

def mark_failed!(error)
  record.update!(
    refreshing: false,
    last_error: error.message
  )
end

#mark_refreshed!(row_count:, duration_ms:) ⇒ Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.



123
124
125
126
127
128
129
130
131
132
133
# File 'lib/activerecord/materialized/metadata.rb', line 123

def mark_refreshed!(row_count:, duration_ms:)
  record.update!(
    last_refreshed_at: Timestamps.current,
    refreshing: false,
    dirty: false,
    row_count: row_count,
    refresh_duration_ms: duration_ms,
    last_error: nil,
    maintenance_payload: nil
  )
end

#mark_refreshing!Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.



116
117
118
119
120
121
# File 'lib/activerecord/materialized/metadata.rb', line 116

def mark_refreshing!
  record.update!(
    refreshing: true,
    last_error: nil
  )
end

#mark_warm!Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.



61
62
63
# File 'lib/activerecord/materialized/metadata.rb', line 61

def mark_warm!
  record.update!(warm: true)
end

#recordObject

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Single entry point for the metadata row. Provision the schema once per instance — re-ensuring on every access thrashes the schema cache and makes high-write workloads quadratic.



24
25
26
27
# File 'lib/activerecord/materialized/metadata.rb', line 24

def record
  ensure_schema!
  MetadataRecord.find_or_initialize_by(view_name: view_class.view_key)
end

#record_maintenance_payload!(payload) ⇒ Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.



104
105
106
# File 'lib/activerecord/materialized/metadata.rb', line 104

def record_maintenance_payload!(payload)
  MaintenancePayload.record!(self, payload)
end

#refresh_duration_msObject

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.



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

def refresh_duration_ms
  record.refresh_duration_ms
end

#refreshing?Boolean

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Returns:

  • (Boolean)


40
41
42
# File 'lib/activerecord/materialized/metadata.rb', line 40

def refreshing?
  !!record.refreshing?
end

#row_countObject

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.



44
45
46
# File 'lib/activerecord/materialized/metadata.rb', line 44

def row_count
  record.row_count
end

#stale?(max_staleness: view_class.resolved_max_staleness) ⇒ Boolean

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Returns:

  • (Boolean)


87
88
89
90
91
92
93
94
95
96
97
98
# File 'lib/activerecord/materialized/metadata.rb', line 87

def stale?(max_staleness: view_class.resolved_max_staleness)
  return true if dirty?
  return true if last_refreshed_at.nil?
  return false if max_staleness.nil?

  # A reconcile verifies contents against the source, so it resets the staleness clock like a
  # refresh — measure age from whichever happened later. The replica-lag budget tightens the
  # window: a replica read trails the primary by the lag, so the view goes stale that much
  # sooner to keep replica reads within max_staleness.
  freshest = [last_refreshed_at, record.last_reconciled_at].compact.max_by(&:to_time)
  freshest.to_time < Timestamps.threshold(effective_staleness(max_staleness)).to_time
end

#warm?Boolean

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Warm once fully materialized via rebuild!; cold views read through.

Returns:

  • (Boolean)


57
58
59
# File 'lib/activerecord/materialized/metadata.rb', line 57

def warm?
  !!record.warm?
end