bensheldon / good_job

Multithreaded, Postgres-based, Active Job backend for Ruby on Rails.
https://goodjob-demo.herokuapp.com/
MIT License
2.69k stars 200 forks source link

Regression: use of Rails reloader causing mixed constants during seeding #1215

Closed zarqman closed 9 months ago

zarqman commented 9 months ago

I'm running into an issue with missing/mismatched constants during rails db:seed. I've tracked it down to changes made in #1124.

Explanation

This is a super simplified example.

seeds.rb has this general shape:

car = Car.new
car.wheels.new
car.save!

and Wheel has:

after_create { SomeJob.perform_later }

and Car has:

# this is actually from a Concern that applies to several models
after_save do
  case self
  when Car
    # do something
  else
    raise "error"
  end
end

GoodJob's use of the reloader during enqueue_at is causing a code reload when SomeJob.perform_later is called.

By the time Car's callbacks run (which is after Wheel's), Car != Car and the case statement errors.

Context

Rails 7.1.2 GoodJob 3.19.4 works properly GoodJob 3.20.0-3.22.0 are broken

Fixes / Workarounds

It's possible to surround the entire contents of seeds.rb with Rails.application.reloader.wrap{ ... }. However, since seeds.rb is standard Rails behavior, it'd be nice to find a proper fix.

I'll try to take a look at GJ's internals before too long and see what can be done to fix it. In the meantime, thought I'd create an issue now and see what you think?

zarqman commented 9 months ago

Debugging

I found this helpful when debugging:

# sometime before contents of seeds.rb runs:
Rails.autoloaders.main.on_unload do |class_name, _, _|
  puts "Removed const: #{class_name}"
  pp caller if class_name == 'Car'
end

In the backtrace, the first mention of good_job (line 42) points to enqueue_at.

Backtrace ``` ~/.rvm/gems/ruby-3.2.2@rails71/gems/zeitwerk-2.6.12/lib/zeitwerk/loader.rb:555:in `block in run_on_unload_callbacks' ~/.rvm/gems/ruby-3.2.2@rails71/gems/zeitwerk-2.6.12/lib/zeitwerk/loader.rb:555:in `each' ~/.rvm/gems/ruby-3.2.2@rails71/gems/zeitwerk-2.6.12/lib/zeitwerk/loader.rb:555:in `run_on_unload_callbacks' ~/.rvm/gems/ruby-3.2.2@rails71/gems/zeitwerk-2.6.12/lib/zeitwerk/loader.rb:178:in `block (2 levels) in unload' ~/.rvm/gems/ruby-3.2.2@rails71/gems/zeitwerk-2.6.12/lib/zeitwerk/loader.rb:169:in `each' ~/.rvm/gems/ruby-3.2.2@rails71/gems/zeitwerk-2.6.12/lib/zeitwerk/loader.rb:169:in `block in unload' ~/.rvm/gems/ruby-3.2.2@rails71/gems/zeitwerk-2.6.12/lib/zeitwerk/loader.rb:146:in `synchronize' ~/.rvm/gems/ruby-3.2.2@rails71/gems/zeitwerk-2.6.12/lib/zeitwerk/loader.rb:146:in `unload' ~/.rvm/gems/ruby-3.2.2@rails71/gems/zeitwerk-2.6.12/lib/zeitwerk/loader.rb:227:in `reload' ~/.rvm/gems/ruby-3.2.2@rails71/gems/activesupport-7.1.2/lib/active_support/dependencies.rb:79:in `block in clear' ~/.rvm/gems/ruby-3.2.2@rails71/gems/activesupport-7.1.2/lib/active_support/concurrency/share_lock.rb:151:in `exclusive' ~/.rvm/gems/ruby-3.2.2@rails71/gems/activesupport-7.1.2/lib/active_support/dependencies/interlock.rb:17:in `unloading' ~/.rvm/gems/ruby-3.2.2@rails71/gems/activesupport-7.1.2/lib/active_support/dependencies.rb:32:in `unload_interlock' ~/.rvm/gems/ruby-3.2.2@rails71/gems/activesupport-7.1.2/lib/active_support/dependencies.rb:77:in `clear' ~/.rvm/gems/ruby-3.2.2@rails71/gems/railties-7.1.2/lib/rails/application/finisher.rb:185:in `block (2 levels) in ' ~/.rvm/gems/ruby-3.2.2@rails71/gems/activesupport-7.1.2/lib/active_support/file_update_checker.rb:85:in `execute' ~/.rvm/gems/ruby-3.2.2@rails71/gems/railties-7.1.2/lib/rails/application/finisher.rb:214:in `block (3 levels) in ' ~/.rvm/gems/ruby-3.2.2@rails71/gems/activesupport-7.1.2/lib/active_support/callbacks.rb:110:in `run_callbacks' ~/.rvm/gems/ruby-3.2.2@rails71/gems/activesupport-7.1.2/lib/active_support/reloader.rb:128:in `class_unload!' ~/.rvm/gems/ruby-3.2.2@rails71/gems/railties-7.1.2/lib/rails/application/finisher.rb:213:in `block (2 levels) in ' ~/.rvm/gems/ruby-3.2.2@rails71/gems/activesupport-7.1.2/lib/active_support/callbacks.rb:448:in `instance_exec' ~/.rvm/gems/ruby-3.2.2@rails71/gems/activesupport-7.1.2/lib/active_support/callbacks.rb:448:in `block in make_lambda' ~/.rvm/gems/ruby-3.2.2@rails71/gems/activesupport-7.1.2/lib/active_support/callbacks.rb:202:in `block (2 levels) in halting' ~/.rvm/gems/ruby-3.2.2@rails71/gems/activesupport-7.1.2/lib/active_support/callbacks.rb:707:in `block (2 levels) in default_terminator' ~/.rvm/gems/ruby-3.2.2@rails71/gems/activesupport-7.1.2/lib/active_support/callbacks.rb:706:in `catch' ~/.rvm/gems/ruby-3.2.2@rails71/gems/activesupport-7.1.2/lib/active_support/callbacks.rb:706:in `block in default_terminator' ~/.rvm/gems/ruby-3.2.2@rails71/gems/activesupport-7.1.2/lib/active_support/callbacks.rb:203:in `block in halting' ~/.rvm/gems/ruby-3.2.2@rails71/gems/activesupport-7.1.2/lib/active_support/callbacks.rb:598:in `block in invoke_before' ~/.rvm/gems/ruby-3.2.2@rails71/gems/activesupport-7.1.2/lib/active_support/callbacks.rb:598:in `each' ~/.rvm/gems/ruby-3.2.2@rails71/gems/activesupport-7.1.2/lib/active_support/callbacks.rb:598:in `invoke_before' ~/.rvm/gems/ruby-3.2.2@rails71/gems/activesupport-7.1.2/lib/active_support/callbacks.rb:109:in `run_callbacks' ~/.rvm/gems/ruby-3.2.2@rails71/gems/activesupport-7.1.2/lib/active_support/execution_wrapper.rb:129:in `run' ~/.rvm/gems/ruby-3.2.2@rails71/gems/activesupport-7.1.2/lib/active_support/execution_wrapper.rb:125:in `run!' ~/.rvm/gems/ruby-3.2.2@rails71/gems/activesupport-7.1.2/lib/active_support/reloader.rb:122:in `run!' ~/.rvm/gems/ruby-3.2.2@rails71/gems/activesupport-7.1.2/lib/active_support/execution_wrapper.rb:78:in `block in run!' :90:in `tap' ~/.rvm/gems/ruby-3.2.2@rails71/gems/activesupport-7.1.2/lib/active_support/execution_wrapper.rb:75:in `run!' ~/.rvm/gems/ruby-3.2.2@rails71/gems/activesupport-7.1.2/lib/active_support/reloader.rb:64:in `run!' ~/.rvm/gems/ruby-3.2.2@rails71/gems/activesupport-7.1.2/lib/active_support/reloader.rb:75:in `block in wrap' ~/.rvm/gems/ruby-3.2.2@rails71/gems/activesupport-7.1.2/lib/active_support/execution_wrapper.rb:88:in `wrap' ~/.rvm/gems/ruby-3.2.2@rails71/gems/activesupport-7.1.2/lib/active_support/reloader.rb:74:in `wrap' ~/.rvm/gems/ruby-3.2.2@rails71/gems/good_job-3.22.0/lib/good_job/adapter.rb:142:in `enqueue_at' ~/.rvm/gems/ruby-3.2.2@rails71/gems/activejob-7.1.2/lib/active_job/enqueuing.rb:95:in `block in enqueue' ~/.rvm/gems/ruby-3.2.2@rails71/gems/activesupport-7.1.2/lib/active_support/callbacks.rb:121:in `block in run_callbacks' ~/.rvm/gems/ruby-3.2.2@rails71/gems/activejob-7.1.2/lib/active_job/instrumentation.rb:40:in `block in instrument' ~/.rvm/gems/ruby-3.2.2@rails71/gems/activesupport-7.1.2/lib/active_support/notifications.rb:206:in `block in instrument' ~/.rvm/gems/ruby-3.2.2@rails71/gems/activesupport-7.1.2/lib/active_support/notifications/instrumenter.rb:58:in `instrument' ~/.rvm/gems/ruby-3.2.2@rails71/gems/activesupport-7.1.2/lib/active_support/notifications.rb:206:in `instrument' ~/.rvm/gems/ruby-3.2.2@rails71/gems/activejob-7.1.2/lib/active_job/instrumentation.rb:39:in `instrument' ~/.rvm/gems/ruby-3.2.2@rails71/gems/activerecord-7.1.2/lib/active_record/railties/job_runtime.rb:18:in `instrument' ~/.rvm/gems/ruby-3.2.2@rails71/gems/activejob-7.1.2/lib/active_job/instrumentation.rb:21:in `block (2 levels) in ' ~/.rvm/gems/ruby-3.2.2@rails71/gems/activesupport-7.1.2/lib/active_support/callbacks.rb:130:in `instance_exec' ~/.rvm/gems/ruby-3.2.2@rails71/gems/activesupport-7.1.2/lib/active_support/callbacks.rb:130:in `block in run_callbacks' ~/.rvm/gems/ruby-3.2.2@rails71/gems/activesupport-7.1.2/lib/active_support/tagged_logging.rb:135:in `block in tagged' ~/.rvm/gems/ruby-3.2.2@rails71/gems/activesupport-7.1.2/lib/active_support/tagged_logging.rb:39:in `tagged' ~/.rvm/gems/ruby-3.2.2@rails71/gems/activesupport-7.1.2/lib/active_support/tagged_logging.rb:135:in `tagged' ~/.rvm/gems/ruby-3.2.2@rails71/gems/activesupport-7.1.2/lib/active_support/broadcast_logger.rb:240:in `method_missing' ~/.rvm/gems/ruby-3.2.2@rails71/gems/activejob-7.1.2/lib/active_job/logging.rb:25:in `tag_logger' ~/.rvm/gems/ruby-3.2.2@rails71/gems/activejob-7.1.2/lib/active_job/logging.rb:14:in `block (2 levels) in ' ~/.rvm/gems/ruby-3.2.2@rails71/gems/activesupport-7.1.2/lib/active_support/callbacks.rb:130:in `instance_exec' ~/.rvm/gems/ruby-3.2.2@rails71/gems/activesupport-7.1.2/lib/active_support/callbacks.rb:130:in `block in run_callbacks' ~/.rvm/gems/ruby-3.2.2@rails71/gems/activesupport-7.1.2/lib/active_support/callbacks.rb:141:in `run_callbacks' ~/.rvm/gems/ruby-3.2.2@rails71/gems/activejob-7.1.2/lib/active_job/enqueuing.rb:93:in `enqueue' ~/.rvm/gems/ruby-3.2.2@rails71/gems/activejob-7.1.2/lib/active_job/configured_job.rb:15:in `perform_later' ~/Code/new_app/app/models/wheel.rb:72:in `schedule_job' ~/.rvm/gems/ruby-3.2.2@rails71/gems/activesupport-7.1.2/lib/active_support/callbacks.rb:403:in `block in make_lambda' ~/.rvm/gems/ruby-3.2.2@rails71/gems/activesupport-7.1.2/lib/active_support/callbacks.rb:239:in `block in halting_and_conditional' ~/.rvm/gems/ruby-3.2.2@rails71/gems/activesupport-7.1.2/lib/active_support/callbacks.rb:602:in `block in invoke_after' ~/.rvm/gems/ruby-3.2.2@rails71/gems/activesupport-7.1.2/lib/active_support/callbacks.rb:602:in `each' ~/.rvm/gems/ruby-3.2.2@rails71/gems/activesupport-7.1.2/lib/active_support/callbacks.rb:602:in `invoke_after' ~/.rvm/gems/ruby-3.2.2@rails71/gems/activesupport-7.1.2/lib/active_support/callbacks.rb:111:in `run_callbacks' ~/.rvm/gems/ruby-3.2.2@rails71/gems/activesupport-7.1.2/lib/active_support/callbacks.rb:952:in `_run_create_callbacks' ~/.rvm/gems/ruby-3.2.2@rails71/gems/activerecord-7.1.2/lib/active_record/callbacks.rb:445:in `_create_record' ~/.rvm/gems/ruby-3.2.2@rails71/gems/activerecord-7.1.2/lib/active_record/timestamp.rb:114:in `_create_record' ~/.rvm/gems/ruby-3.2.2@rails71/gems/activerecord-7.1.2/lib/active_record/persistence.rb:1220:in `create_or_update' ~/.rvm/gems/ruby-3.2.2@rails71/gems/activerecord-7.1.2/lib/active_record/callbacks.rb:441:in `block in create_or_update' ~/.rvm/gems/ruby-3.2.2@rails71/gems/activesupport-7.1.2/lib/active_support/callbacks.rb:121:in `block in run_callbacks' ~/.rvm/gems/ruby-3.2.2@rails71/gems/activerecord-7.1.2/lib/active_record/autosave_association.rb:375:in `around_save_collection_association' ~/.rvm/gems/ruby-3.2.2@rails71/gems/activesupport-7.1.2/lib/active_support/callbacks.rb:130:in `block in run_callbacks' ~/.rvm/gems/ruby-3.2.2@rails71/gems/activesupport-7.1.2/lib/active_support/callbacks.rb:141:in `run_callbacks' ~/.rvm/gems/ruby-3.2.2@rails71/gems/activesupport-7.1.2/lib/active_support/callbacks.rb:952:in `_run_save_callbacks' ~/.rvm/gems/ruby-3.2.2@rails71/gems/activerecord-7.1.2/lib/active_record/callbacks.rb:441:in `create_or_update' ~/.rvm/gems/ruby-3.2.2@rails71/gems/activerecord-7.1.2/lib/active_record/timestamp.rb:125:in `create_or_update' ~/.rvm/gems/ruby-3.2.2@rails71/gems/activerecord-7.1.2/lib/active_record/persistence.rb:751:in `save!' ~/.rvm/gems/ruby-3.2.2@rails71/gems/activerecord-7.1.2/lib/active_record/validations.rb:55:in `save!' ~/.rvm/gems/ruby-3.2.2@rails71/gems/activerecord-7.1.2/lib/active_record/transactions.rb:313:in `block in save!' ~/.rvm/gems/ruby-3.2.2@rails71/gems/activerecord-7.1.2/lib/active_record/transactions.rb:365:in `block in with_transaction_returning_status' ~/.rvm/gems/ruby-3.2.2@rails71/gems/activerecord-7.1.2/lib/active_record/connection_adapters/abstract/database_statements.rb:342:in `transaction' ~/.rvm/gems/ruby-3.2.2@rails71/gems/activerecord-7.1.2/lib/active_record/transactions.rb:361:in `with_transaction_returning_status' ~/.rvm/gems/ruby-3.2.2@rails71/gems/activerecord-7.1.2/lib/active_record/transactions.rb:313:in `save!' ~/.rvm/gems/ruby-3.2.2@rails71/gems/activerecord-7.1.2/lib/active_record/suppressor.rb:56:in `save!' ~/.rvm/gems/ruby-3.2.2@rails71/gems/activerecord-7.1.2/lib/active_record/associations/collection_association.rb:371:in `insert_record' ~/.rvm/gems/ruby-3.2.2@rails71/gems/activerecord-7.1.2/lib/active_record/associations/has_many_association.rb:63:in `insert_record' ~/.rvm/gems/ruby-3.2.2@rails71/gems/activerecord-7.1.2/lib/active_record/associations/collection_association.rb:358:in `block (2 levels) in _create_record' ~/.rvm/gems/ruby-3.2.2@rails71/gems/activerecord-7.1.2/lib/active_record/associations/collection_association.rb:462:in `replace_on_target' ~/.rvm/gems/ruby-3.2.2@rails71/gems/activerecord-7.1.2/lib/active_record/associations/collection_association.rb:278:in `add_to_target' ~/.rvm/gems/ruby-3.2.2@rails71/gems/activerecord-7.1.2/lib/active_record/associations/collection_association.rb:357:in `block in _create_record' ~/.rvm/gems/ruby-3.2.2@rails71/gems/activerecord-7.1.2/lib/active_record/connection_adapters/abstract/database_statements.rb:342:in `transaction' ~/.rvm/gems/ruby-3.2.2@rails71/gems/activerecord-7.1.2/lib/active_record/transactions.rb:212:in `transaction' ~/.rvm/gems/ruby-3.2.2@rails71/gems/activerecord-7.1.2/lib/active_record/associations/collection_association.rb:314:in `transaction' ~/.rvm/gems/ruby-3.2.2@rails71/gems/activerecord-7.1.2/lib/active_record/associations/collection_association.rb:355:in `_create_record' ~/.rvm/gems/ruby-3.2.2@rails71/gems/activerecord-7.1.2/lib/active_record/associations/has_many_association.rb:147:in `_create_record' ~/.rvm/gems/ruby-3.2.2@rails71/gems/activerecord-7.1.2/lib/active_record/associations/association.rb:210:in `create!' ~/.rvm/gems/ruby-3.2.2@rails71/gems/activerecord-7.1.2/lib/active_record/associations/collection_proxy.rb:366:in `create!' ~/Code/new_app/app/models/car.rb:152:in `create_parts' ~/.rvm/gems/ruby-3.2.2@rails71/gems/can_has_state-0.9.0/lib/can_has_state/trigger.rb:41:in `block in call' ~/.rvm/gems/ruby-3.2.2@rails71/gems/can_has_state-0.9.0/lib/can_has_state/trigger.rb:32:in `each' ~/.rvm/gems/ruby-3.2.2@rails71/gems/can_has_state-0.9.0/lib/can_has_state/trigger.rb:32:in `call' ~/.rvm/gems/ruby-3.2.2@rails71/gems/can_has_state-0.9.0/lib/can_has_state/machine.rb:108:in `block (2 levels) in run_deferred_state_triggers' ~/.rvm/gems/ruby-3.2.2@rails71/gems/can_has_state-0.9.0/lib/can_has_state/machine.rb:105:in `each' ~/.rvm/gems/ruby-3.2.2@rails71/gems/can_has_state-0.9.0/lib/can_has_state/machine.rb:105:in `block in run_deferred_state_triggers' ~/.rvm/gems/ruby-3.2.2@rails71/gems/can_has_state-0.9.0/lib/can_has_state/machine.rb:94:in `each' ~/.rvm/gems/ruby-3.2.2@rails71/gems/can_has_state-0.9.0/lib/can_has_state/machine.rb:94:in `run_deferred_state_triggers' ~/.rvm/gems/ruby-3.2.2@rails71/gems/activesupport-7.1.2/lib/active_support/callbacks.rb:403:in `block in make_lambda' ~/.rvm/gems/ruby-3.2.2@rails71/gems/activesupport-7.1.2/lib/active_support/callbacks.rb:239:in `block in halting_and_conditional' ~/.rvm/gems/ruby-3.2.2@rails71/gems/activesupport-7.1.2/lib/active_support/callbacks.rb:602:in `block in invoke_after' ~/.rvm/gems/ruby-3.2.2@rails71/gems/activesupport-7.1.2/lib/active_support/callbacks.rb:602:in `each' ~/.rvm/gems/ruby-3.2.2@rails71/gems/activesupport-7.1.2/lib/active_support/callbacks.rb:602:in `invoke_after' ~/.rvm/gems/ruby-3.2.2@rails71/gems/activesupport-7.1.2/lib/active_support/callbacks.rb:135:in `block in run_callbacks' ~/.rvm/gems/ruby-3.2.2@rails71/gems/activesupport-7.1.2/lib/active_support/callbacks.rb:141:in `run_callbacks' ~/.rvm/gems/ruby-3.2.2@rails71/gems/activesupport-7.1.2/lib/active_support/callbacks.rb:952:in `_run_save_callbacks' ~/.rvm/gems/ruby-3.2.2@rails71/gems/activerecord-7.1.2/lib/active_record/callbacks.rb:441:in `create_or_update' ~/.rvm/gems/ruby-3.2.2@rails71/gems/activerecord-7.1.2/lib/active_record/timestamp.rb:125:in `create_or_update' ~/.rvm/gems/ruby-3.2.2@rails71/gems/activerecord-7.1.2/lib/active_record/persistence.rb:751:in `save!' ~/.rvm/gems/ruby-3.2.2@rails71/gems/activerecord-7.1.2/lib/active_record/validations.rb:55:in `save!' ~/.rvm/gems/ruby-3.2.2@rails71/gems/activerecord-7.1.2/lib/active_record/transactions.rb:313:in `block in save!' ~/.rvm/gems/ruby-3.2.2@rails71/gems/activerecord-7.1.2/lib/active_record/transactions.rb:365:in `block in with_transaction_returning_status' ~/.rvm/gems/ruby-3.2.2@rails71/gems/activerecord-7.1.2/lib/active_record/connection_adapters/abstract/database_statements.rb:342:in `transaction' ~/.rvm/gems/ruby-3.2.2@rails71/gems/activerecord-7.1.2/lib/active_record/transactions.rb:361:in `with_transaction_returning_status' ~/.rvm/gems/ruby-3.2.2@rails71/gems/activerecord-7.1.2/lib/active_record/transactions.rb:313:in `save!' ~/.rvm/gems/ruby-3.2.2@rails71/gems/activerecord-7.1.2/lib/active_record/suppressor.rb:56:in `save!' ~/.rvm/gems/ruby-3.2.2@rails71/gems/activerecord-7.1.2/lib/active_record/associations/collection_association.rb:371:in `insert_record' ~/.rvm/gems/ruby-3.2.2@rails71/gems/activerecord-7.1.2/lib/active_record/associations/has_many_association.rb:63:in `insert_record' ~/.rvm/gems/ruby-3.2.2@rails71/gems/activerecord-7.1.2/lib/active_record/associations/collection_association.rb:358:in `block (2 levels) in _create_record' ~/.rvm/gems/ruby-3.2.2@rails71/gems/activerecord-7.1.2/lib/active_record/associations/collection_association.rb:462:in `replace_on_target' ~/.rvm/gems/ruby-3.2.2@rails71/gems/activerecord-7.1.2/lib/active_record/associations/collection_association.rb:278:in `add_to_target' ~/.rvm/gems/ruby-3.2.2@rails71/gems/activerecord-7.1.2/lib/active_record/associations/collection_association.rb:357:in `block in _create_record' ~/.rvm/gems/ruby-3.2.2@rails71/gems/activerecord-7.1.2/lib/active_record/connection_adapters/abstract/database_statements.rb:342:in `transaction' ~/.rvm/gems/ruby-3.2.2@rails71/gems/activerecord-7.1.2/lib/active_record/transactions.rb:212:in `transaction' ~/.rvm/gems/ruby-3.2.2@rails71/gems/activerecord-7.1.2/lib/active_record/associations/collection_association.rb:314:in `transaction' ~/.rvm/gems/ruby-3.2.2@rails71/gems/activerecord-7.1.2/lib/active_record/associations/collection_association.rb:355:in `_create_record' ~/.rvm/gems/ruby-3.2.2@rails71/gems/activerecord-7.1.2/lib/active_record/associations/has_many_association.rb:147:in `_create_record' ~/.rvm/gems/ruby-3.2.2@rails71/gems/activerecord-7.1.2/lib/active_record/associations/association.rb:210:in `create!' ~/.rvm/gems/ruby-3.2.2@rails71/gems/activerecord-7.1.2/lib/active_record/associations/collection_proxy.rb:366:in `create!' ~/Code/new_app/db/seeds.rb:102:in `block in
' ~/.rvm/gems/ruby-3.2.2@rails71/gems/activerecord-7.1.2/lib/active_record/connection_adapters/abstract/transaction.rb:535:in `block in within_new_transaction' ~/.rvm/gems/ruby-3.2.2@rails71/gems/activesupport-7.1.2/lib/active_support/concurrency/null_lock.rb:9:in `synchronize' ~/.rvm/gems/ruby-3.2.2@rails71/gems/activerecord-7.1.2/lib/active_record/connection_adapters/abstract/transaction.rb:532:in `within_new_transaction' ~/.rvm/gems/ruby-3.2.2@rails71/gems/activerecord-7.1.2/lib/active_record/connection_adapters/abstract/database_statements.rb:344:in `transaction' ~/.rvm/gems/ruby-3.2.2@rails71/gems/activerecord-7.1.2/lib/active_record/transactions.rb:212:in `transaction' ~/Code/new_app/db/seeds.rb:25:in `
' ... ```
bensheldon commented 9 months ago

Thanks for reporting this!! It's a little unexpected because I think that seeds should already be wrapped with an Executor: https://github.com/rails/rails/pull/40626

...so wrap in GoodJob should be a noop. Though I'm still a little unclear on the implications of wrapping a reloader with an executor or vice versa.

I'm reluctant to remove those executors/reloaders from the Adapter because I have seen the problem of people enqueuing jobs inline without an Executor and then the database connection is dropped, losing the advisory lock prematurely.

So maybe the issue here is that it's a reloader and it should be an executor.

zarqman commented 9 months ago

Interesting. I tried manually wrapping the seeds with an Executor and it still fails. But if Rails is already wrapping in an Executor, then that would make sense. But when I wrap them with a Reloader, then they work. So it would seem than an outside Executor (Rails or seeds) and inside Reloader (GoodJob) still triggers an actual reload, whereas a Reloader both outside and inside causes the inside to no-op.

It sounds like you're using Executor/Reloader both for code reloading and database connection handling?

I'll try changing enqueue_at to use an executor and report back.

zarqman commented 9 months ago

I swapped out reloader to executor inside enqueue_at and that does resolve the errant reload. The app's test suite and a spot check don't indicate any negative side effects. Reloading of *Job classes still happens with env=development.

segiddins commented 9 months ago

Chiming in that changing to Rails.application.executor.wrap inside enqueue_at also resolves the reload error I was running into

bensheldon commented 9 months ago

@zarqman thank you for digging into that and @segiddins for the confirmation 🙇🏻 🙇🏻

I'll make a quick PR converting those places (anywhere that isn't in a thread that GoodJob for sure sets up itself) into an executor instead of a reloader.

I think I'm still unsure if it's possible to drop database connections moving between reloader <--> executor contexts, but let's solve the problem in front of us right now.