ransack
ransack copied to clipboard
Error in schema validation with multi-tenant DB
Hello!
I got an intermittent error when querying a multitenant database using ransack:
RuntimeError:
No table named messages exists.
# ./.gems/ruby/2.7.0/gems/ransack-2.4.2/lib/ransack/adapters/active_record/context.rb:19:in `type_for'
# ./.gems/ruby/2.7.0/gems/ransack-2.4.2/lib/ransack/nodes/attribute.rb:35:in `type'
# ./.gems/ruby/2.7.0/gems/ransack-2.4.2/lib/ransack/nodes/condition.rb:266:in `default_type'
# ./.gems/ruby/2.7.0/gems/ransack-2.4.2/lib/ransack/nodes/condition.rb:25:in `extract'
# ./.gems/ruby/2.7.0/gems/ransack-2.4.2/lib/ransack/nodes/grouping.rb:175:in `write_attribute'
# ./.gems/ruby/2.7.0/gems/ransack-2.4.2/lib/ransack/nodes/grouping.rb:115:in `method_missing'
# ./.gems/ruby/2.7.0/gems/ransack-2.4.2/lib/ransack/search.rb:46:in `block in build'
# ./.gems/ruby/2.7.0/gems/ransack-2.4.2/lib/ransack/search.rb:42:in `each'
# ./.gems/ruby/2.7.0/gems/ransack-2.4.2/lib/ransack/search.rb:42:in `build'
# ./.gems/ruby/2.7.0/gems/ransack-2.4.2/lib/ransack/search.rb:34:in `initialize'
# ./.gems/ruby/2.7.0/gems/ransack-2.4.2/lib/ransack/adapters/active_record/base.rb:18:in `new'
# ./.gems/ruby/2.7.0/gems/ransack-2.4.2/lib/ransack/adapters/active_record/base.rb:18:in `ransack'
# ./app/services/message_reports/select.rb:10:in `paginated_data'
I tried to search the cause of this error and I fear that it should be related to how the schema cache is managed by a multi-tenant database - it seems to only be updated once the new schema is accessed by that connection. I tried to debug this issue placing a binding.pry
before this line and tried the following:
[7] pry(#<Ransack::Adapters::ActiveRecord::Context>)> table
=> "messages"
[8] pry(#<Ransack::Adapters::ActiveRecord::Context>)> schema_cache.send(:data_source_exists?, table)
=> false
[9] pry(#<Ransack::Adapters::ActiveRecord::Context>)> Message.count
=> 2
[10] pry(#<Ransack::Adapters::ActiveRecord::Context>)> schema_cache.clear!
=> nil
[11] pry(#<Ransack::Adapters::ActiveRecord::Context>)> schema_cache.send(:data_source_exists?, table)
=> true
As you can see, the table actually exists, and can be accessed normally when doing Message.count
, but the schema_cache
is outdated. I'm not sure if this kind of validation is being used in other parts of the gem, but I'm wondering if ransack
could just let the database adapter itself raise an error (such as PG::UndefinedTable
, for example) instead of validating the database schema before the query is made. WDYT?
As a workaround for now, we added this line before the Message.ransack
call, and for now it works fine:
Message.connection.schema_cache.clear!
Here's my stack versions:
- ruby: 2.7.2
- rails: 6.0.3
- ransack: 2.4.2