Skip to content

Commit 0e4f2ad

Browse files
author
claudiamurialdo
committed
Add support for tenant ID in Redis-based database caching
1 parent b27d402 commit 0e4f2ad

File tree

8 files changed

+52
-9
lines changed

8 files changed

+52
-9
lines changed

dotnet/src/dotnetcore/GxClasses/Properties/AssemblyInfo.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,4 +21,5 @@
2121
[assembly: InternalsVisibleTo("GeneXus.OpenTelemetry.Diagnostics")]
2222
[assembly: InternalsVisibleTo("ConsoleApp2")]
2323
[assembly: InternalsVisibleTo("GxAI")]
24+
[assembly: InternalsVisibleTo("GxRedis")]
2425

dotnet/src/dotnetcore/GxClasses/Services/Session/GXSessionFactory.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
using System;
22
using GeneXus.Application;
3+
using GeneXus.Cache;
34
using GeneXus.Configuration;
45
using GeneXus.Data;
56
using GeneXus.Data.ADO;
@@ -65,7 +66,6 @@ public class GxRedisSession : ISessionService
6566
internal static string SESSION_INSTANCE = "SESSION_PROVIDER_INSTANCE_NAME";
6667
internal static string SESSION_PASSWORD = "SESSION_PROVIDER_PASSWORD";
6768
static string SESSION_TIMEOUT = "SESSION_PROVIDER_SESSION_TIMEOUT";
68-
const string SUBDOMAIN = "%SUBDOMAIN%";
6969
public GxRedisSession(GXService serviceProvider)
7070
{
7171
string password = serviceProvider.Properties.Get(SESSION_PASSWORD);
@@ -105,7 +105,7 @@ public GxRedisSession(string host, string password, string instanceName, int ses
105105
}
106106
internal bool IsMultitenant
107107
{
108-
get { return InstanceName == SUBDOMAIN; }
108+
get { return InstanceName == CacheFactory.SUBDOMAIN; }
109109
}
110110
public string ConnectionString { get; }
111111
public string InstanceName { get; }

dotnet/src/dotnetcore/GxNetCoreStartup/SessionHelper.cs

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ public TenantRedisCache(IHttpContextAccessor httpContextAccessor, IServiceProvid
2424

2525
private IDistributedCache GetTenantCache()
2626
{
27-
string tenantId = _httpContextAccessor.HttpContext?.Items[TenantMiddleware.TENANT_ID]?.ToString() ?? "default";
27+
string tenantId = _httpContextAccessor.HttpContext?.Items[AppContext.TENANT_ID]?.ToString() ?? "default";
2828

2929
return _redisCaches.GetOrAdd(tenantId, id =>
3030
{
@@ -51,7 +51,6 @@ private IDistributedCache GetTenantCache()
5151

5252
public class TenantMiddleware
5353
{
54-
internal const string TENANT_ID = "TenantId";
5554
private readonly RequestDelegate _next;
5655

5756
public TenantMiddleware(RequestDelegate next)
@@ -63,7 +62,7 @@ public async Task Invoke(HttpContext context)
6362
{
6463
string host = context.Request.Host.Host;
6564
string subdomain = host.Split('.').FirstOrDefault();
66-
context.Items[TENANT_ID] = subdomain;
65+
context.Items[AppContext.TENANT_ID] = subdomain;
6766

6867
await _next(context);
6968
}

dotnet/src/dotnetcore/Providers/Cache/GxRedis/GxRedis.csproj

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
<Project Sdk="Microsoft.NET.Sdk">
2-
<PropertyGroup>
2+
<PropertyGroup>
33
<TargetFrameworks>net8.0</TargetFrameworks>
44
<PackageTags>Redis</PackageTags>
55
<PackageId>GeneXus.Redis.Core</PackageId>
6+
<DefineConstants>NETCORE</DefineConstants>
67
</PropertyGroup>
78
<PropertyGroup>
89
<AppDesignerFolder>Properties</AppDesignerFolder>

dotnet/src/dotnetframework/GxClasses/Core/GXApplication.cs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -287,6 +287,12 @@ public interface IGxContext
287287
#if NETCORE
288288
internal static class AppContext
289289
{
290+
internal const string TENANT_ID = "TenantId";
291+
internal static string TenantId
292+
{
293+
get => Current?.Items[TENANT_ID]?.ToString() ?? "default";
294+
}
295+
290296
static IHttpContextAccessor _httpContextAccessor { get; set; }
291297
internal static HttpContext Current => _httpContextAccessor != null ? new GxHttpContextAccesor(_httpContextAccessor) : null;
292298
internal static void Configure(IHttpContextAccessor accessor)

dotnet/src/dotnetframework/GxClasses/Services/Caching/GxCache.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,7 @@ public override void Write(Utf8JsonWriter writer, object value, JsonSerializerOp
9595
public class CacheFactory
9696
{
9797
private static readonly IGXLogger log = GXLoggerFactory.GetLogger<CacheFactory>();
98-
98+
internal const string SUBDOMAIN = "%SUBDOMAIN%";
9999
public static string CACHE_SD = "SD";
100100
public static string CACHE_DB = "DB";
101101
public static string CACHE_FILES = "FL";

dotnet/src/dotnetframework/Providers/Cache/GxRedis/GxRedis.cs

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
using GeneXus.Services;
88
using GeneXus.Utils;
99
using StackExchange.Redis;
10+
using StackExchange.Redis.KeyspaceIsolation;
1011

1112
namespace GeneXus.Cache
1213
{
@@ -16,9 +17,14 @@ public sealed class Redis : ICacheService2
1617

1718
ConnectionMultiplexer _redisConnection;
1819
IDatabase _redisDatabase;
20+
#if NETCORE
21+
bool _multitenant;
22+
#endif
1923
ConfigurationOptions _redisConnectionOptions;
2024
private const int REDIS_DEFAULT_PORT = 6379;
2125
public int redisSessionTimeout;
26+
private string _instanceName;
27+
2228
public Redis(string connectionString)
2329
{
2430
_redisConnectionOptions = ConfigurationOptions.Parse(connectionString);
@@ -39,6 +45,8 @@ public Redis()
3945
string address, password;
4046
address = providerService.Properties.Get("CACHE_PROVIDER_ADDRESS");
4147
password = providerService.Properties.Get("CACHE_PROVIDER_PASSWORD");
48+
_instanceName = providerService.Properties.Get("CACHE_PROVIDER_INSTANCE_NAME");
49+
4250
if (!string.IsNullOrEmpty(password))
4351
{
4452
string ret = string.Empty;
@@ -74,7 +82,29 @@ IDatabase RedisDatabase
7482
if (_redisDatabase == null)
7583
{
7684
_redisConnection = ConnectionMultiplexer.Connect(_redisConnectionOptions);
77-
_redisDatabase = _redisConnection.GetDatabase();
85+
IDatabase db = _redisConnection.GetDatabase();
86+
87+
if (!string.IsNullOrEmpty(_instanceName))
88+
{
89+
#if NETCORE
90+
if (_instanceName == CacheFactory.SUBDOMAIN)
91+
{
92+
_multitenant = true;
93+
GXLogging.Debug(log, "Using Redis multitenant (key prefix):" + CacheFactory.SUBDOMAIN);
94+
_redisDatabase = db;
95+
}
96+
else
97+
#endif
98+
{
99+
string prefixKey = _instanceName.EndsWith(":") ? _instanceName : _instanceName + ":";
100+
_redisDatabase = db.WithKeyPrefix(_instanceName);
101+
GXLogging.Debug(log, "Using Redis instance name (key prefix): " + prefixKey);
102+
}
103+
}
104+
else
105+
{
106+
_redisDatabase = db;
107+
}
78108
}
79109
return _redisDatabase;
80110
}
@@ -221,6 +251,12 @@ private IEnumerable<RedisKey> Key(string cacheid, IEnumerable<string> key)
221251
}
222252
private string FormatKey(string cacheid, string key, Nullable<long> prefix)
223253
{
254+
#if NETCORE
255+
if (_multitenant)
256+
{
257+
return String.Format("{0}:{1}_{2}_{3}", Application.AppContext.TenantId, cacheid, prefix, GXUtil.GetHash(key));
258+
}
259+
#endif
224260
return String.Format("{0}_{1}_{2}", cacheid, prefix, GXUtil.GetHash(key));
225261
}
226262
private Nullable<long> KeyPrefix(string cacheid)

dotnet/src/dotnetframework/Providers/Cache/GxRedis/GxRedis.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
<PackageId>GeneXus.Redis</PackageId>
99
</PropertyGroup>
1010
<ItemGroup>
11-
<PackageReference Include="StackExchange.Redis" Version="2.2.4" />
11+
<PackageReference Include="StackExchange.Redis" Version="2.6.122" />
1212
<PackageReference Include="System.Text.Json" Version="8.0.5" />
1313
</ItemGroup>
1414
<ItemGroup>

0 commit comments

Comments
 (0)