Skip to content

Commit 1557775

Browse files
author
claudiamurialdo
committed
Cherry pick branch 'genexuslabs:custom-redis-session-store' into beta
(cherry picked from commit 95c3ff7) # Conflicts: # dotnet/src/dotnetcore/GxNetCoreStartup/SessionHelper.cs # dotnet/src/dotnetcore/GxNetCoreStartup/Startup.cs
1 parent a1dba76 commit 1557775

File tree

2 files changed

+97
-6
lines changed

2 files changed

+97
-6
lines changed
Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
using System;
2+
using System.Collections.Concurrent;
3+
using System.Linq;
4+
using System.Threading;
5+
using System.Threading.Tasks;
6+
using GeneXus.Services;
7+
using Microsoft.AspNetCore.Http;
8+
using Microsoft.Extensions.Caching.Distributed;
9+
using StackExchange.Redis;
10+
11+
namespace GeneXus.Application
12+
{
13+
14+
public class CustomRedisSessionStore : IDistributedCache
15+
{
16+
private readonly IDatabase _db;
17+
private readonly TimeSpan _idleTimeout;
18+
private readonly TimeSpan _refreshThreshold;
19+
private readonly string _instanceName;
20+
21+
public CustomRedisSessionStore(string connectionString, TimeSpan idleTimeout, string instanceName)
22+
{
23+
var mux = ConnectionMultiplexer.Connect(connectionString);
24+
_db = mux.GetDatabase();
25+
_idleTimeout = idleTimeout;
26+
_refreshThreshold = TimeSpan.FromTicks((long)(idleTimeout.Ticks * 0.2));
27+
_instanceName = instanceName ?? string.Empty;
28+
}
29+
30+
private string FormatKey(string key) => string.IsNullOrEmpty(_instanceName) ? key : $"{_instanceName}:{key}";
31+
32+
public byte[] Get(string key) => _db.StringGet(FormatKey(key));
33+
34+
public async Task<byte[]> GetAsync(string key, CancellationToken token = default)
35+
{
36+
string redisKey = FormatKey(key);
37+
var value = await _db.StringGetAsync(redisKey);
38+
39+
await RefreshKeyIfNeededAsync(redisKey);
40+
41+
return value;
42+
}
43+
44+
public void Refresh(string key)
45+
{
46+
string redisKey = FormatKey(key);
47+
48+
var ttl = _db.KeyTimeToLive(redisKey);
49+
50+
if (ShouldRefreshKey(ttl))
51+
{
52+
_db.KeyExpire(redisKey, _idleTimeout);
53+
}
54+
}
55+
private bool ShouldRefreshKey(TimeSpan? ttl)
56+
{
57+
return ttl.HasValue && ttl.Value < _refreshThreshold;
58+
}
59+
public async Task RefreshAsync(string key, CancellationToken token = default)
60+
{
61+
string redisKey = FormatKey(key);
62+
await RefreshKeyIfNeededAsync(redisKey);
63+
}
64+
private async Task RefreshKeyIfNeededAsync(string redisKey)
65+
{
66+
var ttl = await _db.KeyTimeToLiveAsync(redisKey);
67+
68+
if (ShouldRefreshKey(ttl))
69+
{
70+
_ = _db.KeyExpireAsync(redisKey, _idleTimeout);
71+
}
72+
}
73+
public void Remove(string key) => _db.KeyDelete(FormatKey(key));
74+
75+
public Task RemoveAsync(string key, CancellationToken token = default)
76+
=> _db.KeyDeleteAsync(FormatKey(key));
77+
78+
public void Set(string key, byte[] value, DistributedCacheEntryOptions options)
79+
{
80+
_db.StringSet(FormatKey(key), value, _idleTimeout);
81+
}
82+
83+
public Task SetAsync(string key, byte[] value, DistributedCacheEntryOptions options, CancellationToken token = default)
84+
{
85+
return _db.StringSetAsync(FormatKey(key), value, _idleTimeout);
86+
}
87+
}
88+
89+
}

dotnet/src/dotnetcore/GxNetCoreStartup/Startup.cs

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
using GeneXus.Services.OpenTelemetry;
1313
using GeneXus.Utils;
1414
using GxClasses.Web.Middleware;
15+
1516
using Microsoft.AspNetCore;
1617
using Microsoft.AspNetCore.Antiforgery;
1718
using Microsoft.AspNetCore.Builder;
@@ -36,6 +37,7 @@
3637
using Microsoft.Extensions.Diagnostics.HealthChecks;
3738
using Microsoft.Extensions.FileProviders;
3839
using Microsoft.Extensions.Logging;
40+
3941
using StackExchange.Redis;
4042

4143
namespace GeneXus.Application
@@ -64,6 +66,7 @@ public static void Main(string[] args)
6466
LocatePhysicalLocalPath();
6567

6668
}
69+
6770
if (port == DEFAULT_PORT)
6871
{
6972
BuildWebHost(null).Run();
@@ -310,14 +313,13 @@ private void DefineCorsPolicy(IServiceCollection services)
310313

311314
private void ConfigureSessionService(IServiceCollection services, ISessionService sessionService)
312315
{
316+
313317
if (sessionService is GxRedisSession)
314318
{
315-
services.AddStackExchangeRedisCache(options =>
316-
{
317-
GXLogging.Info(log, $"Using Redis for Distributed session, ConnectionString:{sessionService.ConnectionString}, InstanceName: {sessionService.InstanceName}");
318-
options.Configuration = sessionService.ConnectionString;
319-
options.InstanceName = sessionService.InstanceName;
320-
});
319+
GXLogging.Info(log, $"Using Redis for Distributed session, ConnectionString:{sessionService.ConnectionString}, InstanceName: {sessionService.InstanceName}");
320+
321+
services.AddSingleton<IDistributedCache>(sp =>
322+
new CustomRedisSessionStore(sessionService.ConnectionString, TimeSpan.FromMinutes(Preferences.SessionTimeout), sessionService.InstanceName));
321323
services.AddDataProtection().PersistKeysToStackExchangeRedis(ConnectionMultiplexer.Connect(sessionService.ConnectionString), DATA_PROTECTION_KEYS).SetApplicationName(sessionService.InstanceName);
322324
}
323325
else if (sessionService is GxDatabaseSession)

0 commit comments

Comments
 (0)