Как заставить ActiveRecord открыть базу данных в режиме только для чтения? - PullRequest
0 голосов
/ 28 января 2019

Есть ли параметр для ActiveRecord :: Base.configurations илиуждение_коннекции (), который принудительно гарантирует, что НИКАКАЯ запись в базу данных невозможна?(Если это имеет значение, то это база данных Heroku Postgres)

Вспомогательному приложению Sinatra (использующему ActiveRecord 5.2) требуется строго доступ только для чтения к базе данных Heroku Postgres, используемой основным приложением... например, даже если ошибка кода случайно пытается записать изменение, нам нужно, чтобы оно не сработало.

Совет в нескольких потоках SO - определять чтение только для чтения?метод, как показано ниже.

Это ПОЧТИ работает ... с одним важным исключением ...

Хотя это предотвращает сохранение или атрибуты 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

1 Ответ

0 голосов
/ 06 апреля 2019

К сожалению, я знаю только ответ для 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.. будет вызываться только при подключении.

...