Rails & postgresql, notify/listen 到创建新记录时
Rails & postgresql, notify/listen to when a new record is created
我正在试验和学习如何使用 PostgreSQL,即它的 Notify/Listen 功能,在根据此 tutorial 制作服务器发送事件的上下文中。
每当保存 user
并更改属性 authy_status
时,教程都会将 NOTIFY
发布到 user
频道(通过其 id
) . LISTEN
方法然后产生新的 authy_status
代码:
class Order < ActiveRecord::Base
after_commit :notify_creation
def notify_creation
if created?
ActiveRecord::Base.connection_pool.with_connection do |connection|
execute_query(connection, ["NOTIFY user_?, ?", id, authy_status])
end
end
end
def on_creation
ActiveRecord::Base.connection_pool.with_connection do |connection|
begin
execute_query(connection, ["LISTEN user_?", id])
connection.raw_connection.wait_for_notify do |event, pid, status|
yield status
end
ensure
execute_query(connection, ["UNLISTEN user_?", id])
end
end
end
end
我想做一些不同的事情,但一直无法找到有关如何执行此操作的信息。我想 NOTIFY
首先创建用户(即插入数据库),然后在 LISTEN
中,我想 yield
建立新的创建用户本身(或者更确切地说是它的 id
)。
我将如何修改代码来实现这一点?我对编写 SQL 很陌生,所以例如,我不太确定如何将 ["NOTIFY user_?, ?", id, authy_status]
更改为不针对特定用户的语句,而是针对整个 USER
table,监听新记录(类似... ["NOTIFY USER on INSERT", id]
?? )
澄清
抱歉没说清楚。 after_save
是一个复制错误,已更正为上面的 after_commit
。但这不是问题。问题是侦听器侦听特定现有用户的更改,通知程序通知特定用户的更改。
我反而想监听任何新用户的创建,因此通知它。 Notify 和 Listen 代码需要如何更改才能满足此要求?
我想,与我对代码的猜测不同,通知代码可能不需要更改,因为在创建 id 时通知它似乎仍然有意义(但同样,我不知道,随意纠正我)。但是,你怎么听整个 table,而不是特定的记录,因为我又没有现有的记录可以听?
对于更广泛的上下文,这是原始教程中控制器中 SSE 中监听器的使用方式:
def one_touch_status_live
response.headers['Content-Type'] = 'text/event-stream'
@user = User.find(session[:pre_2fa_auth_user_id])
sse = SSE.new(response.stream, event: "authy_status")
begin
@user.on_creation do |status|
if status == "approved"
session[:user_id] = @user.id
session[:pre_2fa_auth_user_id] = nil
end
sse.write({status: status})
end
rescue ClientDisconnected
ensure
sse.close
end
end
但同样,在我的情况下,这不起作用,我没有特定的 @user
我正在听,我希望 SSE 在创建任何用户时触发.. . 也许是这个控制器代码也需要修改?但这是我非常不清楚的地方。如果我有类似...
User.on_creation do |u|
一个class方法是有道理的,但是我又如何获得监听代码来监听整个table?
请使用 after_commit
而不是 after_save
。这样,用户记录肯定会提交到数据库中
There are two additional callbacks that are triggered by the completion of a database transaction: after_commit and after_rollback. These callbacks are very similar to the after_save callback except that they don't execute until after database changes have either been committed or rolled back.
https://guides.rubyonrails.org/active_record_callbacks.html#transaction-callbacks
其实与你的问题无关,你可以使用任何一个。
以下是我处理您的用例的方式:您希望在创建用户时收到通知:
#app/models/user.rb
class User < ActiveRecord::Base
after_commit :notify_creation
def notify_creation
if id_previously_changed?
ActiveRecord::Base.connection_pool.with_connection do |connection|
self.class.execute_query(connection, ["NOTIFY user_created, '?'", id])
end
end
end
def self.on_creation
ActiveRecord::Base.connection_pool.with_connection do |connection|
begin
execute_query(connection, ["LISTEN user_created"])
connection.raw_connection.wait_for_notify do |event, pid, id|
yield self.find id
end
ensure
execute_query(connection, ["UNLISTEN user_created"])
end
end
end
def self.clean_sql(query)
sanitize_sql(query)
end
def self.execute_query(connection, query)
sql = self.clean_sql(query)
connection.execute(sql)
end
end
所以如果你使用
User.on_creation do |user|
#do something with the user
#check user.authy_status or whatever attribute you want.
end
有一件事我不确定你为什么要这样做,因为它可能会出现竞争条件情况,即创建了 2 个用户而不需要的用户先完成。
我正在试验和学习如何使用 PostgreSQL,即它的 Notify/Listen 功能,在根据此 tutorial 制作服务器发送事件的上下文中。
每当保存 user
并更改属性 authy_status
时,教程都会将 NOTIFY
发布到 user
频道(通过其 id
) . LISTEN
方法然后产生新的 authy_status
代码:
class Order < ActiveRecord::Base
after_commit :notify_creation
def notify_creation
if created?
ActiveRecord::Base.connection_pool.with_connection do |connection|
execute_query(connection, ["NOTIFY user_?, ?", id, authy_status])
end
end
end
def on_creation
ActiveRecord::Base.connection_pool.with_connection do |connection|
begin
execute_query(connection, ["LISTEN user_?", id])
connection.raw_connection.wait_for_notify do |event, pid, status|
yield status
end
ensure
execute_query(connection, ["UNLISTEN user_?", id])
end
end
end
end
我想做一些不同的事情,但一直无法找到有关如何执行此操作的信息。我想 NOTIFY
首先创建用户(即插入数据库),然后在 LISTEN
中,我想 yield
建立新的创建用户本身(或者更确切地说是它的 id
)。
我将如何修改代码来实现这一点?我对编写 SQL 很陌生,所以例如,我不太确定如何将 ["NOTIFY user_?, ?", id, authy_status]
更改为不针对特定用户的语句,而是针对整个 USER
table,监听新记录(类似... ["NOTIFY USER on INSERT", id]
?? )
澄清
抱歉没说清楚。 after_save
是一个复制错误,已更正为上面的 after_commit
。但这不是问题。问题是侦听器侦听特定现有用户的更改,通知程序通知特定用户的更改。
我反而想监听任何新用户的创建,因此通知它。 Notify 和 Listen 代码需要如何更改才能满足此要求?
我想,与我对代码的猜测不同,通知代码可能不需要更改,因为在创建 id 时通知它似乎仍然有意义(但同样,我不知道,随意纠正我)。但是,你怎么听整个 table,而不是特定的记录,因为我又没有现有的记录可以听?
对于更广泛的上下文,这是原始教程中控制器中 SSE 中监听器的使用方式:
def one_touch_status_live
response.headers['Content-Type'] = 'text/event-stream'
@user = User.find(session[:pre_2fa_auth_user_id])
sse = SSE.new(response.stream, event: "authy_status")
begin
@user.on_creation do |status|
if status == "approved"
session[:user_id] = @user.id
session[:pre_2fa_auth_user_id] = nil
end
sse.write({status: status})
end
rescue ClientDisconnected
ensure
sse.close
end
end
但同样,在我的情况下,这不起作用,我没有特定的 @user
我正在听,我希望 SSE 在创建任何用户时触发.. . 也许是这个控制器代码也需要修改?但这是我非常不清楚的地方。如果我有类似...
User.on_creation do |u|
一个class方法是有道理的,但是我又如何获得监听代码来监听整个table?
请使用 after_commit
而不是 after_save
。这样,用户记录肯定会提交到数据库中
There are two additional callbacks that are triggered by the completion of a database transaction: after_commit and after_rollback. These callbacks are very similar to the after_save callback except that they don't execute until after database changes have either been committed or rolled back.
https://guides.rubyonrails.org/active_record_callbacks.html#transaction-callbacks
其实与你的问题无关,你可以使用任何一个。
以下是我处理您的用例的方式:您希望在创建用户时收到通知:
#app/models/user.rb
class User < ActiveRecord::Base
after_commit :notify_creation
def notify_creation
if id_previously_changed?
ActiveRecord::Base.connection_pool.with_connection do |connection|
self.class.execute_query(connection, ["NOTIFY user_created, '?'", id])
end
end
end
def self.on_creation
ActiveRecord::Base.connection_pool.with_connection do |connection|
begin
execute_query(connection, ["LISTEN user_created"])
connection.raw_connection.wait_for_notify do |event, pid, id|
yield self.find id
end
ensure
execute_query(connection, ["UNLISTEN user_created"])
end
end
end
def self.clean_sql(query)
sanitize_sql(query)
end
def self.execute_query(connection, query)
sql = self.clean_sql(query)
connection.execute(sql)
end
end
所以如果你使用
User.on_creation do |user|
#do something with the user
#check user.authy_status or whatever attribute you want.
end
有一件事我不确定你为什么要这样做,因为它可能会出现竞争条件情况,即创建了 2 个用户而不需要的用户先完成。