How to reproduce:
Configure a column in the list view with a formatted_value definition that takes a little longer to process.
config.model Rollout do
list do
...
field :leads do
formatted_value do
sleep 0.5
bindings[:view].render partial: 'rails_admin/main/leads', locals: { object: bindings[:object] }
end
end
...
end
end
The longer it takes the more likely for the race-condition to show up. Run the admin panel in a multi-threaded environment, e.g. using puma in production. Go to the respective list in the admin panel and stress it by reloading the page many times. The best thing is if you create two scopes and always click between the two.
At some point you will get:
NoMethodError at /rollout
undefined method `leads' for #<Rollout:0x007f4a878dfb20>
If you want to use a RailsAdmin virtual field(= a field without corresponding instance method),
you should declare 'formatted_value' in the field definition.
field :leads do
formatted_value{ bindings[:object].call_some_method }
end
Cause:
The recursion tracking in the Configurable module, here.
Doing instance_variable_set("@#{option_name}_recurring", true) is not thread-safe.
- In this case thread A sets
@formatted_value_recurring to true and then evaluates the proc.
- Meanwhile thread B enters the stage, sees that
@formatted_value_recurring is true and thus attempts to call the instance method of the model, which is not there.
Solution:
Scope the recursion tracking variable to the request. This proof-of-concept uses RequestStore.
if RequestStore.store["rails_admin/#{option_name}_recurring"]
value = instance_eval(&default)
else
RequestStore.store["rails_admin/#{option_name}_recurring"] = true
value = instance_eval(&value)
RequestStore.store["rails_admin/#{option_name}_recurring"] = false
end
The key is not optimal but this proof-of-concept solves the bug on this page.
How to reproduce:
Configure a column in the list view with a
formatted_valuedefinition that takes a little longer to process.The longer it takes the more likely for the race-condition to show up. Run the admin panel in a multi-threaded environment, e.g. using
pumainproduction. Go to the respective list in the admin panel and stress it by reloading the page many times. The best thing is if you create two scopes and always click between the two.At some point you will get:
Cause:
The recursion tracking in the
Configurablemodule, here.Doing
instance_variable_set("@#{option_name}_recurring", true)is not thread-safe.@formatted_value_recurringtotrueand then evaluates the proc.@formatted_value_recurringistrueand thus attempts to call the instance method of the model, which is not there.Solution:
Scope the recursion tracking variable to the request. This proof-of-concept uses RequestStore.
The key is not optimal but this proof-of-concept solves the bug on this page.