@@ -207,5 +207,47 @@ public async Task ObservedLockMultiple()
207207
208208 cacheStackMock . Verify ( c => c . GetAsync < int > ( "TestKey" ) , Times . Exactly ( 4 ) , "Two checks to the cache stack are expected" ) ;
209209 }
210+
211+ [ TestMethod ]
212+ public async Task FailsafeOnSubscriberFailure ( )
213+ {
214+ RedisHelper . ResetState ( ) ;
215+
216+ var connection = RedisHelper . GetConnection ( ) ;
217+
218+ var cacheStackMock = new Mock < ICacheStack > ( ) ;
219+ var extension = new RedisLockExtension ( connection , new RedisLockOptions ( lockTimeout : TimeSpan . FromSeconds ( 1 ) ) ) ;
220+ extension . Register ( cacheStackMock . Object ) ;
221+
222+ var cacheEntry = new CacheEntry < int > ( 13 , TimeSpan . FromDays ( 1 ) ) ;
223+
224+ //Establish lock
225+ await connection . GetDatabase ( ) . StringSetAsync ( "Lock:TestKey" , RedisValue . EmptyString ) ;
226+
227+ var refreshTask = extension . WithRefreshAsync ( "TestKey" ,
228+ ( ) =>
229+ {
230+ return new ValueTask < CacheEntry < int > > ( cacheEntry ) ;
231+ } ,
232+ new CacheSettings ( TimeSpan . FromDays ( 1 ) )
233+ ) . AsTask ( ) ;
234+
235+ //Delay to allow for Redis check and self-entry into lock
236+ await Task . Delay ( TimeSpan . FromSeconds ( 1 ) ) ;
237+
238+ Assert . IsTrue ( extension . LockedOnKeyRefresh . ContainsKey ( "TestKey" ) , "Lock was not established" ) ;
239+
240+ //We don't publish to end lock
241+
242+ //However, we still expect to succeed
243+ var succeedingTask = await Task . WhenAny ( refreshTask , Task . Delay ( TimeSpan . FromSeconds ( 10 ) ) ) ;
244+ if ( ! succeedingTask . Equals ( refreshTask ) )
245+ {
246+ RedisHelper . DebugInfo ( connection ) ;
247+ Assert . Fail ( "Refresh has timed out - something has gone very wrong" ) ;
248+ }
249+
250+ cacheStackMock . Verify ( c => c . GetAsync < int > ( "TestKey" ) , Times . Exactly ( 1 ) , "One checks to the cache stack are expected as it will fail to resolve lock" ) ;
251+ }
210252 }
211253}
0 commit comments