Skip to content

Configurable recursion tracking causes errors in multi-threaded environment #2897

@andyentity

Description

@andyentity

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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions