Content
View differences
Updated by Christophe Bliard about 2 years ago
### Steps to reproduce
0: Run OpenProject 14 as follows.
```bash
docker run -e OPENPROJECT_HTTPS=false -e OPENPROJECT_GOOD__JOB__MAX__THREADS=1 -p 8080:80 --rm -it openproject/openproject:14
```
1. Create a [new work package custom field](http://localhost:8080/custom_fields) "CF1", enabled for all projects (type doesn't matter)
2. Enable it for the type 'Task' via the [form configuration](http://localhost:8080/types/1/edit/form_configuration)
3. Enable the "Time and costs" module in the Demo [project settings](http://localhost:8080/projects/demo-project/settings/modules)
4. Configure a [cost report](http://localhost:8080/projects/demo-project/cost_reports) filtering for CF1 (no data required)
5. Export it to XLS (button in top right corner)
6. Create a custom field "CF2", enabled for all projects (like in step 1)
7. Enable it for the type 'Task' (like in step 2)
8. Configure a cost report filtering for CF2 (no data required)
9. Export it to XLS (button in top right corner)
### What is the buggy behavior?
The export fails with an error.
<img class="op-uc-image op-uc-image_inline" src="/api/v3/attachments/100836/content">
### What is the expected behavior?
The export should work.
### **Logs**
```text
E, [2024-04-26T10:30:12.717661 #147] ERROR -- : [ActiveJob] [CostQuery::ExportJob] [ce2b9913-fad7-413f-9a8d-088f6f799df1] Error performing CostQuery::ExportJob (Job ID: ce2b9913-fad7-413f-9a8d-088f6f799df1) from GoodJob(default) in 24.52ms: NameError (uninitialized constant CostQuery::Filter::CustomField2):
/app/modules/reporting/app/models/cost_query.rb:142:in `add_chain'
/app/modules/reporting/app/models/cost_query.rb:170:in `filter'
/app/modules/reporting/app/workers/cost_query/export_job.rb:49:in `block (2 levels) in build_query'
/app/modules/reporting/app/workers/cost_query/export_job.rb:46:in `each'
/app/modules/reporting/app/workers/cost_query/export_job.rb:46:in `block in build_query'
<internal:kernel>:90:in `tap'
/app/modules/reporting/app/workers/cost_query/export_job.rb:45:in `build_query'
/app/modules/reporting/app/workers/cost_query/export_job.rb:21:in `prepare!'
/app/app/workers/exports/export_job.rb:15:in `block in perform'
/app/lib/open_project/locale_helper.rb:36:in `with_locale_for'
/app/app/models/user.rb:497:in `execute_as'
/app/app/workers/exports/export_job.rb:14:in `perform'
/app/vendor/bundle/ruby/3.2.0/gems/activejob-7.1.3.2/lib/active_job/execution.rb:69:in `block in _perform_job'
/app/vendor/bundle/ruby/3.2.0/gems/activesupport-7.1.3.2/lib/active_support/callbacks.rb:121:in `block in run_callbacks'
/app/app/workers/application_job.rb:106:in `block in prepare_job_context'
/app/app/workers/application_job.rb:79:in `with_clean_request_store'
/app/app/workers/application_job.rb:102:in `prepare_job_context'
/app/vendor/bundle/ruby/3.2.0/gems/activesupport-7.1.3.2/lib/active_support/callbacks.rb:130:in `block in run_callbacks'
/app/vendor/bundle/ruby/3.2.0/gems/i18n-1.14.4/lib/i18n.rb:322:in `with_locale'
/app/vendor/bundle/ruby/3.2.0/gems/activejob-7.1.3.2/lib/active_job/translation.rb:9:in `block (2 levels) in <module:Translation>'
/app/vendor/bundle/ruby/3.2.0/gems/activesupport-7.1.3.2/lib/active_support/callbacks.rb:130:in `instance_exec'
/app/vendor/bundle/ruby/3.2.0/gems/activesupport-7.1.3.2/lib/active_support/callbacks.rb:130:in `block in run_callbacks'
/app/vendor/bundle/ruby/3.2.0/gems/activesupport-7.1.3.2/lib/active_support/core_ext/time/zones.rb:65:in `use_zone'
/app/vendor/bundle/ruby/3.2.0/gems/activejob-7.1.3.2/lib/active_job/timezones.rb:9:in `block (2 levels) in <module:Timezones>'
/app/vendor/bundle/ruby/3.2.0/gems/activesupport-7.1.3.2/lib/active_support/callbacks.rb:130:in `instance_exec'
/app/vendor/bundle/ruby/3.2.0/gems/activesupport-7.1.3.2/lib/active_support/callbacks.rb:130:in `block in run_callbacks'
/app/vendor/bundle/ruby/3.2.0/gems/activesupport-7.1.3.2/lib/active_support/callbacks.rb:141:in `run_callbacks'
/app/vendor/bundle/ruby/3.2.0/gems/activejob-7.1.3.2/lib/active_job/execution.rb:68:in `_perform_job'
/app/vendor/bundle/ruby/3.2.0/gems/activejob-7.1.3.2/lib/active_job/instrumentation.rb:32:in `_perform_job'
/app/vendor/bundle/ruby/3.2.0/gems/activejob-7.1.3.2/lib/active_job/execution.rb:52:in `perform_now'
/app/vendor/bundle/ruby/3.2.0/gems/activejob-7.1.3.2/lib/active_job/instrumentation.rb:26:in `block in perform_now'
/app/vendor/bundle/ruby/3.2.0/gems/activerecord-7.1.3.2/lib/active_record/railties/job_runtime.rb:13:in `block in instrument'
/app/vendor/bundle/ruby/3.2.0/gems/activejob-7.1.3.2/lib/active_job/instrumentation.rb:40:in `block in instrument'
/app/vendor/bundle/ruby/3.2.0/gems/activesupport-7.1.3.2/lib/active_support/notifications.rb:206:in `block in instrument'
/app/vendor/bundle/ruby/3.2.0/gems/activesupport-7.1.3.2/lib/active_support/notifications/instrumenter.rb:58:in `instrument'
/app/vendor/bundle/ruby/3.2.0/gems/activesupport-7.1.3.2/lib/active_support/notifications.rb:206:in `instrument'
/app/vendor/bundle/ruby/3.2.0/gems/activejob-7.1.3.2/lib/active_job/instrumentation.rb:39:in `instrument'
/app/vendor/bundle/ruby/3.2.0/gems/activerecord-7.1.3.2/lib/active_record/railties/job_runtime.rb:11:in `instrument'
/app/vendor/bundle/ruby/3.2.0/gems/activejob-7.1.3.2/lib/active_job/instrumentation.rb:26:in `perform_now'
/app/vendor/bundle/ruby/3.2.0/gems/activejob-7.1.3.2/lib/active_job/logging.rb:18:in `block in perform_now'
/app/vendor/bundle/ruby/3.2.0/gems/activesupport-7.1.3.2/lib/active_support/tagged_logging.rb:135:in `block in tagged'
/app/vendor/bundle/ruby/3.2.0/gems/activesupport-7.1.3.2/lib/active_support/tagged_logging.rb:39:in `tagged'
/app/vendor/bundle/ruby/3.2.0/gems/activesupport-7.1.3.2/lib/active_support/tagged_logging.rb:135:in `tagged'
/app/vendor/bundle/ruby/3.2.0/gems/activesupport-7.1.3.2/lib/active_support/broadcast_logger.rb:240:in `method_missing'
/app/vendor/bundle/ruby/3.2.0/gems/activejob-7.1.3.2/lib/active_job/logging.rb:25:in `tag_logger'
/app/vendor/bundle/ruby/3.2.0/gems/activejob-7.1.3.2/lib/active_job/logging.rb:18:in `perform_now'
/app/vendor/bundle/ruby/3.2.0/gems/activejob-7.1.3.2/lib/active_job/execution.rb:30:in `block in execute'
/app/vendor/bundle/ruby/3.2.0/gems/activesupport-7.1.3.2/lib/active_support/callbacks.rb:121:in `block in run_callbacks'
/app/vendor/bundle/ruby/3.2.0/gems/activejob-7.1.3.2/lib/active_job/railtie.rb:67:in `block (4 levels) in <class:Railtie>'
/app/vendor/bundle/ruby/3.2.0/gems/activesupport-7.1.3.2/lib/active_support/reloader.rb:77:in `block in wrap'
/app/vendor/bundle/ruby/3.2.0/gems/activesupport-7.1.3.2/lib/active_support/execution_wrapper.rb:88:in `wrap'
/app/vendor/bundle/ruby/3.2.0/gems/activesupport-7.1.3.2/lib/active_support/reloader.rb:74:in `wrap'
/app/vendor/bundle/ruby/3.2.0/gems/activejob-7.1.3.2/lib/active_job/railtie.rb:66:in `block (3 levels) in <class:Railtie>'
/app/vendor/bundle/ruby/3.2.0/gems/activesupport-7.1.3.2/lib/active_support/callbacks.rb:130:in `instance_exec'
/app/vendor/bundle/ruby/3.2.0/gems/activesupport-7.1.3.2/lib/active_support/callbacks.rb:130:in `block in run_callbacks'
/app/vendor/bundle/ruby/3.2.0/gems/activesupport-7.1.3.2/lib/active_support/callbacks.rb:141:in `run_callbacks'
/app/vendor/bundle/ruby/3.2.0/gems/activejob-7.1.3.2/lib/active_job/execution.rb:28:in `execute'
/app/vendor/bundle/ruby/3.2.0/gems/good_job-3.26.2/app/models/good_job/execution.rb:412:in `block (3 levels) in perform'
/app/vendor/bundle/ruby/3.2.0/gems/activesupport-7.1.3.2/lib/active_support/notifications.rb:206:in `block in instrument'
/app/vendor/bundle/ruby/3.2.0/gems/activesupport-7.1.3.2/lib/active_support/notifications/instrumenter.rb:58:in `instrument'
/app/vendor/bundle/ruby/3.2.0/gems/activesupport-7.1.3.2/lib/active_support/notifications.rb:206:in `instrument'
/app/vendor/bundle/ruby/3.2.0/gems/good_job-3.26.2/app/models/good_job/execution.rb:411:in `block (2 levels) in perform'
/app/vendor/bundle/ruby/3.2.0/gems/good_job-3.26.2/lib/good_job/current_thread.rb:113:in `within'
/app/vendor/bundle/ruby/3.2.0/gems/good_job-3.26.2/app/models/good_job/execution.rb:374:in `block in perform'
/app/vendor/bundle/ruby/3.2.0/gems/activesupport-7.1.3.2/lib/active_support/callbacks.rb:121:in `block in run_callbacks'
/app/vendor/bundle/ruby/3.2.0/gems/good_job-3.26.2/app/models/good_job/batch.rb:78:in `within_thread'
/app/vendor/bundle/ruby/3.2.0/gems/good_job-3.26.2/app/models/good_job/execution.rb:573:in `reset_batch_values'
/app/vendor/bundle/ruby/3.2.0/gems/activesupport-7.1.3.2/lib/active_support/callbacks.rb:130:in `block in run_callbacks'
/app/vendor/bundle/ruby/3.2.0/gems/activesupport-7.1.3.2/lib/active_support/callbacks.rb:141:in `run_callbacks'
/app/vendor/bundle/ruby/3.2.0/gems/good_job-3.26.2/app/models/good_job/execution.rb:369:in `perform'
/app/vendor/bundle/ruby/3.2.0/gems/good_job-3.26.2/app/models/good_job/execution.rb:272:in `block in perform_with_advisory_lock'
/app/vendor/bundle/ruby/3.2.0/gems/good_job-3.26.2/app/models/concerns/good_job/advisory_lockable.rb:172:in `block in with_advisory_lock'
/app/vendor/bundle/ruby/3.2.0/gems/activerecord-7.1.3.2/lib/active_record/relation.rb:929:in `_scoping'
/app/vendor/bundle/ruby/3.2.0/gems/activerecord-7.1.3.2/lib/active_record/relation.rb:467:in `scoping'
/app/vendor/bundle/ruby/3.2.0/gems/activerecord-7.1.3.2/lib/active_record/scoping/default.rb:51:in `unscoped'
/app/vendor/bundle/ruby/3.2.0/gems/good_job-3.26.2/app/models/concerns/good_job/advisory_lockable.rb:172:in `with_advisory_lock'
/app/vendor/bundle/ruby/3.2.0/gems/activerecord-7.1.3.2/lib/active_record/relation/delegation.rb:79:in `block in with_advisory_lock'
/app/vendor/bundle/ruby/3.2.0/gems/activerecord-7.1.3.2/lib/active_record/relation.rb:929:in `_scoping'
/app/vendor/bundle/ruby/3.2.0/gems/activerecord-7.1.3.2/lib/active_record/relation.rb:467:in `scoping'
/app/vendor/bundle/ruby/3.2.0/gems/activerecord-7.1.3.2/lib/active_record/relation/delegation.rb:79:in `with_advisory_lock'
/app/vendor/bundle/ruby/3.2.0/gems/good_job-3.26.2/app/models/good_job/execution.rb:268:in `perform_with_advisory_lock'
/app/vendor/bundle/ruby/3.2.0/gems/activerecord-7.1.3.2/lib/active_record/relation/delegation.rb:79:in `block in perform_with_advisory_lock'
/app/vendor/bundle/ruby/3.2.0/gems/activerecord-7.1.3.2/lib/active_record/relation.rb:929:in `_scoping'
/app/vendor/bundle/ruby/3.2.0/gems/activerecord-7.1.3.2/lib/active_record/relation.rb:467:in `scoping'
/app/vendor/bundle/ruby/3.2.0/gems/activerecord-7.1.3.2/lib/active_record/relation/delegation.rb:79:in `perform_with_advisory_lock'
/app/vendor/bundle/ruby/3.2.0/gems/good_job-3.26.2/lib/good_job/job_performer.rb:33:in `next'
/app/vendor/bundle/ruby/3.2.0/gems/good_job-3.26.2/lib/good_job/scheduler.rb:276:in `block (2 levels) in create_task'
/app/vendor/bundle/ruby/3.2.0/gems/activesupport-7.1.3.2/lib/active_support/reloader.rb:77:in `block in wrap'
/app/vendor/bundle/ruby/3.2.0/gems/activesupport-7.1.3.2/lib/active_support/execution_wrapper.rb:92:in `wrap'
/app/vendor/bundle/ruby/3.2.0/gems/activesupport-7.1.3.2/lib/active_support/reloader.rb:74:in `wrap'
/app/vendor/bundle/ruby/3.2.0/gems/good_job-3.26.2/lib/good_job/scheduler.rb:275:in `block in create_task'
/app/vendor/bundle/ruby/3.2.0/gems/concurrent-ruby-1.2.3/lib/concurrent-ruby/concurrent/executor/safe_task_executor.rb:24:in `block in execute'
/app/vendor/bundle/ruby/3.2.0/gems/concurrent-ruby-1.2.3/lib/concurrent-ruby/concurrent/synchronization/mutex_lockable_object.rb:48:in `block in synchronize'
/app/vendor/bundle/ruby/3.2.0/gems/concurrent-ruby-1.2.3/lib/concurrent-ruby/concurrent/synchronization/mutex_lockable_object.rb:48:in `synchronize'
/app/vendor/bundle/ruby/3.2.0/gems/concurrent-ruby-1.2.3/lib/concurrent-ruby/concurrent/synchronization/mutex_lockable_object.rb:48:in `synchronize'
/app/vendor/bundle/ruby/3.2.0/gems/concurrent-ruby-1.2.3/lib/concurrent-ruby/concurrent/executor/safe_task_executor.rb:22:in `execute'
/app/vendor/bundle/ruby/3.2.0/gems/concurrent-ruby-1.2.3/lib/concurrent-ruby/concurrent/ivar.rb:170:in `safe_execute'
/app/vendor/bundle/ruby/3.2.0/gems/concurrent-ruby-1.2.3/lib/concurrent-ruby/concurrent/scheduled_task.rb:298:in `process_task'
/app/vendor/bundle/ruby/3.2.0/gems/concurrent-ruby-1.2.3/lib/concurrent-ruby/concurrent/executor/timer_set.rb:98:in `block in ns_post_task'
/app/vendor/bundle/ruby/3.2.0/gems/concurrent-ruby-1.2.3/lib/concurrent-ruby/concurrent/executor/ruby_thread_pool_executor.rb:359:in `run_task'
/app/vendor/bundle/ruby/3.2.0/gems/concurrent-ruby-1.2.3/lib/concurrent-ruby/concurrent/executor/ruby_thread_pool_executor.rb:350:in `block (3 levels) in create_worker'
/app/vendor/bundle/ruby/3.2.0/gems/concurrent-ruby-1.2.3/lib/concurrent-ruby/concurrent/executor/ruby_thread_pool_executor.rb:341:in `loop'
/app/vendor/bundle/ruby/3.2.0/gems/concurrent-ruby-1.2.3/lib/concurrent-ruby/concurrent/executor/ruby_thread_pool_executor.rb:341:in `block (2 levels) in create_worker'
/app/vendor/bundle/ruby/3.2.0/gems/concurrent-ruby-1.2.3/lib/concurrent-ruby/concurrent/executor/ruby_thread_pool_executor.rb:340:in `catch'
/app/vendor/bundle/ruby/3.2.0/gems/concurrent-ruby-1.2.3/lib/concurrent-ruby/concurrent/executor/ruby_thread_pool_executor.rb:340:in `block in create_worker'
```
### Environment information
**OpenProject installation type**
* [x] Hosted cloud edition
* [x] Packaged installation
* What distribution?
* [x] Docker All-in-one container
* [x] Docker-compose installation
* [ ] Other (please specify)
**OpenProject version**
_v14.0.0_
### Explanation
In `modules/reporting/app/models/cost_query/custom_field_mixin.rb` a class-level variable is cached containing a list of classes dynamically defined (see `generate_subclasses`) for each custom field filter.
It gets called:
* through `CostQuery::Filter::CustomFieldEntries` by `CostQuery::Filter.all` which also does some caching with a `@all` instance variable.
* and through `CostQuery::GroupBy::CustomFieldEntries` by `CostQuery::GroupBy.all` which also does some caching with a `@all` instance variable.
So what we do in the example above is create 1 custom field and export it. This initializes this variable (`@all`) with `[CustomField1]`. Then we create a new custom field and try filtering by it. This does not get added because the variable is already cached, so the new class `CustomField2` is not defined.
While this behaviour is forced in this example by limiting the background job threads to 1, this can happen both on-prem and in the cloud regardless of that. It may just take longer to manifest.
This is especially big of a problem in the cloud edition, however, where this code will run for different tenants with different custom fields. Which means this is a problem even when no new custom fields are introduced.
0: Run OpenProject 14 as follows.
```bash
docker run -e OPENPROJECT_HTTPS=false -e OPENPROJECT_GOOD__JOB__MAX__THREADS=1 -p 8080:80 --rm -it openproject/openproject:14
```
1. Create a [new work package custom field](http://localhost:8080/custom_fields) "CF1", enabled for all projects (type doesn't matter)
2. Enable it for the type 'Task' via the [form configuration](http://localhost:8080/types/1/edit/form_configuration)
3. Enable the "Time and costs" module in the Demo [project settings](http://localhost:8080/projects/demo-project/settings/modules)
4. Configure a [cost report](http://localhost:8080/projects/demo-project/cost_reports) filtering for CF1 (no data required)
5. Export it to XLS (button in top right corner)
6. Create a custom field "CF2", enabled for all projects (like in step 1)
7. Enable it for the type 'Task' (like in step 2)
8. Configure a cost report filtering for CF2 (no data required)
9. Export it to XLS (button in top right corner)
### What is the buggy behavior?
The export fails with an error.
<img class="op-uc-image op-uc-image_inline" src="/api/v3/attachments/100836/content">
### What is the expected behavior?
The export should work.
### **Logs**
```text
E, [2024-04-26T10:30:12.717661 #147] ERROR -- : [ActiveJob] [CostQuery::ExportJob] [ce2b9913-fad7-413f-9a8d-088f6f799df1] Error performing CostQuery::ExportJob (Job ID: ce2b9913-fad7-413f-9a8d-088f6f799df1) from GoodJob(default) in 24.52ms: NameError (uninitialized constant CostQuery::Filter::CustomField2):
/app/modules/reporting/app/models/cost_query.rb:142:in `add_chain'
/app/modules/reporting/app/models/cost_query.rb:170:in `filter'
/app/modules/reporting/app/workers/cost_query/export_job.rb:49:in `block (2 levels) in build_query'
/app/modules/reporting/app/workers/cost_query/export_job.rb:46:in `each'
/app/modules/reporting/app/workers/cost_query/export_job.rb:46:in `block in build_query'
<internal:kernel>:90:in `tap'
/app/modules/reporting/app/workers/cost_query/export_job.rb:45:in `build_query'
/app/modules/reporting/app/workers/cost_query/export_job.rb:21:in `prepare!'
/app/app/workers/exports/export_job.rb:15:in `block in perform'
/app/lib/open_project/locale_helper.rb:36:in `with_locale_for'
/app/app/models/user.rb:497:in `execute_as'
/app/app/workers/exports/export_job.rb:14:in `perform'
/app/vendor/bundle/ruby/3.2.0/gems/activejob-7.1.3.2/lib/active_job/execution.rb:69:in `block in _perform_job'
/app/vendor/bundle/ruby/3.2.0/gems/activesupport-7.1.3.2/lib/active_support/callbacks.rb:121:in `block in run_callbacks'
/app/app/workers/application_job.rb:106:in `block in prepare_job_context'
/app/app/workers/application_job.rb:79:in `with_clean_request_store'
/app/app/workers/application_job.rb:102:in `prepare_job_context'
/app/vendor/bundle/ruby/3.2.0/gems/activesupport-7.1.3.2/lib/active_support/callbacks.rb:130:in `block in run_callbacks'
/app/vendor/bundle/ruby/3.2.0/gems/i18n-1.14.4/lib/i18n.rb:322:in `with_locale'
/app/vendor/bundle/ruby/3.2.0/gems/activejob-7.1.3.2/lib/active_job/translation.rb:9:in `block (2 levels) in <module:Translation>'
/app/vendor/bundle/ruby/3.2.0/gems/activesupport-7.1.3.2/lib/active_support/callbacks.rb:130:in `instance_exec'
/app/vendor/bundle/ruby/3.2.0/gems/activesupport-7.1.3.2/lib/active_support/callbacks.rb:130:in `block in run_callbacks'
/app/vendor/bundle/ruby/3.2.0/gems/activesupport-7.1.3.2/lib/active_support/core_ext/time/zones.rb:65:in `use_zone'
/app/vendor/bundle/ruby/3.2.0/gems/activejob-7.1.3.2/lib/active_job/timezones.rb:9:in `block (2 levels) in <module:Timezones>'
/app/vendor/bundle/ruby/3.2.0/gems/activesupport-7.1.3.2/lib/active_support/callbacks.rb:130:in `instance_exec'
/app/vendor/bundle/ruby/3.2.0/gems/activesupport-7.1.3.2/lib/active_support/callbacks.rb:130:in `block in run_callbacks'
/app/vendor/bundle/ruby/3.2.0/gems/activesupport-7.1.3.2/lib/active_support/callbacks.rb:141:in `run_callbacks'
/app/vendor/bundle/ruby/3.2.0/gems/activejob-7.1.3.2/lib/active_job/execution.rb:68:in `_perform_job'
/app/vendor/bundle/ruby/3.2.0/gems/activejob-7.1.3.2/lib/active_job/instrumentation.rb:32:in `_perform_job'
/app/vendor/bundle/ruby/3.2.0/gems/activejob-7.1.3.2/lib/active_job/execution.rb:52:in `perform_now'
/app/vendor/bundle/ruby/3.2.0/gems/activejob-7.1.3.2/lib/active_job/instrumentation.rb:26:in `block in perform_now'
/app/vendor/bundle/ruby/3.2.0/gems/activerecord-7.1.3.2/lib/active_record/railties/job_runtime.rb:13:in `block in instrument'
/app/vendor/bundle/ruby/3.2.0/gems/activejob-7.1.3.2/lib/active_job/instrumentation.rb:40:in `block in instrument'
/app/vendor/bundle/ruby/3.2.0/gems/activesupport-7.1.3.2/lib/active_support/notifications.rb:206:in `block in instrument'
/app/vendor/bundle/ruby/3.2.0/gems/activesupport-7.1.3.2/lib/active_support/notifications/instrumenter.rb:58:in `instrument'
/app/vendor/bundle/ruby/3.2.0/gems/activesupport-7.1.3.2/lib/active_support/notifications.rb:206:in `instrument'
/app/vendor/bundle/ruby/3.2.0/gems/activejob-7.1.3.2/lib/active_job/instrumentation.rb:39:in `instrument'
/app/vendor/bundle/ruby/3.2.0/gems/activerecord-7.1.3.2/lib/active_record/railties/job_runtime.rb:11:in `instrument'
/app/vendor/bundle/ruby/3.2.0/gems/activejob-7.1.3.2/lib/active_job/instrumentation.rb:26:in `perform_now'
/app/vendor/bundle/ruby/3.2.0/gems/activejob-7.1.3.2/lib/active_job/logging.rb:18:in `block in perform_now'
/app/vendor/bundle/ruby/3.2.0/gems/activesupport-7.1.3.2/lib/active_support/tagged_logging.rb:135:in `block in tagged'
/app/vendor/bundle/ruby/3.2.0/gems/activesupport-7.1.3.2/lib/active_support/tagged_logging.rb:39:in `tagged'
/app/vendor/bundle/ruby/3.2.0/gems/activesupport-7.1.3.2/lib/active_support/tagged_logging.rb:135:in `tagged'
/app/vendor/bundle/ruby/3.2.0/gems/activesupport-7.1.3.2/lib/active_support/broadcast_logger.rb:240:in `method_missing'
/app/vendor/bundle/ruby/3.2.0/gems/activejob-7.1.3.2/lib/active_job/logging.rb:25:in `tag_logger'
/app/vendor/bundle/ruby/3.2.0/gems/activejob-7.1.3.2/lib/active_job/logging.rb:18:in `perform_now'
/app/vendor/bundle/ruby/3.2.0/gems/activejob-7.1.3.2/lib/active_job/execution.rb:30:in `block in execute'
/app/vendor/bundle/ruby/3.2.0/gems/activesupport-7.1.3.2/lib/active_support/callbacks.rb:121:in `block in run_callbacks'
/app/vendor/bundle/ruby/3.2.0/gems/activejob-7.1.3.2/lib/active_job/railtie.rb:67:in `block (4 levels) in <class:Railtie>'
/app/vendor/bundle/ruby/3.2.0/gems/activesupport-7.1.3.2/lib/active_support/reloader.rb:77:in `block in wrap'
/app/vendor/bundle/ruby/3.2.0/gems/activesupport-7.1.3.2/lib/active_support/execution_wrapper.rb:88:in `wrap'
/app/vendor/bundle/ruby/3.2.0/gems/activesupport-7.1.3.2/lib/active_support/reloader.rb:74:in `wrap'
/app/vendor/bundle/ruby/3.2.0/gems/activejob-7.1.3.2/lib/active_job/railtie.rb:66:in `block (3 levels) in <class:Railtie>'
/app/vendor/bundle/ruby/3.2.0/gems/activesupport-7.1.3.2/lib/active_support/callbacks.rb:130:in `instance_exec'
/app/vendor/bundle/ruby/3.2.0/gems/activesupport-7.1.3.2/lib/active_support/callbacks.rb:130:in `block in run_callbacks'
/app/vendor/bundle/ruby/3.2.0/gems/activesupport-7.1.3.2/lib/active_support/callbacks.rb:141:in `run_callbacks'
/app/vendor/bundle/ruby/3.2.0/gems/activejob-7.1.3.2/lib/active_job/execution.rb:28:in `execute'
/app/vendor/bundle/ruby/3.2.0/gems/good_job-3.26.2/app/models/good_job/execution.rb:412:in `block (3 levels) in perform'
/app/vendor/bundle/ruby/3.2.0/gems/activesupport-7.1.3.2/lib/active_support/notifications.rb:206:in `block in instrument'
/app/vendor/bundle/ruby/3.2.0/gems/activesupport-7.1.3.2/lib/active_support/notifications/instrumenter.rb:58:in `instrument'
/app/vendor/bundle/ruby/3.2.0/gems/activesupport-7.1.3.2/lib/active_support/notifications.rb:206:in `instrument'
/app/vendor/bundle/ruby/3.2.0/gems/good_job-3.26.2/app/models/good_job/execution.rb:411:in `block (2 levels) in perform'
/app/vendor/bundle/ruby/3.2.0/gems/good_job-3.26.2/lib/good_job/current_thread.rb:113:in `within'
/app/vendor/bundle/ruby/3.2.0/gems/good_job-3.26.2/app/models/good_job/execution.rb:374:in `block in perform'
/app/vendor/bundle/ruby/3.2.0/gems/activesupport-7.1.3.2/lib/active_support/callbacks.rb:121:in `block in run_callbacks'
/app/vendor/bundle/ruby/3.2.0/gems/good_job-3.26.2/app/models/good_job/batch.rb:78:in `within_thread'
/app/vendor/bundle/ruby/3.2.0/gems/good_job-3.26.2/app/models/good_job/execution.rb:573:in `reset_batch_values'
/app/vendor/bundle/ruby/3.2.0/gems/activesupport-7.1.3.2/lib/active_support/callbacks.rb:130:in `block in run_callbacks'
/app/vendor/bundle/ruby/3.2.0/gems/activesupport-7.1.3.2/lib/active_support/callbacks.rb:141:in `run_callbacks'
/app/vendor/bundle/ruby/3.2.0/gems/good_job-3.26.2/app/models/good_job/execution.rb:369:in `perform'
/app/vendor/bundle/ruby/3.2.0/gems/good_job-3.26.2/app/models/good_job/execution.rb:272:in `block in perform_with_advisory_lock'
/app/vendor/bundle/ruby/3.2.0/gems/good_job-3.26.2/app/models/concerns/good_job/advisory_lockable.rb:172:in `block in with_advisory_lock'
/app/vendor/bundle/ruby/3.2.0/gems/activerecord-7.1.3.2/lib/active_record/relation.rb:929:in `_scoping'
/app/vendor/bundle/ruby/3.2.0/gems/activerecord-7.1.3.2/lib/active_record/relation.rb:467:in `scoping'
/app/vendor/bundle/ruby/3.2.0/gems/activerecord-7.1.3.2/lib/active_record/scoping/default.rb:51:in `unscoped'
/app/vendor/bundle/ruby/3.2.0/gems/good_job-3.26.2/app/models/concerns/good_job/advisory_lockable.rb:172:in `with_advisory_lock'
/app/vendor/bundle/ruby/3.2.0/gems/activerecord-7.1.3.2/lib/active_record/relation/delegation.rb:79:in `block in with_advisory_lock'
/app/vendor/bundle/ruby/3.2.0/gems/activerecord-7.1.3.2/lib/active_record/relation.rb:929:in `_scoping'
/app/vendor/bundle/ruby/3.2.0/gems/activerecord-7.1.3.2/lib/active_record/relation.rb:467:in `scoping'
/app/vendor/bundle/ruby/3.2.0/gems/activerecord-7.1.3.2/lib/active_record/relation/delegation.rb:79:in `with_advisory_lock'
/app/vendor/bundle/ruby/3.2.0/gems/good_job-3.26.2/app/models/good_job/execution.rb:268:in `perform_with_advisory_lock'
/app/vendor/bundle/ruby/3.2.0/gems/activerecord-7.1.3.2/lib/active_record/relation/delegation.rb:79:in `block in perform_with_advisory_lock'
/app/vendor/bundle/ruby/3.2.0/gems/activerecord-7.1.3.2/lib/active_record/relation.rb:929:in `_scoping'
/app/vendor/bundle/ruby/3.2.0/gems/activerecord-7.1.3.2/lib/active_record/relation.rb:467:in `scoping'
/app/vendor/bundle/ruby/3.2.0/gems/activerecord-7.1.3.2/lib/active_record/relation/delegation.rb:79:in `perform_with_advisory_lock'
/app/vendor/bundle/ruby/3.2.0/gems/good_job-3.26.2/lib/good_job/job_performer.rb:33:in `next'
/app/vendor/bundle/ruby/3.2.0/gems/good_job-3.26.2/lib/good_job/scheduler.rb:276:in `block (2 levels) in create_task'
/app/vendor/bundle/ruby/3.2.0/gems/activesupport-7.1.3.2/lib/active_support/reloader.rb:77:in `block in wrap'
/app/vendor/bundle/ruby/3.2.0/gems/activesupport-7.1.3.2/lib/active_support/execution_wrapper.rb:92:in `wrap'
/app/vendor/bundle/ruby/3.2.0/gems/activesupport-7.1.3.2/lib/active_support/reloader.rb:74:in `wrap'
/app/vendor/bundle/ruby/3.2.0/gems/good_job-3.26.2/lib/good_job/scheduler.rb:275:in `block in create_task'
/app/vendor/bundle/ruby/3.2.0/gems/concurrent-ruby-1.2.3/lib/concurrent-ruby/concurrent/executor/safe_task_executor.rb:24:in `block in execute'
/app/vendor/bundle/ruby/3.2.0/gems/concurrent-ruby-1.2.3/lib/concurrent-ruby/concurrent/synchronization/mutex_lockable_object.rb:48:in `block in synchronize'
/app/vendor/bundle/ruby/3.2.0/gems/concurrent-ruby-1.2.3/lib/concurrent-ruby/concurrent/synchronization/mutex_lockable_object.rb:48:in `synchronize'
/app/vendor/bundle/ruby/3.2.0/gems/concurrent-ruby-1.2.3/lib/concurrent-ruby/concurrent/synchronization/mutex_lockable_object.rb:48:in `synchronize'
/app/vendor/bundle/ruby/3.2.0/gems/concurrent-ruby-1.2.3/lib/concurrent-ruby/concurrent/executor/safe_task_executor.rb:22:in `execute'
/app/vendor/bundle/ruby/3.2.0/gems/concurrent-ruby-1.2.3/lib/concurrent-ruby/concurrent/ivar.rb:170:in `safe_execute'
/app/vendor/bundle/ruby/3.2.0/gems/concurrent-ruby-1.2.3/lib/concurrent-ruby/concurrent/scheduled_task.rb:298:in `process_task'
/app/vendor/bundle/ruby/3.2.0/gems/concurrent-ruby-1.2.3/lib/concurrent-ruby/concurrent/executor/timer_set.rb:98:in `block in ns_post_task'
/app/vendor/bundle/ruby/3.2.0/gems/concurrent-ruby-1.2.3/lib/concurrent-ruby/concurrent/executor/ruby_thread_pool_executor.rb:359:in `run_task'
/app/vendor/bundle/ruby/3.2.0/gems/concurrent-ruby-1.2.3/lib/concurrent-ruby/concurrent/executor/ruby_thread_pool_executor.rb:350:in `block (3 levels) in create_worker'
/app/vendor/bundle/ruby/3.2.0/gems/concurrent-ruby-1.2.3/lib/concurrent-ruby/concurrent/executor/ruby_thread_pool_executor.rb:341:in `loop'
/app/vendor/bundle/ruby/3.2.0/gems/concurrent-ruby-1.2.3/lib/concurrent-ruby/concurrent/executor/ruby_thread_pool_executor.rb:341:in `block (2 levels) in create_worker'
/app/vendor/bundle/ruby/3.2.0/gems/concurrent-ruby-1.2.3/lib/concurrent-ruby/concurrent/executor/ruby_thread_pool_executor.rb:340:in `catch'
/app/vendor/bundle/ruby/3.2.0/gems/concurrent-ruby-1.2.3/lib/concurrent-ruby/concurrent/executor/ruby_thread_pool_executor.rb:340:in `block in create_worker'
```
### Environment information
**OpenProject installation type**
* [x] Hosted cloud edition
* [x] Packaged installation
* What distribution?
* [x] Docker All-in-one container
* [x] Docker-compose installation
* [ ] Other (please specify)
**OpenProject version**
_v14.0.0_
### Explanation
In `modules/reporting/app/models/cost_query/custom_field_mixin.rb` a class-level variable is cached containing a list of classes dynamically defined (see `generate_subclasses`) for each custom field filter.
It gets called:
* through `CostQuery::Filter::CustomFieldEntries` by `CostQuery::Filter.all` which also does some caching with a `@all` instance variable.
* and through `CostQuery::GroupBy::CustomFieldEntries` by `CostQuery::GroupBy.all` which also does some caching with a `@all` instance variable.
So what we do in the example above is create 1 custom field and export it. This initializes this variable (`@all`) with `[CustomField1]`. Then we create a new custom field and try filtering by it. This does not get added because the variable is already cached, so the new class `CustomField2` is not defined.
While this behaviour is forced in this example by limiting the background job threads to 1, this can happen both on-prem and in the cloud regardless of that. It may just take longer to manifest.
This is especially big of a problem in the cloud edition, however, where this code will run for different tenants with different custom fields. Which means this is a problem even when no new custom fields are introduced.