has_one associations allow you to define a parent-child relationship. In addition to that, the
dependent option controls what happens to the child records when the parent record is destroyed.
dependent option accepts the following values (Reference: Rails API doc):
:nullify-- used to dereference the parent by setting the foreign key of the child records as
:destroy-- used to delete the child records by calling
.destroyon them one by one so that the callbacks are processed.
:delete_all-- also used to delete the child records, but skips the callback processing by calling
:restrict_with_exception-- causes an
ActiveRecord::DeleteRestrictionErrorexception to be raised if there are any associated records.
:restrict_with_error- causes an error to be added to the owner if there are any associated objects.
With the merging of the Offer dependent: :destroy_async for associations PR, a new value is introduced in Rails 6.1, namely
:destroy_async. This value makes the
dependent option function the same as when passing
:destroy, except the callback processing happens in the background job named
ActiveRecord::DestroyAssociationAsyncJob. The main reason for introducing this option was to reduce the server response time since the end-user shouldn't have to wait for all the child records to be deleted.
With Rails >= 7.1
Rails 7.1 takes this a step further and introduces
Rails.application.config.active_record.destroy_association_async_batch_size, courtesy of the Add active_record.destroy_association_async_batch_size configuration PR. If this config is set, the
ActiveRecord::DestroyAssociationAsyncJob jobs are enqueued several times as necessary, with a maximum number of records per job as specified in the config.
Here's an example of one such case where the
User model has many
Comment records that need to be destroyed when the
User record is deleted:
# app/models/user.rb class User has_many :comments, dependent: :destroy_async end
destroy_async, being set, once the
User instance is destroyed, all the related
Comment records are to be destroyed in a single background job.
This is fine, but you may not want Rails to call
.destroy on each
Comment record if the number of comments is huge. The reason would be that it would take forever and other delayed jobs might not get processed due to it. The solution to this is to set the
Rails.application.config.active_record.destroy_association_async_batch_size option to a reasonable value, for example
# config/environments/application.rb Rails.application.configure do # ... config.active_record.destroy_association_async_batch_size = 100 # ... end
Due to this, if a user has let's say 1000 comments, and if the user record is destroyed,
ActiveRecord::DestroyAssociationAsyncJob will be enqueued with a maximum of 100 records per job, and a total of 10 such jobs will be enqueued to achieve the desired result.
By default, the value for
nil, which means that all child records will be deleted in single background job. One can specify a smaller batch size to allow a higher number of short-duration jobs or a larger batch size to allow a lower number of long-duration jobs.
With this new functionality, the developers may fine-tune their Ruby on Rails applications by controlling the frequency and duration of