如何强制 ActiveRecord 以只读模式打开数据库?
How to force ActiveRecord to open database in read-only mode?
是否有 ActiveRecord::Base.configurations 或 establish_connection() 的参数强制确保无法写入数据库? (如果它有所不同,它是一个 Heroku Postgres 数据库)
辅助 Sinatra 应用程序(使用 ActiveRecord 5.2)需要严格 只读访问主要应用程序使用的 Heroku Postgres 数据库...例如,即使代码错误不小心尝试写入更改,我们需要它失败。
几个SO线程中的建议是定义一个readonly?方法如下图
它几乎可以工作...但有一个重要的例外...
虽然它确实会阻止保存或 update_attributes,但不会阻止 update_column 写入。
APP_DB_HASH = {
"appdb"=>
{ "adapter"=>"postgresql",
"encoding" => "unicode",
"pool" => 5,
"url"=> ENV["APP_DATABASE_URL"] },
ActiveRecord::Base.configurations["appdb"] = {
:adapter => APP_DB_HASH["appdb"]["adapter"],
:encoding => APP_DB_HASH["appdb"]["encoding"],
:database => uri.path.gsub('/',''),
:username => uri.user,
:password => uri.password,
:port => uri.port,
:host => uri.host
}
class AppBase < ActiveRecord::Base
self.abstract_class = true
establish_connection configurations["appdb"]
# THIS DOES NOT PREVENT update_column FROM WRITING TO DATABASE!
def readonly?
true
end
end
class MyModel << AppBase
...
end
结果:
> rec = MyModel.first.foo
# false
> rec.update_attributes foo: true
# GOOD: exception thrown, prevents write
> rec.foo = true
> rec.save
# GOOD: exception thrown, prevents write
> rec.update_column :foo, true
# FAIL: THE 'READONLY" DATABASE GETS WRITTEN
不幸的是,我只知道 Postgresql 的答案,但这似乎是您正在使用的。
简单的答案是(可能在初始化程序中):
ActiveRecord::Base.connection.execute("SET SESSION CHARACTERISTICS AS TRANSACTION READ ONLY")
我的使用方式是:
def with_read_only_connection(configuration)
original_connection = ActiveRecord::Base.remove_connection
ActiveRecord::Base.establish_connection(configuration)
ActiveRecord::Base.connection.execute("SET SESSION CHARACTERISTICS AS TRANSACTION READ ONLY")
yield
ensure
ActiveRecord::Base.establish_connection(original_connection)
end
这是一个使用示例:
[5] pry(main)> with_read_only_connection(:development) do
[5] pry(main)* User.count
[5] pry(main)* end
(0.2ms) SET SESSION CHARACTERISTICS AS TRANSACTION READ ONLY
(96.6ms) SELECT COUNT(*) FROM "users"
=> 24566
[6] pry(main)> with_read_only_connection(:development) do
[6] pry(main)* User.first.update_attribute(:first_name, "Bob")
[6] pry(main)* end
(0.2ms) SET SESSION CHARACTERISTICS AS TRANSACTION READ ONLY
User Load (1.9ms) SELECT "users".* FROM "users" ORDER BY "users"."id" ASC LIMIT 1
(0.2ms) BEGIN
SQL (0.7ms) UPDATE "users" SET "first_name" = , "updated_at" = WHERE "users"."id" = [["first_name", "Bob"], ["updated_at", "2019-04-06 13:14:12.270619"], ["id", 1]]
(0.2ms) ROLLBACK
ActiveRecord::StatementInvalid: PG::ReadOnlySqlTransaction: ERROR: cannot execute UPDATE in a read-only transaction
: UPDATE "users" SET "first_name" = , "updated_at" = WHERE "users"."id" =
from .../.bundle/ruby/2.2.0/gems/activerecord-4.2.11.1/lib/active_record/connection_adapters/postgresql_adapter.rb:602:in `exec_prepared'
注意 - 它只会在连接时调用 SET SESSION CHARACTERISTICS..
。
当我尝试在只读环境中执行 (ActiveRecord) 操作时,我偶然发现了这个问题。一段时间后,我意识到与其将整个连接设置为只读及其会话特征(如上面方法 with_read_only_connection
中所建议的),我可以简单地将所有内容包装在一个事务中并在执行后回滚:
ActiveRecord::Base.transaction do
# do your thing!
raise ActiveRecord::Rollback
end
希望对您有所帮助。非常感谢@jjthrash,感谢 SET SESSION CHARACTERISTICS
的 tipp,我在制作过程中一直使用它。
是否有 ActiveRecord::Base.configurations 或 establish_connection() 的参数强制确保无法写入数据库? (如果它有所不同,它是一个 Heroku Postgres 数据库)
辅助 Sinatra 应用程序(使用 ActiveRecord 5.2)需要严格 只读访问主要应用程序使用的 Heroku Postgres 数据库...例如,即使代码错误不小心尝试写入更改,我们需要它失败。
几个SO线程中的建议是定义一个readonly?方法如下图
它几乎可以工作...但有一个重要的例外...
虽然它确实会阻止保存或 update_attributes,但不会阻止 update_column 写入。
APP_DB_HASH = {
"appdb"=>
{ "adapter"=>"postgresql",
"encoding" => "unicode",
"pool" => 5,
"url"=> ENV["APP_DATABASE_URL"] },
ActiveRecord::Base.configurations["appdb"] = {
:adapter => APP_DB_HASH["appdb"]["adapter"],
:encoding => APP_DB_HASH["appdb"]["encoding"],
:database => uri.path.gsub('/',''),
:username => uri.user,
:password => uri.password,
:port => uri.port,
:host => uri.host
}
class AppBase < ActiveRecord::Base
self.abstract_class = true
establish_connection configurations["appdb"]
# THIS DOES NOT PREVENT update_column FROM WRITING TO DATABASE!
def readonly?
true
end
end
class MyModel << AppBase
...
end
结果:
> rec = MyModel.first.foo
# false
> rec.update_attributes foo: true
# GOOD: exception thrown, prevents write
> rec.foo = true
> rec.save
# GOOD: exception thrown, prevents write
> rec.update_column :foo, true
# FAIL: THE 'READONLY" DATABASE GETS WRITTEN
不幸的是,我只知道 Postgresql 的答案,但这似乎是您正在使用的。
简单的答案是(可能在初始化程序中):
ActiveRecord::Base.connection.execute("SET SESSION CHARACTERISTICS AS TRANSACTION READ ONLY")
我的使用方式是:
def with_read_only_connection(configuration)
original_connection = ActiveRecord::Base.remove_connection
ActiveRecord::Base.establish_connection(configuration)
ActiveRecord::Base.connection.execute("SET SESSION CHARACTERISTICS AS TRANSACTION READ ONLY")
yield
ensure
ActiveRecord::Base.establish_connection(original_connection)
end
这是一个使用示例:
[5] pry(main)> with_read_only_connection(:development) do
[5] pry(main)* User.count
[5] pry(main)* end
(0.2ms) SET SESSION CHARACTERISTICS AS TRANSACTION READ ONLY
(96.6ms) SELECT COUNT(*) FROM "users"
=> 24566
[6] pry(main)> with_read_only_connection(:development) do
[6] pry(main)* User.first.update_attribute(:first_name, "Bob")
[6] pry(main)* end
(0.2ms) SET SESSION CHARACTERISTICS AS TRANSACTION READ ONLY
User Load (1.9ms) SELECT "users".* FROM "users" ORDER BY "users"."id" ASC LIMIT 1
(0.2ms) BEGIN
SQL (0.7ms) UPDATE "users" SET "first_name" = , "updated_at" = WHERE "users"."id" = [["first_name", "Bob"], ["updated_at", "2019-04-06 13:14:12.270619"], ["id", 1]]
(0.2ms) ROLLBACK
ActiveRecord::StatementInvalid: PG::ReadOnlySqlTransaction: ERROR: cannot execute UPDATE in a read-only transaction
: UPDATE "users" SET "first_name" = , "updated_at" = WHERE "users"."id" =
from .../.bundle/ruby/2.2.0/gems/activerecord-4.2.11.1/lib/active_record/connection_adapters/postgresql_adapter.rb:602:in `exec_prepared'
注意 - 它只会在连接时调用 SET SESSION CHARACTERISTICS..
。
当我尝试在只读环境中执行 (ActiveRecord) 操作时,我偶然发现了这个问题。一段时间后,我意识到与其将整个连接设置为只读及其会话特征(如上面方法 with_read_only_connection
中所建议的),我可以简单地将所有内容包装在一个事务中并在执行后回滚:
ActiveRecord::Base.transaction do
# do your thing!
raise ActiveRecord::Rollback
end
希望对您有所帮助。非常感谢@jjthrash,感谢 SET SESSION CHARACTERISTICS
的 tipp,我在制作过程中一直使用它。