rails / solid_cache

A database-backed ActiveSupport::Cache::Store
MIT License
888 stars 62 forks source link

Fix Read-Only Errors for Cache Deletion in MultiDB Setup with `database_selector` #238

Open Dandush03 opened 1 week ago

Dandush03 commented 1 week ago

Description

When using Rails' MultiDB configuration with database_selector, attempts to delete entries from the SolidCache::Entry model may result in a read-only error. This issue arises because the database_selector configuration causes certain operations to route to the replica database, which is read-only.

The Error

The following error occurs when the application tries to execute a DELETE query in a replica marked as read-only by rails middleware

ActionView::Template::Error (Write query attempted while in readonly mode: DELETE FROM "solid_cache_entries" WHERE "solid_cache_entries"."key_hash" = 8850981528477303422)

This error disrupts operations that rely on cache clearing, such as session management or dynamic cache updates.

Reproduction Steps

  1. Add the following configuration to your Rails app:

    Rails.application.configure do
    config.active_record.database_selector = { delay: 2.seconds }
    config.active_record.database_resolver = ActiveRecord::Middleware::DatabaseSelector::Resolver
    config.active_record.database_resolver_context = ActiveRecord::Middleware::DatabaseSelector::Resolver::Session
    end
  2. Configure a primary and replica database in database.yml.

  3. Attempt to delete a cache entry using SolidCache::Entry in a context where the replica database connection is active. Observe the error described above.

_Hint: I suggest using devise user_sign_in? method in an index action since it would make it easier to reproduce_

Expected Behavior

Cache-related models like SolidCache::Entry should always use the writable primary database for operations that require modifications, regardless of the database_selector configuration.

My current solution:

# config/initializer/override_read_only_for_cache.rb
module OverrideReadOnlyForCache
  def readonly?
    false
  end
end

Rails.application.config.after_initialize do
  SolidCache::Entry.prepend(OverrideReadOnlyForCache)
end

This ensures that the model is always writable, bypassing the default read-only behavior in a MultiDB setup.

ausangshukla commented 18 hours ago

Same MultiDB setup. This fix is not working for me, I get the error

ActiveRecord::ReadOnlyError - Write query attempted while in readonly mode: INSERT INTO solid_cache_entries solid_cache (1.0.6)

Note there is no error with solid_cache (1.0.2), however the cache is not being populated, ie mysql> select * from solid_cache_entries; Empty set (0.00 sec)

Please can you help