Class: SolidObjects::WakeUpAdapters::Postgresql
- Inherits:
-
Object
- Object
- SolidObjects::WakeUpAdapters::Postgresql
- Defined in:
- lib/solid_objects/wake_up_adapters/postgresql.rb,
sig/generated/lib/solid_objects/wake_up_adapters/postgresql.rbs
Overview
Wakes runtime roles across processes using PostgreSQL notifications.
The in-process wake-up cannot reach another process, so a commit in a web process leaves a worker waiting out its polling interval. This adapter keeps that polling interval as the upper bound and delivers a notification when one is available, so a missed or failed notification costs latency rather than correctness.
Constant Summary collapse
- CHANNEL =
"solid_objects_wake_up"- FAILED_WAIT_INTERVAL =
0.05
Instance Attribute Summary collapse
- #channel ⇒ Object readonly
-
#connections ⇒ Object
readonly
Returns the value of attribute connections.
-
#mutex ⇒ Object
readonly
Returns the value of attribute mutex.
Instance Method Summary collapse
- #disconnect(connection) ⇒ void
-
#initialize(channel: CHANNEL) ⇒ Postgresql
constructor
A new instance of Postgresql.
- #instrument_failure(operation, error) ⇒ void
-
#listen ⇒ Boolean
Starts listening before a caller blocks, so a notification sent between startup and the first wait is not missed.
-
#listening_connection ⇒ Object
A listening connection is dedicated and per thread.
- #notify_channel ⇒ void
- #open_listening_connection ⇒ Object
- #pace_after_failure(timeout) ⇒ void
- #signal ⇒ Boolean
- #stop ⇒ Boolean
- #thread_key ⇒ Symbol
- #wait(timeout:) ⇒ Boolean
- #watch ⇒ self
Constructor Details
#initialize(channel: CHANNEL) ⇒ Postgresql
Returns a new instance of Postgresql.
23 24 25 26 27 |
# File 'lib/solid_objects/wake_up_adapters/postgresql.rb', line 23 def initialize(channel: CHANNEL) @channel = channel @mutex = Thread::Mutex.new @connections = [] end |
Instance Attribute Details
#channel ⇒ Object (readonly)
20 21 22 |
# File 'lib/solid_objects/wake_up_adapters/postgresql.rb', line 20 def channel @channel end |
#connections ⇒ Object (readonly)
Returns the value of attribute connections.
79 80 81 |
# File 'lib/solid_objects/wake_up_adapters/postgresql.rb', line 79 def connections @connections end |
#mutex ⇒ Object (readonly)
Returns the value of attribute mutex.
79 80 81 |
# File 'lib/solid_objects/wake_up_adapters/postgresql.rb', line 79 def mutex @mutex end |
Instance Method Details
#disconnect(connection) ⇒ void
This method returns an undefined value.
116 117 118 119 120 |
# File 'lib/solid_objects/wake_up_adapters/postgresql.rb', line 116 def disconnect(connection) connection.disconnect! rescue nil end |
#instrument_failure(operation, error) ⇒ void
This method returns an undefined value.
131 132 133 134 135 136 137 138 139 |
# File 'lib/solid_objects/wake_up_adapters/postgresql.rb', line 131 def instrument_failure(operation, error) SolidObjects.instrument( :"wake_up.failed", adapter: "postgresql", operation: operation.to_s, error_class: error.class.name, error_message: error. ) end |
#listen ⇒ Boolean
Starts listening before a caller blocks, so a notification sent between startup and the first wait is not missed.
57 58 59 60 61 62 63 |
# File 'lib/solid_objects/wake_up_adapters/postgresql.rb', line 57 def listen listening_connection true rescue => error instrument_failure(:listen, error) false end |
#listening_connection ⇒ Object
A listening connection is dedicated and per thread. LISTEN is per
connection, a blocking wait must not hold a connection the rest of the
runtime needs, and one connection cannot serve concurrent waiters: the
supervisor shares one adapter across roles, and a notification consumed
by one waiter would leave the others asleep until their poll expired.
94 95 96 97 98 99 |
# File 'lib/solid_objects/wake_up_adapters/postgresql.rb', line 94 def listening_connection connection = Thread.current[thread_key] return connection if connection&.active? open_listening_connection end |
#notify_channel ⇒ void
This method returns an undefined value.
82 83 84 85 86 |
# File 'lib/solid_objects/wake_up_adapters/postgresql.rb', line 82 def notify_channel Record.connection_pool.with_connection do |connection| connection.execute("NOTIFY #{connection.quote_table_name(channel)}") end end |
#open_listening_connection ⇒ Object
102 103 104 105 106 107 108 |
# File 'lib/solid_objects/wake_up_adapters/postgresql.rb', line 102 def open_listening_connection connection = Record.connection_pool.send(:new_connection) connection.execute("LISTEN #{connection.quote_table_name(channel)}") Thread.current[thread_key] = connection mutex.synchronize { connections << connection } connection end |
#pace_after_failure(timeout) ⇒ void
This method returns an undefined value.
123 124 125 126 127 128 |
# File 'lib/solid_objects/wake_up_adapters/postgresql.rb', line 123 def pace_after_failure(timeout) interval = [ timeout.to_f, FAILED_WAIT_INTERVAL ].min return unless interval.positive? sleep interval end |
#signal ⇒ Boolean
30 31 32 33 34 35 36 |
# File 'lib/solid_objects/wake_up_adapters/postgresql.rb', line 30 def signal notify_channel true rescue => error instrument_failure(:signal, error) false end |
#stop ⇒ Boolean
66 67 68 69 70 71 72 73 74 75 |
# File 'lib/solid_objects/wake_up_adapters/postgresql.rb', line 66 def stop open = mutex.synchronize do listening = connections.dup connections.clear listening end Thread.current[thread_key] = nil open.each { |connection| disconnect(connection) } open.any? end |
#thread_key ⇒ Symbol
111 112 113 |
# File 'lib/solid_objects/wake_up_adapters/postgresql.rb', line 111 def thread_key :"solid_objects_wake_up_#{object_id}" end |
#wait(timeout:) ⇒ Boolean
39 40 41 42 43 44 45 46 |
# File 'lib/solid_objects/wake_up_adapters/postgresql.rb', line 39 def wait(timeout:) connection = listening_connection !connection.raw_connection.wait_for_notify(timeout.to_f).nil? rescue => error instrument_failure(:wait, error) pace_after_failure(timeout) false end |
#watch ⇒ self
49 50 51 52 |
# File 'lib/solid_objects/wake_up_adapters/postgresql.rb', line 49 def watch listen self end |