When running Rails rails db: seed, the following error may occur.
$ rails db:seed
rails aborted!
ActiveRecord::StatementInvalid: SQLite3::BusyException: database is locked
/path-to-your-app/db/seeds.rb:69:in `<main>'
The code around the error is as follows.
# ...
User.destroy_all
100.times do |n|
  user = User.create!(
    email: "sample-#{n}@example.com",
    password: 'password',
    name: Faker::Name.name
  )
  image_url = Faker::Avatar.image(slug: user.email, size: '150x150')
  #Set up avatar using Active Storage
  user.avatar.attach(io: URI.parse(image_url).open, filename: 'avatar.png')
end
# ...
In addition, this application uses SQLite3.
default: &default
  adapter: sqlite3
  pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
  timeout: 5000
development:
  <<: *default
  database: db/development.sqlite3
This error is caused by two factors:
--Since ActiveStorage is used, images are uploaded and deleted asynchronously. --SQLite3 is vulnerable to concurrency (Reference)
When User.destroy_all is executed, the image deletion process is executed asynchronously for the existing data. If you have a large amount of existing data, read and write queries will be issued to the database in parallel.
Similarly, user.avatar.attach also performs image upload processing asynchronously. In the above code example, we tried to create 100 user data, so read and write queries are also issued to the database in parallel.
SQLite3 :: BusyException occurs when SQLiter3 cannot withstand concurrency.
Considering the performance restrictions of SQLite3, delete and upload images should be performed synchronously. Specifically, add the following two lines to config/seeds.rb.
+ActiveStorage::AnalyzeJob.queue_adapter = :inline
+ActiveStorage::PurgeJob.queue_adapter = :inline
 User.destroy_all
 100.times do |n|
   user = User.create!(
     email: "sample-#{n}@example.com",
     password: 'password',
     name: Faker::Name.name
   )
   image_url = Faker::Avatar.image(slug: user.email, size: '150x150')
   user.avatar.attach(io: URI.parse(image_url).open, filename: 'avatar.png')
 end
ActiveStorage :: AnalyzeJob is a class of ActiveJob used at the time of upload and ActiveStorage :: PurgeJob is used at the time of deletion.
By changing this queue_adapter to: inline, you can upload and delete images synchronously.
Reference https://api.rubyonrails.org/classes/ActiveJob/QueueAdapters/InlineAdapter.html
Recommended Posts