Patrzysz w odpowiednim miejscu z metodą wait_for_notify
, ale ponieważ ActiveRecord najwyraźniej nie dostarcza API dla go używać, trzeba uzyskać co bazowego PG :: obiektu Connection (lub jeden z nich , jeśli używasz konfiguracji wielowątkowej), której ActiveRecord używa do komunikowania się z Postgresem.
Po uzyskaniu połączenia po prostu wykonaj wszystkie potrzebne instrukcje, a następnie przekaż blok (i opcjonalny okres czasu) na wait_for_notify
. Zauważ, że spowoduje to zablokowanie bieżącego wątku i zmonopolizowanie połączenia PostgreS, aż do osiągnięcia limitu czasu lub pojawienia się NOTIFY
(więc nie będziesz chciał tego robić na przykład w żądaniu internetowym). Kiedy inny proces wyda numer NOTIFY
na jednym z kanałów, którego słuchasz, blok zostanie wywołany z trzema argumentami - kanałem, który powiadomił, pid z PostgreS backend, który uruchomił NOTIFY
, i ładunek, który towarzyszył NOTIFY
(Jeśli w ogóle).
Nie używałem ActiveRecord na dłuższą chwilę, więc nie może być czystszy sposób, aby to zrobić, ale to wydaje się działać dobrze w 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
Na przykład bardziej ogólne użycie, patrz Sequel's implementation.
Edytuj, aby dodać: Oto kolejny opis tego, co się dzieje. Może to nie być dokładna implementacja za kulisami, ale wydaje się, że wystarczająco dobrze opisuje to zachowanie.
Postgres przechowuje listę powiadomień dla każdego połączenia. Kiedy używasz połączenia do wykonania LISTEN channel_name
, mówisz Postgresowi, że wszelkie powiadomienia na tym kanale powinny zostać przekazane na listę tego połączenia (wiele połączeń może nasłuchiwać na tym samym kanale, dzięki czemu pojedyncze powiadomienie może zostać przekazane na wiele list) . Połączenie może LISTEN
do wielu kanałów w tym samym czasie, a powiadomienia do któregokolwiek z nich zostaną przekazane na tę samą listę.
Co to jest jest wysyłane najstarsze powiadomienie z listy połączeń i przekazuje informacje do bloku - lub, jeśli lista jest pusta, śpi, aż powiadomienie stanie się dostępne i robi to samo dla tego (lub do upływu czasu zostanie osiągnięty, w takim przypadku po prostu zwraca zero). Ponieważ wait_for_notify
obsługuje tylko jedno powiadomienie, będziesz musiał wielokrotnie go wywoływać, jeśli chcesz obsłużyć wiele powiadomień.
Kiedy UNLISTEN channel_name
lub UNLISTEN *
Postgres zatrzyma popychanie tych powiadomień do listy połączeniem, ale te, które już zostały zepchnięte na tej liście pozostanie tam, i będzie wait_for_notify jeszcze zwrócić je, kiedy jest następny nazywa. Może to spowodować problem z powiadomieniami, które są gromadzone po wait_for_notify
, ale przed UNLISTEN
pozostają w pobliżu i są nadal obecne, gdy inny wątek sprawdzi to połączenie.W takim przypadku po UNLISTEN
możesz wywołać wait_for_notify
z krótkimi limitami czasu, aż zwróci zero. Ale chyba że intensywnie korzystasz z LISTEN
i NOTIFY
dla wielu różnych celów, jednak prawdopodobnie nie warto się tym martwić.
Dodałem lepszy link do implementacji Sequel powyżej, polecam, patrząc na to. To całkiem proste.
Jeśli dobrze rozumiem, chcesz db do przekazywania informacji do szyn. Jeśli szyny mają jakiegoś rodzaju słuchacza, można to zrobić za pomocą plperl, który następnie łączy się z takim słuchaczem i dostarcza informacji. –
Próbuję znaleźć przykładowy kod, jak skonfigurować szyny do działania jako ten słuchacz. Nie sądzę, że potrzebujemy plperl, ze względu na LISTEN/NOTIFY zdolności PostgreSzu, ale jestem grą, aby wypróbować wszystko, co możesz wymyślić –
Miałem na myśli hack; lepiej spójrz na http://sequel.rubyforge.org/rdoc/files/doc/postgresql_rdoc.html lub https://github.com/taotetek/listen_notify_poller/blob/master/example.rb –