Rails 6.1升级时load_defaults与框架默认文件行为不一致问题
Rails 6.0升级到6.1测试用例出现数据库约束错误求助
将项目从Rails 6.0升级到6.1时遇到以下问题:
- 初始状态:
application.rb中配置config.load_defaults = 6.0,保留config/initializers/new_framework_defaults_6_1.rb文件,所有测试用例执行正常 - 修改后:移除
new_framework_defaults_6_1.rb,在application.rb中添加环境判断切换为config.load_defaults 6.1,大量测试失败,报错集中在ActiveRecord::RecordNotUnique(违反联合唯一约束)和ActiveRecord::NotNullViolation(违反非空约束)两类数据库错误,无法继续推进升级,求有类似经历的开发者提供解决思路。
项目配置文件内容
config/initializers/new_framework_defaults_6_0.rb
# Be sure to restart your server when you modify this file. # # This file contains migration options to ease your Rails 6.0 upgrade. # # Once upgraded flip defaults one by one to migrate to the new default. # # Read the Guide for Upgrading Ruby on Rails for more info on each option. # Don't force requests from old versions of IE to be UTF-8 encoded. Rails.application.config.action_view.default_enforce_utf8 = false # Embed purpose and expiry metadata inside signed and encrypted # cookies for increased security. # # This option is not backwards compatible with earlier Rails versions. # It's best enabled when your entire app is migrated and stable on 6.0. Rails.application.config.action_dispatch.use_cookies_with_metadata = true # Change the return value of `ActionDispatch::Response#content_type` to Content-Type header without modification. # Rails.application.config.action_dispatch.return_only_media_type_on_content_type = false # Return false instead of self when enqueuing is aborted from a callback. # Rails.application.config.active_job.return_false_on_aborted_enqueue = true # Send Active Storage analysis and purge jobs to dedicated queues. # Rails.application.config.active_storage.queues.analysis = :active_storage_analysis # Rails.application.config.active_storage.queues.purge = :active_storage_purge # When assigning to a collection of attachments declared via `has_many_attached`, replace existing # attachments instead of appending. Use #attach to add new attachments without replacing existing ones. Rails.application.config.active_storage.replace_on_assign_to_many = true # Use ActionMailer::MailDeliveryJob for sending parameterized and normal mail. # # The default delivery jobs (ActionMailer::Parameterized::DeliveryJob, ActionMailer::DeliveryJob), # will be removed in Rails 6.1. This setting is not backwards compatible with earlier Rails versions. # If you send mail in the background, job workers need to have a copy of # MailDeliveryJob to ensure all delivery jobs are processed properly. # Make sure your entire app is migrated and stable on 6.0 before using this setting. # Rails.application.config.action_mailer.delivery_job = 'ActionMailer::MailDeliveryJob' # Enable the same cache key to be reused when the object being cached of type # `ActiveRecord::Relation` changes by moving the volatile information (max updated at and count) # of the relation's cache key into the cache version to support recycling cache key. Rails.application.config.active_record.collection_cache_versioning = true
config/initializers/new_framework_defaults_6_1.rb
# Be sure to restart your server when you modify this file. # # This file contains migration options to ease your Rails 6.1 upgrade. # # Once upgraded flip defaults one by one to migrate to the new default. # # Read the Guide for Upgrading Ruby on Rails for more info on each option. # Support for inversing belongs_to -> has_many Active Record associations. Rails.application.config.active_record.has_many_inversing = true # Track Active Storage variants in the database. Rails.application.config.active_storage.track_variants = true # Apply random variation to the delay when retrying failed jobs. Rails.application.config.active_job.retry_jitter = 0.15 # Stop executing `after_enqueue`/`after_perform` callbacks if # `before_enqueue`/`before_perform` respectively halts with `throw :abort`. Rails.application.config.active_job.skip_after_callbacks_if_terminated = true # Specify cookies SameSite protection level: either :none, :lax, or :strict. # # This change is not backwards compatible with earlier Rails versions. # It's best enabled when your entire app is migrated and stable on 6.1. Rails.application.config.action_dispatch.cookies_same_site_protection = :lax # Generate CSRF tokens that are encoded in URL-safe Base64. # # This change is not backwards compatible with earlier Rails versions. # It's best enabled when your entire app is migrated and stable on 6.1. Rails.application.config.action_controller.urlsafe_csrf_tokens = true # Specify whether `ActiveSupport::TimeZone.utc_to_local` returns a time with an # UTC offset or a UTC time. ActiveSupport.utc_to_local_returns_utc_offset_times = true # Change the default HTTP status code to `308` when redirecting non-GET/HEAD # requests to HTTPS in `ActionDispatch::SSL` middleware. Rails.application.config.action_dispatch.ssl_default_redirect_status = 308 # Use new connection handling API. For most applications this won't have any # effect. For applications using multiple databases, this new API provides # support for granular connection swapping. Rails.application.config.active_record.legacy_connection_handling = false # Make `form_with` generate non-remote forms by default. Rails.application.config.action_view.form_with_generates_remote_forms = false # Set the default queue name for the analysis job to the queue adapter default. Rails.application.config.active_storage.queues.analysis = nil # Set the default queue name for the purge job to the queue adapter default. Rails.application.config.active_storage.queues.purge = nil # Set the default queue name for the incineration job to the queue adapter default. Rails.application.config.action_mailbox.queues.incineration = nil # Set the default queue name for the routing job to the queue adapter default. Rails.application.config.action_mailbox.queues.routing = nil # Set the default queue name for the mail deliver job to the queue adapter default. Rails.application.config.action_mailer.deliver_later_queue_name = nil # Generate a `Link` header that gives a hint to modern browsers about # preloading assets when using `javascript_include_tag` and `stylesheet_link_tag`. # Rails.application.config.action_view.preload_links_header = true
修改后的application.rb配置片段
if $next_rails config.load_defaults 6.1 else config.load_defaults 6.0 end
报错信息
RuntimeError: Wrapped undumpable exception for: ActiveRecord::RecordNotUnique: PG::UniqueViolation: ERROR: duplicate key value violates unique constraint "as_bs_unique" DETAIL: Key (a_id, b_id)=(493764392, 897260172) already exists.
Error: RuntimeError: Wrapped undumpable exception for: ActiveRecord::NotNullViolation: PG::NotNullViolation: ERROR: null value in column "site_id" of relation "xyz_tags" violates not-null constraint DETAIL: Failing row contains (16, null, 16).
可能的原因及解决思路
重点排查
has_many_inversing配置
Rails 6.1默认启用了config.active_record.has_many_inversing = true,这个配置会自动反转belongs_to与has_many的关联,可能导致对象保存时的状态同步逻辑变化。比如之前代码依赖未反转的关联,现在反转后关联对象的属性会自动同步,可能触发重复创建记录或字段未正确赋值的问题。- 临时解决方案:在
application.rb中显式关闭该配置:config.active_record.has_many_inversing = false,重新运行测试,如果测试恢复正常,再逐个排查受影响的关联代码,调整逻辑适配关联反转的行为。
- 临时解决方案:在
分步启用6.1默认配置
不要直接移除new_framework_defaults_6_1.rb,而是先保留该文件,然后逐个注释掉里面的配置项(即启用默认值),每次只启用一个,运行测试,定位到具体是哪个配置导致的错误,再针对性修复。检查测试用例的对象创建逻辑
- 对于唯一约束错误:查看测试中是否存在重复创建关联记录的逻辑,比如在关联赋值时多次调用
create或build,关联反转后可能导致这些操作被重复执行。 - 对于非空约束错误:检查
site_id这类字段的赋值逻辑,是否依赖关联对象的状态同步,关联反转后是否因为对象未正确加载导致字段未赋值。
- 对于唯一约束错误:查看测试中是否存在重复创建关联记录的逻辑,比如在关联赋值时多次调用
验证数据库连接与事务逻辑
Rails 6.1默认关闭了legacy_connection_handling,虽然大部分项目不受影响,但如果项目使用多数据库或自定义事务逻辑,可能导致连接管理变化,进而影响测试中的数据写入。可以尝试临时开启config.active_record.legacy_connection_handling = true,看是否能解决问题。
内容的提问来源于stack exchange,提问作者Kiran Kumawat
相关产品推荐
相关产品推荐

