@@ -13,57 +13,70 @@ public class CustomRedisSessionStore : IDistributedCache
1313 private readonly TimeSpan _idleTimeout ;
1414 private readonly TimeSpan _refreshThreshold ;
1515 private readonly string _instanceName ;
16-
16+ // Refresh only if remaining TTL is below 35% of the original idle timeout.
17+ private const double IdleTimeoutRefreshRatio = 0.3 ; // 30%
1718 public CustomRedisSessionStore ( string connectionString , TimeSpan idleTimeout , string instanceName )
1819 {
1920 var mux = ConnectionMultiplexer . Connect ( connectionString ) ;
2021 _db = mux . GetDatabase ( ) ;
2122 _idleTimeout = idleTimeout ;
22- _refreshThreshold = TimeSpan . FromTicks ( ( long ) ( idleTimeout . Ticks * 0.2 ) ) ;
23+ _refreshThreshold = TimeSpan . FromTicks ( ( long ) ( idleTimeout . Ticks * IdleTimeoutRefreshRatio ) ) ;
2324 _instanceName = instanceName ?? string . Empty ;
2425 }
2526
2627 private string FormatKey ( string key ) => string . IsNullOrEmpty ( _instanceName ) ? key : $ "{ _instanceName } :{ key } ";
2728
28- public byte [ ] Get ( string key ) => _db . StringGet ( FormatKey ( key ) ) ;
29+ public byte [ ] Get ( string key )
30+ {
31+ string redisKey = FormatKey ( key ) ;
32+ var value = _db . StringGet ( redisKey ) ;
33+
34+ if ( value . HasValue )
35+ RefreshKeyIfNeeded ( redisKey ) ;
2936
37+ return value ;
38+ }
3039 public async Task < byte [ ] > GetAsync ( string key , CancellationToken token = default )
3140 {
3241 string redisKey = FormatKey ( key ) ;
3342 var value = await _db . StringGetAsync ( redisKey ) ;
3443
35- await RefreshKeyIfNeededAsync ( redisKey ) ;
44+ if ( value . HasValue )
45+ {
46+ await RefreshKeyIfNeededAsync ( redisKey ) ;
47+ }
3648
3749 return value ;
3850 }
39-
4051 public void Refresh ( string key )
4152 {
4253 string redisKey = FormatKey ( key ) ;
43-
44- var ttl = _db . KeyTimeToLive ( redisKey ) ;
45-
46- if ( ShouldRefreshKey ( ttl ) )
47- {
48- _db . KeyExpire ( redisKey , _idleTimeout ) ;
49- }
54+ RefreshKeyIfNeeded ( redisKey ) ;
5055 }
5156 private bool ShouldRefreshKey ( TimeSpan ? ttl )
5257 {
53- return ttl . HasValue && ttl . Value < _refreshThreshold ;
58+ return ttl . HasValue && ttl . Value < _refreshThreshold && ttl . Value > TimeSpan . Zero ; ;
5459 }
5560 public async Task RefreshAsync ( string key , CancellationToken token = default )
5661 {
5762 string redisKey = FormatKey ( key ) ;
5863 await RefreshKeyIfNeededAsync ( redisKey ) ;
5964 }
65+ private void RefreshKeyIfNeeded ( string redisKey )
66+ {
67+ var ttl = _db . KeyTimeToLive ( redisKey ) ;
68+ if ( ShouldRefreshKey ( ttl ) )
69+ {
70+ _db . KeyExpire ( redisKey , _idleTimeout ) ;
71+ }
72+ }
6073 private async Task RefreshKeyIfNeededAsync ( string redisKey )
6174 {
6275 var ttl = await _db . KeyTimeToLiveAsync ( redisKey ) ;
6376
6477 if ( ShouldRefreshKey ( ttl ) )
6578 {
66- _ = _db . KeyExpireAsync ( redisKey , _idleTimeout ) ;
79+ await _db . KeyExpireAsync ( redisKey , _idleTimeout ) ;
6780 }
6881 }
6982 public void Remove ( string key ) => _db . KeyDelete ( FormatKey ( key ) ) ;
@@ -73,13 +86,13 @@ public Task RemoveAsync(string key, CancellationToken token = default)
7386
7487 public void Set ( string key , byte [ ] value , DistributedCacheEntryOptions options )
7588 {
76- _db . StringSet ( FormatKey ( key ) , value , _idleTimeout ) ;
89+ TimeSpan expiry = options ? . AbsoluteExpirationRelativeToNow ?? _idleTimeout ;
90+ _db . StringSet ( FormatKey ( key ) , value , expiry ) ;
7791 }
78-
7992 public Task SetAsync ( string key , byte [ ] value , DistributedCacheEntryOptions options , CancellationToken token = default )
8093 {
81- return _db . StringSetAsync ( FormatKey ( key ) , value , _idleTimeout ) ;
94+ TimeSpan expiry = options ? . AbsoluteExpirationRelativeToNow ?? _idleTimeout ;
95+ return _db . StringSetAsync ( FormatKey ( key ) , value , expiry ) ;
8296 }
8397 }
84-
8598}
0 commit comments