1- using Microsoft . AspNetCore . Http ;
2- using Microsoft . Extensions . Caching . Distributed ;
3- using Microsoft . Extensions . Caching . StackExchangeRedis ;
1+ using System ;
42using System . Collections . Concurrent ;
5- using System . Threading . Tasks ;
3+ using System . Linq ;
64using System . Threading ;
7- using System ;
5+ using System . Threading . Tasks ;
86using GeneXus . Services ;
9- using System . Linq ;
7+ using Microsoft . AspNetCore . Http ;
8+ using Microsoft . Extensions . Caching . Distributed ;
9+ using StackExchange . Redis ;
1010
1111namespace GeneXus . Application
1212{
1313 public class TenantRedisCache : IDistributedCache
1414 {
1515 private readonly IHttpContextAccessor _httpContextAccessor ;
16- private readonly IServiceProvider _serviceProvider ;
17- private readonly ConcurrentDictionary < string , RedisCache > _redisCaches = new ( ) ;
16+ private readonly ConcurrentDictionary < string , IDistributedCache > _redisCaches = new ( ) ;
1817
19- public TenantRedisCache ( IHttpContextAccessor httpContextAccessor , IServiceProvider serviceProvider )
18+ public TenantRedisCache ( IHttpContextAccessor httpContextAccessor )
2019 {
2120 _httpContextAccessor = httpContextAccessor ;
22- _serviceProvider = serviceProvider ;
2321 }
2422
2523 private IDistributedCache GetTenantCache ( )
@@ -29,12 +27,7 @@ private IDistributedCache GetTenantCache()
2927 return _redisCaches . GetOrAdd ( tenantId , id =>
3028 {
3129 ISessionService sessionService = GXSessionServiceFactory . GetProvider ( ) ;
32- var options = new RedisCacheOptions
33- {
34- Configuration = sessionService . ConnectionString ,
35- InstanceName = $ "{ id } :"
36- } ;
37- return new RedisCache ( options ) ;
30+ return new CustomRedisSessionStore ( sessionService . ConnectionString , TimeSpan . FromMinutes ( sessionService . SessionTimeout ) , id ) ;
3831 } ) ;
3932 }
4033
@@ -48,6 +41,80 @@ private IDistributedCache GetTenantCache()
4841 public Task SetAsync ( string key , byte [ ] value , DistributedCacheEntryOptions options , CancellationToken token = default ) => GetTenantCache ( ) . SetAsync ( key , value , options , token ) ;
4942 }
5043
44+ public class CustomRedisSessionStore : IDistributedCache
45+ {
46+ private readonly IDatabase _db ;
47+ private readonly TimeSpan _idleTimeout ;
48+ private readonly TimeSpan _refreshThreshold ;
49+ private readonly string _instanceName ;
50+
51+ public CustomRedisSessionStore ( string connectionString , TimeSpan idleTimeout , string instanceName )
52+ {
53+ var mux = ConnectionMultiplexer . Connect ( connectionString ) ;
54+ _db = mux . GetDatabase ( ) ;
55+ _idleTimeout = idleTimeout ;
56+ _refreshThreshold = TimeSpan . FromTicks ( ( long ) ( idleTimeout . Ticks * 0.2 ) ) ;
57+ _instanceName = instanceName ?? string . Empty ;
58+ }
59+
60+ private string FormatKey ( string key ) => string . IsNullOrEmpty ( _instanceName ) ? key : $ "{ _instanceName } :{ key } ";
61+
62+ public byte [ ] Get ( string key ) => _db . StringGet ( FormatKey ( key ) ) ;
63+
64+ public async Task < byte [ ] > GetAsync ( string key , CancellationToken token = default )
65+ {
66+ string redisKey = FormatKey ( key ) ;
67+ var value = await _db . StringGetAsync ( redisKey ) ;
68+
69+ await RefreshKeyIfNeededAsync ( redisKey ) ;
70+
71+ return value ;
72+ }
73+
74+ public void Refresh ( string key )
75+ {
76+ string redisKey = FormatKey ( key ) ;
77+
78+ var ttl = _db . KeyTimeToLive ( redisKey ) ;
79+
80+ if ( ShouldRefreshKey ( ttl ) )
81+ {
82+ _db . KeyExpire ( redisKey , _idleTimeout ) ;
83+ }
84+ }
85+ private bool ShouldRefreshKey ( TimeSpan ? ttl )
86+ {
87+ return ttl . HasValue && ttl . Value < _refreshThreshold ;
88+ }
89+ public async Task RefreshAsync ( string key , CancellationToken token = default )
90+ {
91+ string redisKey = FormatKey ( key ) ;
92+ await RefreshKeyIfNeededAsync ( redisKey ) ;
93+ }
94+ private async Task RefreshKeyIfNeededAsync ( string redisKey )
95+ {
96+ var ttl = await _db . KeyTimeToLiveAsync ( redisKey ) ;
97+
98+ if ( ShouldRefreshKey ( ttl ) )
99+ {
100+ _ = _db . KeyExpireAsync ( redisKey , _idleTimeout ) ;
101+ }
102+ }
103+ public void Remove ( string key ) => _db . KeyDelete ( FormatKey ( key ) ) ;
104+
105+ public Task RemoveAsync ( string key , CancellationToken token = default )
106+ => _db . KeyDeleteAsync ( FormatKey ( key ) ) ;
107+
108+ public void Set ( string key , byte [ ] value , DistributedCacheEntryOptions options )
109+ {
110+ _db . StringSet ( FormatKey ( key ) , value , _idleTimeout ) ;
111+ }
112+
113+ public Task SetAsync ( string key , byte [ ] value , DistributedCacheEntryOptions options , CancellationToken token = default )
114+ {
115+ return _db . StringSetAsync ( FormatKey ( key ) , value , _idleTimeout ) ;
116+ }
117+ }
51118
52119 public class TenantMiddleware
53120 {
0 commit comments