From 0e43f89af9dea2e741bbda9b26ab0a087a392fe2 Mon Sep 17 00:00:00 2001 From: Tadhg Boyle Date: Wed, 14 Oct 2026 14:08:09 -0700 Subject: [PATCH] Fix false success for failed unless_exist writes Use the result of lock_and_write instead of marking a write successful before persistence. Cover handled write failures and avoid populating the local cache with an unpersisted value. --- lib/solid_cache/store/api.rb | 8 ++------ test/unit/solid_cache_test.rb | 17 +++++++++++++++++ 2 files changed, 19 insertions(+), 6 deletions(-) diff --git a/lib/solid_cache/store/api.rb b/lib/solid_cache/store/api.rb index b7859ab0..ecd6c620 100644 --- a/lib/solid_cache/store/api.rb +++ b/lib/solid_cache/store/api.rb @@ -53,12 +53,8 @@ def write_entry(key, entry, raw: false, unless_exist: false, **options) payload = serialize_entry(entry, raw: raw, **options) if unless_exist - written = false - entry_lock_and_write(key) do |value| - if value.nil? || deserialize_entry(value, **options).expired? - written = true - payload - end + written = !!entry_lock_and_write(key) do |value| + payload if value.nil? || deserialize_entry(value, **options).expired? end else written = entry_write(key, payload) diff --git a/test/unit/solid_cache_test.rb b/test/unit/solid_cache_test.rb index 2376345c..adec85ba 100644 --- a/test/unit/solid_cache_test.rb +++ b/test/unit/solid_cache_test.rb @@ -81,6 +81,23 @@ class SolidCacheFailsafeTest < ActiveSupport::TestCase @peek = lookup_store(expires_in: 60) end + test "write with unless_exist returns false when persistence fails" do + SolidCache::Entry.expects(:write).raises(ActiveRecord::Deadlocked) + + assert_equal false, @cache.write("foo", "bar", unless_exist: true) + assert_nil @cache.read("foo") + end + + test "failed write with unless_exist does not populate the local cache" do + SolidCache::Entry.expects(:write).raises(ActiveRecord::StatementTimeout) + + @cache.with_local_cache do + @cache.write("foo", "bar", unless_exist: true) + + assert_nil @cache.read("foo") + end + end + def emulating_unavailability wait_for_background_tasks(@cache) emulating_timeouts do