From f37fee3235f438c38ed9774dcdcc77247a7005cb Mon Sep 17 00:00:00 2001 From: rootdarkarchon Date: Sun, 8 Jan 2023 19:27:39 +0100 Subject: [PATCH] attempt redis connection pooling --- .../Controllers/JwtController.cs | 10 ++--- .../Hubs/MareHub.Admin.cs | 11 +++--- .../Hubs/MareHub.Functions.cs | 19 +++++++--- .../MareSynchronosServer/Hubs/MareHub.User.cs | 6 ++- .../MareSynchronosServer/Hubs/MareHub.cs | 8 ++-- .../MareSynchronosServer.csproj | 4 ++ .../UserRequirementHandler.cs | 9 +++-- .../Services/SystemInfoService.cs | 11 +++--- .../MareSynchronosServer/Startup.cs | 37 +++++++++++++++++-- 9 files changed, 79 insertions(+), 36 deletions(-) diff --git a/MareSynchronosServer/MareSynchronosServer/Controllers/JwtController.cs b/MareSynchronosServer/MareSynchronosServer/Controllers/JwtController.cs index e303e76..ac5d527 100644 --- a/MareSynchronosServer/MareSynchronosServer/Controllers/JwtController.cs +++ b/MareSynchronosServer/MareSynchronosServer/Controllers/JwtController.cs @@ -8,7 +8,7 @@ using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using Microsoft.EntityFrameworkCore; using Microsoft.IdentityModel.Tokens; -using StackExchange.Redis; +using StackExchange.Redis.Extensions.Core.Abstractions; using System.IdentityModel.Tokens.Jwt; using System.Security.Claims; using System.Text; @@ -20,7 +20,7 @@ namespace MareSynchronosServer.Controllers; public class JwtController : Controller { private readonly IHttpContextAccessor _accessor; - private readonly IDatabase _redis; + private readonly IRedisDatabase _redis; private readonly MareDbContext _mareDbContext; private readonly SecretKeyAuthenticatorService _secretKeyAuthenticatorService; private readonly IConfigurationService _configuration; @@ -28,10 +28,10 @@ public class JwtController : Controller public JwtController(IHttpContextAccessor accessor, MareDbContext mareDbContext, SecretKeyAuthenticatorService secretKeyAuthenticatorService, IConfigurationService configuration, - IConnectionMultiplexer connectionMultiplexer) + IRedisDatabase redisDb) { _accessor = accessor; - _redis = connectionMultiplexer.GetDatabase(); + _redis = redisDb; _mareDbContext = mareDbContext; _secretKeyAuthenticatorService = secretKeyAuthenticatorService; _configuration = configuration; @@ -54,7 +54,7 @@ public class JwtController : Controller if (!authResult.Success && !authResult.TempBan) return Unauthorized("The provided secret key is invalid. Verify your accounts existence and/or recover the secret key."); if (!authResult.Success && authResult.TempBan) return Unauthorized("You are temporarily banned. Try connecting again later."); - var existingIdent = await _redis.StringGetAsync("UID:" + authResult.Uid); + var existingIdent = await _redis.GetAsync("UID:" + authResult.Uid); if (!string.IsNullOrEmpty(existingIdent)) return Unauthorized("Already logged in to this account."); var token = CreateToken(new List() diff --git a/MareSynchronosServer/MareSynchronosServer/Hubs/MareHub.Admin.cs b/MareSynchronosServer/MareSynchronosServer/Hubs/MareHub.Admin.cs index 0092739..e4419a5 100644 --- a/MareSynchronosServer/MareSynchronosServer/Hubs/MareHub.Admin.cs +++ b/MareSynchronosServer/MareSynchronosServer/Hubs/MareHub.Admin.cs @@ -82,12 +82,13 @@ public partial class MareHub public async Task> AdminGetOnlineUsers() { var users = await _dbContext.Users.AsNoTracking().ToListAsync().ConfigureAwait(false); - return users.Select(user => new { user, GetIdentFromUidFromRedis(user.UID).Result }).Where(a => !string.IsNullOrEmpty(a.Result)).Select(b => new OnlineUserDto + var redisUsers = await GetIdentFromUidsFromRedis(users.Select(u => u.UID)).ConfigureAwait(false); + return users.Select(user => new { User = user, Ident = redisUsers[user.UID] }).Where(a => !string.IsNullOrEmpty(a.Ident)).Select(b => new OnlineUserDto { - CharacterNameHash = b.Result, - UID = b.user.UID, - IsModerator = b.user.IsModerator, - IsAdmin = b.user.IsAdmin + CharacterNameHash = b.Ident, + UID = b.User.UID, + IsModerator = b.User.IsModerator, + IsAdmin = b.User.IsAdmin }).ToList(); } diff --git a/MareSynchronosServer/MareSynchronosServer/Hubs/MareHub.Functions.cs b/MareSynchronosServer/MareSynchronosServer/Hubs/MareHub.Functions.cs index e84e893..a075a47 100644 --- a/MareSynchronosServer/MareSynchronosServer/Hubs/MareHub.Functions.cs +++ b/MareSynchronosServer/MareSynchronosServer/Hubs/MareHub.Functions.cs @@ -9,23 +9,30 @@ public partial class MareHub { private async Task UpdateUserOnRedis() { - await _redis.StringSetAsync("UID:" + UserUID, UserCharaIdent, TimeSpan.FromSeconds(60), flags: StackExchange.Redis.CommandFlags.FireAndForget).ConfigureAwait(false); - await _redis.StringSetAsync("IDENT:" + UserCharaIdent, UserUID, TimeSpan.FromSeconds(60), flags: StackExchange.Redis.CommandFlags.FireAndForget).ConfigureAwait(false); + await _redis.AddAsync("UID:" + UserUID, UserCharaIdent, TimeSpan.FromSeconds(60), StackExchange.Redis.When.Always, StackExchange.Redis.CommandFlags.FireAndForget).ConfigureAwait(false); + await _redis.AddAsync("IDENT:" + UserCharaIdent, UserUID, TimeSpan.FromSeconds(60), StackExchange.Redis.When.Always, StackExchange.Redis.CommandFlags.FireAndForget).ConfigureAwait(false); } private async Task RemoveUserFromRedis() { - await _redis.StringGetDeleteAsync("UID:" + UserUID).ConfigureAwait(false); - await _redis.StringGetDeleteAsync("IDENT:" + UserCharaIdent).ConfigureAwait(false); + await _redis.RemoveAsync("UID:" + UserUID, StackExchange.Redis.CommandFlags.FireAndForget).ConfigureAwait(false); + await _redis.RemoveAsync("IDENT:" + UserCharaIdent, StackExchange.Redis.CommandFlags.FireAndForget).ConfigureAwait(false); } public async Task GetIdentFromUidFromRedis(string uid) { - return await _redis.StringGetAsync("UID:" + uid).ConfigureAwait(false); + return await _redis.GetAsync("UID:" + uid).ConfigureAwait(false); } + + public async Task> GetIdentFromUidsFromRedis(IEnumerable uids) + { + var result = await _redis.GetAllAsync(uids.Select(u => "UID:" + u).ToArray()).ConfigureAwait(false); + return result.ToDictionary(k => k.Key.Replace("UID:", "", StringComparison.Ordinal), k => k.Value, StringComparer.Ordinal); + } + public async Task GetUidFromIdentFromRedis(string ident) { - return await _redis.StringGetAsync("IDENT:" + ident).ConfigureAwait(false); + return await _redis.GetAsync("IDENT:" + ident).ConfigureAwait(false); } private async Task> GetAllPairedClientsWithPauseState(string? uid = null) diff --git a/MareSynchronosServer/MareSynchronosServer/Hubs/MareHub.User.cs b/MareSynchronosServer/MareSynchronosServer/Hubs/MareHub.User.cs index 54b534d..4f743e9 100644 --- a/MareSynchronosServer/MareSynchronosServer/Hubs/MareHub.User.cs +++ b/MareSynchronosServer/MareSynchronosServer/Hubs/MareHub.User.cs @@ -64,7 +64,8 @@ public partial class MareHub _logger.LogCallInfo(); var usersToSendOnlineTo = await SendOnlineToAllPairedUsers(UserCharaIdent).ConfigureAwait(false); - return usersToSendOnlineTo.Select(e => GetIdentFromUidFromRedis(e).Result).Where(t => !string.IsNullOrEmpty(t)).Distinct(StringComparer.Ordinal).ToList(); + var idents = await GetIdentFromUidsFromRedis(usersToSendOnlineTo).ConfigureAwait(false); + return idents.Where(i => !string.IsNullOrEmpty(i.Value)).Select(k => k.Value).ToList(); } [Authorize(Policy = "Identified")] @@ -146,8 +147,9 @@ public partial class MareHub } var allPairedUsers = await GetAllPairedUnpausedUsers().ConfigureAwait(false); + var idents = await GetIdentFromUidsFromRedis(allPairedUsers).ConfigureAwait(false); - var allPairedUsersDict = allPairedUsers.ToDictionary(f => f, f => GetIdentFromUidFromRedis(f).Result, StringComparer.Ordinal) + var allPairedUsersDict = idents .Where(f => visibleCharacterIds.Contains(f.Value, StringComparer.Ordinal)); _logger.LogCallInfo(MareHubLogger.Args(visibleCharacterIds.Count, allPairedUsersDict.Count())); diff --git a/MareSynchronosServer/MareSynchronosServer/Hubs/MareHub.cs b/MareSynchronosServer/MareSynchronosServer/Hubs/MareHub.cs index 72fa4ee..4e6ec36 100644 --- a/MareSynchronosServer/MareSynchronosServer/Hubs/MareHub.cs +++ b/MareSynchronosServer/MareSynchronosServer/Hubs/MareHub.cs @@ -9,7 +9,7 @@ using MareSynchronosShared.Services; using MareSynchronosShared.Utils; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.SignalR; -using StackExchange.Redis; +using StackExchange.Redis.Extensions.Core.Abstractions; namespace MareSynchronosServer.Hubs; @@ -28,12 +28,12 @@ public partial class MareHub : Hub, IMareHub private readonly int _maxJoinedGroupsByUser; private readonly int _maxGroupUserCount; private readonly IConfigurationService _configurationService; - private readonly IDatabase _redis; + private readonly IRedisDatabase _redis; public MareHub(MareMetrics mareMetrics, FileService.FileServiceClient fileServiceClient, MareDbContext mareDbContext, ILogger logger, SystemInfoService systemInfoService, IConfigurationService configuration, IHttpContextAccessor contextAccessor, - IConnectionMultiplexer connectionMultiplexer) + IRedisDatabase redisDb) { _mareMetrics = mareMetrics; _fileServiceClient = fileServiceClient; @@ -45,7 +45,7 @@ public partial class MareHub : Hub, IMareHub _maxJoinedGroupsByUser = configuration.GetValueOrDefault(nameof(ServerConfiguration.MaxJoinedGroupsByUser), 6); _maxGroupUserCount = configuration.GetValueOrDefault(nameof(ServerConfiguration.MaxGroupUserCount), 100); _contextAccessor = contextAccessor; - _redis = connectionMultiplexer.GetDatabase(); + _redis = redisDb; _logger = new MareHubLogger(this, logger); _dbContext = mareDbContext; } diff --git a/MareSynchronosServer/MareSynchronosServer/MareSynchronosServer.csproj b/MareSynchronosServer/MareSynchronosServer/MareSynchronosServer.csproj index 9edaa8e..f5f168d 100644 --- a/MareSynchronosServer/MareSynchronosServer/MareSynchronosServer.csproj +++ b/MareSynchronosServer/MareSynchronosServer/MareSynchronosServer.csproj @@ -39,6 +39,9 @@ + + + @@ -49,6 +52,7 @@ + diff --git a/MareSynchronosServer/MareSynchronosServer/RequirementHandlers/UserRequirementHandler.cs b/MareSynchronosServer/MareSynchronosServer/RequirementHandlers/UserRequirementHandler.cs index 4e09caf..00a64d1 100644 --- a/MareSynchronosServer/MareSynchronosServer/RequirementHandlers/UserRequirementHandler.cs +++ b/MareSynchronosServer/MareSynchronosServer/RequirementHandlers/UserRequirementHandler.cs @@ -4,6 +4,7 @@ using MareSynchronosShared.Data; using Microsoft.EntityFrameworkCore; using MareSynchronosShared.Utils; using StackExchange.Redis; +using StackExchange.Redis.Extensions.Core.Abstractions; namespace MareSynchronosServer.RequirementHandlers; @@ -11,13 +12,13 @@ public class UserRequirementHandler : AuthorizationHandler _logger; - private readonly IDatabase _redis; + private readonly IRedisDatabase _redis; - public UserRequirementHandler(MareDbContext dbContext, ILogger logger, IConnectionMultiplexer connectionMultiplexer) + public UserRequirementHandler(MareDbContext dbContext, ILogger logger, IRedisDatabase redisDb) { _dbContext = dbContext; _logger = logger; - _redis = connectionMultiplexer.GetDatabase(); + _redis = redisDb; } protected override async Task HandleRequirementAsync(AuthorizationHandlerContext context, UserRequirement requirement, HubInvocationContext resource) @@ -28,7 +29,7 @@ public class UserRequirementHandler : AuthorizationHandler("UID:" + uid).ConfigureAwait(false); if (ident == RedisValue.EmptyString) context.Fail(); } diff --git a/MareSynchronosServer/MareSynchronosServer/Services/SystemInfoService.cs b/MareSynchronosServer/MareSynchronosServer/Services/SystemInfoService.cs index 20bc02e..23171f3 100644 --- a/MareSynchronosServer/MareSynchronosServer/Services/SystemInfoService.cs +++ b/MareSynchronosServer/MareSynchronosServer/Services/SystemInfoService.cs @@ -6,7 +6,7 @@ using MareSynchronosShared.Services; using MareSynchronosShared.Utils; using Microsoft.AspNetCore.SignalR; using Microsoft.EntityFrameworkCore; -using StackExchange.Redis; +using StackExchange.Redis.Extensions.Core.Abstractions; namespace MareSynchronosServer.Services; @@ -17,19 +17,19 @@ public class SystemInfoService : IHostedService, IDisposable private readonly IServiceProvider _services; private readonly ILogger _logger; private readonly IHubContext _hubContext; - private readonly IConnectionMultiplexer _redis; + private readonly IRedisDatabase _redis; private Timer _timer; public SystemInfoDto SystemInfoDto { get; private set; } = new(); public SystemInfoService(MareMetrics mareMetrics, IConfigurationService configurationService, IServiceProvider services, - ILogger logger, IHubContext hubContext, IConnectionMultiplexer connectionMultiplexer) + ILogger logger, IHubContext hubContext, IRedisDatabase redisDb) { _mareMetrics = mareMetrics; _config = configurationService; _services = services; _logger = logger; _hubContext = hubContext; - _redis = connectionMultiplexer; + _redis = redisDb; } public Task StartAsync(CancellationToken cancellationToken) @@ -48,8 +48,7 @@ public class SystemInfoService : IHostedService, IDisposable _mareMetrics.SetGaugeTo(MetricsAPI.GaugeAvailableWorkerThreads, workerThreads); _mareMetrics.SetGaugeTo(MetricsAPI.GaugeAvailableIOWorkerThreads, ioThreads); - var endpoint = _redis.GetEndPoints().First(); - var onlineUsers = (_redis.GetServer(endpoint).Keys(pattern: "UID:*").Count()); + var onlineUsers = (_redis.SearchKeysAsync("UID:*").GetAwaiter().GetResult()).Count(); SystemInfoDto = new SystemInfoDto() { OnlineUsers = onlineUsers, diff --git a/MareSynchronosServer/MareSynchronosServer/Startup.cs b/MareSynchronosServer/MareSynchronosServer/Startup.cs index 1c30e15..d3f27b0 100644 --- a/MareSynchronosServer/MareSynchronosServer/Startup.cs +++ b/MareSynchronosServer/MareSynchronosServer/Startup.cs @@ -22,6 +22,9 @@ using Microsoft.IdentityModel.Tokens; using System.Text; using MareSynchronosServer.Authentication; using StackExchange.Redis; +using StackExchange.Redis.Extensions.Core.Configuration; +using System.Net; +using StackExchange.Redis.Extensions.System.Text.Json; namespace MareSynchronosServer; @@ -111,10 +114,36 @@ public class Startup } var options = ConfigurationOptions.Parse(redis); - options.ClientName = "Mare"; - options.ChannelPrefix = "UserData"; - ConnectionMultiplexer connectionMultiplexer = ConnectionMultiplexer.Connect(options); - services.AddSingleton(connectionMultiplexer); + + var endpoint = options.EndPoints.First(); + string address = ""; + int port = 0; + if (endpoint is DnsEndPoint) { address = ((DnsEndPoint)endpoint).Host; port = ((DnsEndPoint)endpoint).Port; } + if (endpoint is IPEndPoint) { address = ((IPEndPoint)endpoint).Address.ToString(); port = ((IPEndPoint)endpoint).Port; } + var redisConfiguration = new RedisConfiguration() + { + AbortOnConnectFail = true, + KeyPrefix = "", + Hosts = new RedisHost[] + { + new RedisHost(){ Host = address, Port = port } + }, + AllowAdmin = true, + ConnectTimeout = 3000, + Database = 0, + Ssl = false, + Password = options.Password, + ServerEnumerationStrategy = new ServerEnumerationStrategy() + { + Mode = ServerEnumerationStrategy.ModeOptions.All, + TargetRole = ServerEnumerationStrategy.TargetRoleOptions.Any, + UnreachableServerAction = ServerEnumerationStrategy.UnreachableServerActionOptions.Throw + }, + MaxValueLength = 1024, + PoolSize = 50 + }; + + services.AddStackExchangeRedisExtensions(redisConfiguration); } private void ConfigureIpRateLimiting(IServiceCollection services)