代码之家  ›  专栏  ›  技术社区  ›  jpw

如何强制ActiveRecord以只读模式打开数据库?

  •  0
  • jpw  · 技术社区  · 6 年前

    ActiveRecord是否有参数:基本配置或者建立强制确保不可能写入数据库的_connection()?(如果有什么不同,那就是Heroku Postgres数据库)

    辅助的Sinatra应用程序(使用ActiveRecord 5.2)需要 严格地 对主应用程序使用的Heroku Postgres数据库的只读访问。。。例如,即使一个代码错误意外地试图编写更改,我们也需要它失败。

    几个SO线程中的建议是定义一个readonly?方法如下所示。

    它几乎可以工作。。。除了一个重要的例外。。。

    尽管它确实阻止保存或更新属性,但它不阻止更新列写入。

    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
    
    0 回复  |  直到 6 年前
        1
  •  2
  •   jjthrash    6 年前

    不幸的是,我只知道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" = $1, "updated_at" = $2 WHERE "users"."id" = $3  [["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" = $1, "updated_at" = $2 WHERE "users"."id" = $3
    from .../.bundle/ruby/2.2.0/gems/activerecord-4.2.11.1/lib/active_record/connection_adapters/postgresql_adapter.rb:602:in `exec_prepared'
    

    NB-它只会调用 SET SESSION CHARACTERISTICS.. 当它连接起来。