Stai cercando nel posto giusto con il metodo wait_for_notify
, ma dal momento che ActiveRecord a quanto pare non fornisce un'API per utilizzarlo, devi raggiungere l'oggetto PG :: Connection sottostante (o uno di essi , se stai eseguendo una configurazione con multithreading che ActiveRecord sta usando per parlare con Postgres.
Una volta ottenuta la connessione, è sufficiente eseguire qualsiasi istruzione LISTEN
necessaria, quindi passare un blocco (e un periodo di timeout opzionale) a wait_for_notify
. Nota che questo bloccherà il thread corrente e monopolizzerà la connessione di Postgres, fino a quando non verrà raggiunto il timeout o si verificherà un NOTIFY
(quindi non vorresti farlo all'interno di una richiesta web, per esempio). Quando un altro processo invia un NOTIFY
su uno dei canali che stai ascoltando, il blocco verrà chiamato con tre argomenti: il canale che ha ricevuto la notifica, il pid del backend Postgres che ha attivato lo NOTIFY
e il payload che ha accompagnato lo NOTIFY
. (se presente).
Non ho usato ActiveRecord in un bel po ', quindi ci può essere un modo più pulito per fare questo, ma questo sembra funzionare bene in 4.0.0.beta1:
# Be sure to check out a connection, so we stay thread-safe.
ActiveRecord::Base.connection_pool.with_connection do |connection|
# connection is the ActiveRecord::ConnectionAdapters::PostgreSQLAdapter object
conn = connection.instance_variable_get(:@connection)
# conn is the underlying PG::Connection object, and exposes #wait_for_notify
begin
conn.async_exec "LISTEN channel1"
conn.async_exec "LISTEN channel2"
# This will block until a NOTIFY is issued on one of these two channels.
conn.wait_for_notify do |channel, pid, payload|
puts "Received a NOTIFY on channel #{channel}"
puts "from PG backend #{pid}"
puts "saying #{payload}"
end
# Note that you'll need to call wait_for_notify again if you want to pick
# up further notifications. This time, bail out if we don't get a
# notification within half a second.
conn.wait_for_notify(0.5) do |channel, pid, payload|
puts "Received a second NOTIFY on channel #{channel}"
puts "from PG backend #{pid}"
puts "saying #{payload}"
end
ensure
# Don't want the connection to still be listening once we return
# it to the pool - could result in weird behavior for the next
# thread to check it out.
conn.async_exec "UNLISTEN *"
end
end
Per un esempio di un uso più generale, vedere Sequel's implementation.
Modifica per aggiungere: Ecco un'altra descrizione di cosa sta succedendo. Questa potrebbe non essere l'esatta implementazione dietro le quinte, ma sembra descrivere abbastanza bene il comportamento.
Postgres mantiene un elenco di notifiche per ciascuna connessione. Quando usi una connessione per eseguire LISTEN channel_name
, stai dicendo a Postgres che qualsiasi notifica su quel canale dovrebbe essere spinta alla lista di questa connessione (più connessioni possono ascoltare lo stesso canale, quindi una singola notifica può finire per essere spinta in molti elenchi) . Una connessione può LISTEN
in molti canali allo stesso tempo e tutte le notifiche a qualsiasi di esse verranno inviate allo stesso elenco.
Quello che wait_for_notify
compila è la notifica meno recente dell'elenco della connessione e passa le sue informazioni al blocco - oppure, se l'elenco è vuoto, rimane in attesa fino a quando non viene resa disponibile una notifica e fa lo stesso (o fino al timeout è raggiunto, nel qual caso restituisce solo zero). Poiché wait_for_notify
gestisce solo una singola notifica, dovrai chiamarla ripetutamente se desideri gestire più notifiche.
Quando si UNLISTEN channel_name
o UNLISTEN *
, Postgres si smettere di spingere le notifiche alla lista della tua connessione, ma quelli che sono già stati spinti a questo elenco rimarrà lì, e wait_for_notify sarà ancora restituirli quando è prossima chiamata. Ciò potrebbe causare un problema in cui le notifiche accumulate dopo lo wait_for_notify
ma prima dello UNLISTEN
restano attive e sono ancora presenti quando un altro thread controlla tale connessione.In tal caso, dopo UNLISTEN
, è possibile chiamare wait_for_notify
con brevi timeout fino a quando non restituisce nulla. Ma a meno che tu non stia facendo un uso intenso di LISTEN
e NOTIFY
per molti scopi diversi, tuttavia, probabilmente non vale la pena preoccuparsi.
Ho aggiunto un collegamento migliore all'implementazione di Sequel sopra, consiglierei di guardarlo. È piuttosto semplice.
Se ho capito bene, volete db per inviare informazioni alle guide. Se rails ha una sorta di listener, potrebbe essere fatto con plperl che quindi si connette a tale listener e fornisce informazioni. –
Sto cercando di trovare un codice di esempio su come impostare i binari per agire come ascoltatore. Non penso che abbiamo bisogno di plperl, a causa delle abilità LISTEN/NOTIFY di Postgres, ma sono partita per provare qualsiasi cosa tu possa pensare a –
Ho avuto un hack in mente; meglio guardare http://sequel.rubyforge.org/rdoc/files/doc/postgresql_rdoc.html o https://github.com/taotetek/listen_notify_poller/blob/master/example.rb –