I used the following article as a reference.
↓
About prevention processing of DB destruction type task that entered Rails 5
small story.
From Rails 5, there is a mechanism to prevent the execution of DB destruction tasks in the production environment.
For example, when I try to run db:drop in my production environment, I get the following error (ActiveRecord::ProtectedEnvironmentError).
$ RAILS_ENV=production ./bin/rails db:drop
rails aborted!
ActiveRecord::ProtectedEnvironmentError: You are attempting to run a destructive action against your ‘production’ database
If you are sure you want to continue, run the same command with the environment variable
DISABLE_DATABASE_ENVIRONMENT_CHECK=1
As you can see, this is a mechanism to prevent accidentally destroying the DB in the production environment (By the way, according to the PR comment, I actually accidentally destroyed the DB on Heroku and would like to ask for recovery. It seems that the request was allotted).
If you want to intentionally destroy the DB in the production environment, just specify the environment variable DISABLE_DATABASE_ENVIRONMENT_CHECK=1 as shown in the error message.
The tasks targeted for prevention processing are as follows.
db: drop
db:drop:all
db:purge
db:purge:all
db:purge:test
db:schema:load
By default only the production environment is blocked, but this can be changed. If you want to specify an environment other than production, you can specify the environment to be protected in ActiveRecord::Base.protected_environments.
#config/application.rb
ActiveRecord::Base.protected_environments = %w(production staging)
$ RAILS_ENV=staging ./bin/rails db:drop
rails aborted!
ActiveRecord::ProtectedEnvironmentError: You are attempting to run a destructive action against your ‘staging’ database
If you are sure you want to continue, run the same command with the environment variable
DISABLE_DATABASE_ENVIRONMENT_CHECK=1
You can specify an Array for ActiveRecord::Base.protected_environments, so of course it is possible to target multiple environments to be protected (the value will be overwritten, so if you want to specify the environment in addition to production, use production must be specified explicitly).
By the way, the check of environments is done by comparing with the environment where migration processing etc. were performed at the end.
What this means is that from Rails 5, when db:migrate or schema load processing is performed, the information of the environment that performed the processing is stored in the DB.
Specifically, a table named ar_internal_metadata (in the default case) is created inside Rails and environment information is stored there.
irb(main):001:0* ActiveRecord::InternalMetadata.all
ActiveRecord::InternalMetadata Load (0.2ms) SELECT “ar_internal_metadata”.* FROM “ar_internal_metadata”
=> #<:relation key: value: created_at: updated_at:>]>
</:relation>
- If table_name_prefix / table_name_suffix is set, the value will be attached before and after the table name. Also, the table name can be changed with ActiveRecord::Base.internal_metadata_table_name. Furthermore, until Rails 5.0.0.beta2, the default table name was active_record_internal_metadatas.
The value stored in the ar_internal_metadata table is compared with the environment in which the process is to be executed, so of course the process cannot be performed without the ar_internal_metadata table.
Therefore, if you try to execute a destructive task such as db:drop in a state without ar_internal_metadata (when updating from Rails 4 to Rails 5 and db:migrate has never been executed), Environment information An error (ActiveRecord::NoEnvironmentInSchemaError) will occur.
$ ./bin/rails db:drop
rails aborted!
ActiveRecord::NoEnvironmentInSchemaError:
Environment data not found in the schema. To resolve this issue, run:
bin/rails db:environment:set RAILS_ENV=development
This happens regardless of whether the environment is protected or not.
To resolve it, run db:environment:set task as shown in the error message (running db:migrate is also OK).
By the way, if for some reason the environment in the ar_internal_metadata table contains a different value than the environment you are trying to process, this will also result in an error (ActiveRecord::EnvironmentMismatchError).
For example, let’s say that the environment in the ar_internal_metadata table in the development environment contains the value test.
irb(main):001:0* Rails.env
=> “development”
irb(main):002:0> ActiveRecord::InternalMetadata.all
ActiveRecord::InternalMetadata Load (0.2ms) SELECT “ar_internal_metadata”.* FROM “ar_internal_metadata”
=> #<:relation key: value: created_at: updated_at:>]>
irb(main):003:0>
</:relation>
Executing a DB destruction task in this state will result in the following.
$ ./bin/rails db:drop
rails aborted!
ActiveRecord::EnvironmentMismatchError: You are attempting to modify a database that was last run in test
environment.
You are running in development
environment. If you are sure you want to continue, first set the environment using:
bin/rails db:environment:set RAILS_ENV=development
In this case as well, it is OK if you execute db:environment:set task as shown in the error message.
If you use the same DB in different environments, I feel like it’s going to cause a lot of trouble.
Miscellaneous impressions
I think it’s a very good thing that the DB destruction task prevention process that was included in Rails 5 is now provided by default. This is a complete digression, but I made a gem to do something similar, but it is no longer needed. No, it’s a good thing.
And I feel that ActiveRecord::EnvironmentMismatchError will occur from time to time…