Class: ActiveRecord::Materialized::MaintenanceStore Private
- Inherits:
-
Object
- Object
- ActiveRecord::Materialized::MaintenanceStore
- Defined in:
- lib/activerecord/materialized/maintenance_store.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.
Persists a view's pending maintenance (a delta or a scope) in its metadata row.
Instance Method Summary collapse
- #clear! ⇒ Object private
-
#consume_pending_delta! ⇒ Object
private
Atomically consume the pending scoped delta under a row lock on the metadata row, so two cross-process cycles can't both consume it and recompute the same partition twice — which duplicated rows on Postgres (no unique constraint, no gap locks under READ COMMITTED).
-
#initialize(view_class) ⇒ MaintenanceStore
constructor
private
A new instance of MaintenanceStore.
-
#merge!(delta) ⇒ Object
private
Accumulates pending maintenance of either kind.
- #pending ⇒ Object private
- #pending_delta ⇒ Object private
-
#with_consumed_summary_delta ⇒ Object
private
Atomically consume the pending SummaryDelta and apply it under a row lock on the metadata row, so concurrent cycles across servers can't apply the same additive delta twice.
Constructor Details
#initialize(view_class) ⇒ MaintenanceStore
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 MaintenanceStore.
9 10 11 |
# File 'lib/activerecord/materialized/maintenance_store.rb', line 9 def initialize(view_class) @view_class = view_class end |
Instance Method Details
#clear! ⇒ 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.
68 69 70 |
# File 'lib/activerecord/materialized/maintenance_store.rb', line 68 def clear! .clear_maintenance_payload! end |
#consume_pending_delta! ⇒ 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.
Atomically consume the pending scoped delta under a row lock on the metadata row, so two cross-process cycles can't both consume it and recompute the same partition twice — which duplicated rows on Postgres (no unique constraint, no gap locks under READ COMMITTED). Whoever clears the payload owns the delta; a loser reads an empty payload and gets nil (a benign no-op). Returns the delta, or nil. Unlike the additive summary path (which applies inside its lock for crash-atomicity), the scoped recompute is idempotent (delete + re-aggregate), so the caller applies OUTSIDE this lock, keeping it short; a crash between consume and apply loses the delta, which self-healing reconciliation (#64) repairs on the next tick.
42 43 44 45 46 47 48 49 50 |
# File 'lib/activerecord/materialized/maintenance_store.rb', line 42 def consume_pending_delta! .record.with_lock do # blocking FOR UPDATE on the metadata row + a transaction delta = pending_delta next nil if delta.nil? clear! delta end end |
#merge!(delta) ⇒ 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.
Accumulates pending maintenance of either kind. A view's mode is fixed within a window, so existing pending is always the same kind. Once the tracked partitions exceed the configured cap, the payload collapses to a single full recompute, so a bulk write spanning many partitions stays O(1) per write instead of re-serializing an ever-growing blob.
18 19 20 21 22 23 |
# File 'lib/activerecord/materialized/maintenance_store.rb', line 18 def merge!(delta) combined = combine(pending, delta) return drop_cold_full_recompute! if cold_full_recompute?(combined) .record_maintenance_payload!(combined.serialize) end |
#pending ⇒ 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.
25 26 27 28 |
# File 'lib/activerecord/materialized/maintenance_store.rb', line 25 def pending payload = .maintenance_payload SummaryDelta.deserialize(payload) || MaintenanceDelta.deserialize(payload) end |
#pending_delta ⇒ 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.
30 31 32 |
# File 'lib/activerecord/materialized/maintenance_store.rb', line 30 def pending_delta MaintenanceDelta.deserialize(.maintenance_payload) end |
#with_consumed_summary_delta ⇒ 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.
Atomically consume the pending SummaryDelta and apply it under a row lock on the metadata row, so concurrent cycles across servers can't apply the same additive delta twice. Yields the delta to the block (which applies it) inside the locked transaction, so a failed apply rolls back the clear and the delta is retried. Returns the block's result, or nil when another cycle already consumed it — the loser blocks on the lock, then reads an empty payload (a benign no-op). DML-only (no DDL/callbacks), so the lock holds for the whole critical section.
58 59 60 61 62 63 64 65 66 |
# File 'lib/activerecord/materialized/maintenance_store.rb', line 58 def with_consumed_summary_delta .record.with_lock do # blocking FOR UPDATE on the metadata row + a transaction delta = SummaryDelta.deserialize(.maintenance_payload) next nil if delta.nil? clear! yield delta end end |