idk if this will help but this is a certified attempt
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
services:
|
||||
postgres:
|
||||
image: postgres:latest
|
||||
command: ["postgres", "-c", "log_statement=all"]
|
||||
restart: always
|
||||
ports:
|
||||
- 5432:5432/tcp
|
||||
|
||||
@@ -7,6 +7,7 @@ using MareSynchronos.API.Data;
|
||||
using MareSynchronos.API.Dto.Group;
|
||||
using MareSynchronosShared.Metrics;
|
||||
using Microsoft.AspNetCore.SignalR;
|
||||
using MareSynchronosShared.Data;
|
||||
|
||||
namespace MareSynchronosServer.Hubs;
|
||||
|
||||
@@ -70,16 +71,13 @@ public partial class MareHub
|
||||
_dbContext.Users.Remove(user);
|
||||
_dbContext.Auth.Remove(auth);
|
||||
await _dbContext.SaveChangesAsync().ConfigureAwait(false);
|
||||
|
||||
_cacheService.ClearCache(user.UID);
|
||||
_cacheService.MarkAsStale(null, user.UID);
|
||||
}
|
||||
|
||||
private async Task<List<string>> GetAllPairedUnpausedUsers(string? uid = null)
|
||||
{
|
||||
uid ??= UserUID;
|
||||
|
||||
return (await _cacheService.GetAllPairs(UserUID, _dbContext).ConfigureAwait(false))
|
||||
return (await GetAllPairInfo(UserUID).ConfigureAwait(false))
|
||||
.Where(u => !u.Value.OwnPermissions.IsPaused && u.Value.OtherPermissions != null && !u.Value.OtherPermissions.IsPaused)
|
||||
.Select(u => u.Key).ToList();
|
||||
}
|
||||
@@ -173,7 +171,7 @@ public partial class MareHub
|
||||
private async Task UserGroupLeave(GroupPair groupUserPair, string userIdent, string? uid = null)
|
||||
{
|
||||
uid ??= UserUID;
|
||||
var allUserPairs = await _cacheService.GetAllPairs(uid, _dbContext).ConfigureAwait(false);
|
||||
var allUserPairs = await GetAllPairInfo(uid).ConfigureAwait(false);
|
||||
if (!allUserPairs.TryGetValue(groupUserPair.GroupUserUID, out var info) || !info.IsSynced)
|
||||
{
|
||||
var groupUserIdent = await GetUserIdent(groupUserPair.GroupUserUID).ConfigureAwait(false);
|
||||
@@ -238,7 +236,6 @@ public partial class MareHub
|
||||
}
|
||||
|
||||
await _dbContext.SaveChangesAsync().ConfigureAwait(false);
|
||||
_cacheService.MarkAsStale(userUid, null);
|
||||
|
||||
_logger.LogCallInfo(MareHubLogger.Args(dto, "Success"));
|
||||
|
||||
@@ -252,6 +249,126 @@ public partial class MareHub
|
||||
}
|
||||
}
|
||||
|
||||
public record UserQueryPermissionEntry(UserQueryEntry OtherUser, UserPermissionSet OwnPermissions, UserPermissionSet? OtherPermissions);
|
||||
public record UserQueryEntry(string UID, bool IsPaired, string GID, string Alias);
|
||||
private async Task<UserInfo?> GetPairInfo(string uid, string otheruid)
|
||||
{
|
||||
var query = _dbContext.ClientPairs.Where(u => u.UserUID == uid && u.OtherUserUID == otheruid)
|
||||
.Join(_dbContext.ClientPairs.Where(u => u.UserUID == otheruid && u.OtherUserUID == uid),
|
||||
user => user.OtherUserUID, user => user.UserUID, (user, otheruser) =>
|
||||
new
|
||||
{
|
||||
UserUID = user.UserUID,
|
||||
OtherUserUID = user.OtherUserUID,
|
||||
Gid = string.Empty,
|
||||
Synced = otheruser != null
|
||||
});
|
||||
|
||||
var query2 = _dbContext.GroupPairs.Where(u => u.GroupUserUID == uid)
|
||||
.Join(_dbContext.GroupPairs.Where(u => u.GroupUserUID == otheruid),
|
||||
ownPair => ownPair.GroupGID, otherPair => otherPair.GroupGID,
|
||||
(ownPair, otherPair) =>
|
||||
new
|
||||
{
|
||||
UserUID = ownPair.GroupUserUID,
|
||||
OtherUserUID = otherPair.GroupUserUID,
|
||||
Gid = ownPair.GroupGID,
|
||||
Synced = true,
|
||||
});
|
||||
|
||||
var result = from user in query.Union(query2)
|
||||
join u in _dbContext.Users on user.OtherUserUID equals u.UID
|
||||
join o in _dbContext.Permissions
|
||||
on new { user.UserUID, user.OtherUserUID }
|
||||
equals new { o.UserUID, o.OtherUserUID }
|
||||
into ownperms
|
||||
from ownperm in ownperms.DefaultIfEmpty()
|
||||
join p in _dbContext.Permissions
|
||||
on new { UserUID = user.OtherUserUID, OtherUserUID = user.UserUID }
|
||||
equals new { p.UserUID, p.OtherUserUID }
|
||||
into otherperms
|
||||
from otherperm in otherperms.DefaultIfEmpty()
|
||||
select new
|
||||
{
|
||||
UserUID = user.UserUID,
|
||||
OtherUserUID = user.OtherUserUID,
|
||||
OtherUserAlias = u.Alias,
|
||||
GID = user.Gid,
|
||||
Synced = user.Synced,
|
||||
OwnPermissions = ownperm,
|
||||
OtherPermissions = otherperm
|
||||
};
|
||||
|
||||
var resultList = await result.AsNoTracking().ToListAsync().ConfigureAwait(false);
|
||||
|
||||
if (!resultList.Any()) return null;
|
||||
|
||||
var groups = resultList.Select(g => g.GID).ToList();
|
||||
return new UserInfo(resultList[0].OtherUserAlias,
|
||||
resultList.SingleOrDefault(p => string.IsNullOrEmpty(p.GID))?.Synced ?? false,
|
||||
resultList.Max(p => p.Synced),
|
||||
resultList.Select(p => string.IsNullOrEmpty(p.GID) ? Constants.IndividualKeyword : p.GID).ToList(),
|
||||
resultList[0].OwnPermissions,
|
||||
resultList[0].OtherPermissions);
|
||||
}
|
||||
|
||||
private async Task<Dictionary<string, UserInfo>> GetAllPairInfo(string uid)
|
||||
{
|
||||
var query = _dbContext.ClientPairs.Where(u => u.UserUID == uid)
|
||||
.Join(_dbContext.ClientPairs.Where(u => u.OtherUserUID == uid),
|
||||
user => user.OtherUserUID, user => user.UserUID, (user, otheruser) =>
|
||||
new
|
||||
{
|
||||
UserUID = Convert.ToString(user.UserUID),
|
||||
OtherUserUID = Convert.ToString(user.OtherUserUID),
|
||||
Gid = string.Empty,
|
||||
Synced = otheruser != null
|
||||
});
|
||||
|
||||
var query2 = _dbContext.GroupPairs.Where(u => u.GroupUserUID == uid)
|
||||
.Join(_dbContext.GroupPairs.Where(u => u.GroupUserUID != uid),
|
||||
ownPair => ownPair.GroupGID, otherPair => otherPair.GroupGID,
|
||||
(ownPair, otherPair) =>
|
||||
new
|
||||
{
|
||||
UserUID = Convert.ToString(ownPair.GroupUserUID),
|
||||
OtherUserUID = Convert.ToString(otherPair.GroupUserUID),
|
||||
Gid = Convert.ToString(ownPair.GroupGID),
|
||||
Synced = true,
|
||||
});
|
||||
|
||||
var result = from user in query.Union(query2)
|
||||
join u in _dbContext.Users on user.OtherUserUID equals Convert.ToString(u.UID)
|
||||
join o in _dbContext.Permissions
|
||||
on new { UserUID = user.UserUID, OtherUserUID = user.OtherUserUID }
|
||||
equals new { UserUID = Convert.ToString(o.UserUID), OtherUserUID = Convert.ToString(o.OtherUserUID) }
|
||||
into ownperms
|
||||
from ownperm in ownperms.DefaultIfEmpty()
|
||||
join p in _dbContext.Permissions
|
||||
on new { UserUID = user.OtherUserUID, OtherUserUID = user.UserUID }
|
||||
equals new { UserUID = Convert.ToString(p.UserUID), OtherUserUID = Convert.ToString(p.OtherUserUID) }
|
||||
into otherperms
|
||||
from otherperm in otherperms.DefaultIfEmpty()
|
||||
select new
|
||||
{
|
||||
UserUID = user.UserUID,
|
||||
OtherUserUID = user.OtherUserUID,
|
||||
OtherUserAlias = u.Alias,
|
||||
GID = user.Gid,
|
||||
Synced = user.Synced,
|
||||
OwnPermissions = ownperm,
|
||||
OtherPermissions = otherperm
|
||||
};
|
||||
|
||||
var resultList = await result.AsNoTracking().ToListAsync().ConfigureAwait(false);
|
||||
return resultList.GroupBy(g => g.OtherUserUID, StringComparer.Ordinal).ToDictionary(g => g.Key, g =>
|
||||
{
|
||||
return new UserInfo(g.First().OtherUserAlias,
|
||||
g.SingleOrDefault(p => string.IsNullOrEmpty(p.GID))?.Synced ?? false,
|
||||
g.Max(p => p.Synced),
|
||||
g.Select(p => string.IsNullOrEmpty(p.GID) ? Constants.IndividualKeyword : p.GID).ToList(),
|
||||
g.First().OwnPermissions,
|
||||
g.First().OtherPermissions);
|
||||
}, StringComparer.Ordinal);
|
||||
}
|
||||
|
||||
public record UserInfo(string Alias, bool IndividuallyPaired, bool IsSynced, List<string> GIDs, UserPermissionSet? OwnPermissions, UserPermissionSet? OtherPermissions);
|
||||
}
|
||||
@@ -81,7 +81,7 @@ public partial class MareHub
|
||||
groupPreferredPermissions.DisableVFX = dto.GroupPairPermissions.IsDisableVFX();
|
||||
|
||||
// set the permissions for every group pair that is not sticky
|
||||
var allPairs = (await _cacheService.GetAllPairs(UserUID, _dbContext).ConfigureAwait(false))
|
||||
var allPairs = (await GetAllPairInfo(UserUID).ConfigureAwait(false))
|
||||
.Where(u => !u.Value.OwnPermissions.Sticky)
|
||||
.ToDictionary(d => d.Key, d => d.Value, StringComparer.Ordinal);
|
||||
|
||||
@@ -100,11 +100,6 @@ public partial class MareHub
|
||||
}
|
||||
await _dbContext.SaveChangesAsync().ConfigureAwait(false);
|
||||
|
||||
foreach (var item in affectedGroupPairs)
|
||||
{
|
||||
_cacheService.MarkAsStale(UserUID, item.Key);
|
||||
}
|
||||
|
||||
// send messages
|
||||
UserPermissions permissions = UserPermissions.NoneSet;
|
||||
permissions.SetPaused(groupPreferredPermissions.IsPaused);
|
||||
@@ -228,10 +223,6 @@ public partial class MareHub
|
||||
}
|
||||
|
||||
await _dbContext.SaveChangesAsync().ConfigureAwait(false);
|
||||
foreach (var user in notPinned)
|
||||
{
|
||||
_cacheService.MarkAsStale(user.GroupUserUID, null);
|
||||
}
|
||||
}
|
||||
|
||||
[Authorize(Policy = "Identified")]
|
||||
@@ -432,8 +423,7 @@ public partial class MareHub
|
||||
return false;
|
||||
|
||||
// get all pairs before we join
|
||||
var allUserPairs = (await _cacheService.GetAllPairs(UserUID, _dbContext).ConfigureAwait(false))
|
||||
.ToDictionary(u => u.Key, u => u.Value, StringComparer.Ordinal);
|
||||
var allUserPairs = (await GetAllPairInfo(UserUID).ConfigureAwait(false));
|
||||
|
||||
if (oneTimeInvite != null)
|
||||
{
|
||||
@@ -485,7 +475,7 @@ public partial class MareHub
|
||||
|
||||
var groupPairs = await _dbContext.GroupPairs.Include(p => p.GroupUser).Where(p => p.GroupGID == group.GID && p.GroupUserUID != UserUID).ToListAsync().ConfigureAwait(false);
|
||||
|
||||
var userPairsAfterJoin = await _cacheService.GetAllPairs(UserUID, _dbContext, true).ConfigureAwait(false);
|
||||
var userPairsAfterJoin = await GetAllPairInfo(UserUID).ConfigureAwait(false);
|
||||
|
||||
foreach (var pair in groupPairs)
|
||||
{
|
||||
@@ -584,11 +574,6 @@ public partial class MareHub
|
||||
|
||||
await _dbContext.SaveChangesAsync().ConfigureAwait(false);
|
||||
|
||||
foreach (var pair in groupPairs)
|
||||
{
|
||||
_cacheService.MarkAsStale(UserUID, pair.GroupUserUID);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -631,7 +616,6 @@ public partial class MareHub
|
||||
foreach (var groupUserPair in groupPairs)
|
||||
{
|
||||
await UserGroupLeave(groupUserPair, userIdent, dto.User.UID).ConfigureAwait(false);
|
||||
_cacheService.MarkAsStale(dto.User.UID, groupUserPair.GroupUserUID);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -65,8 +65,7 @@ public partial class MareHub
|
||||
};
|
||||
await _dbContext.ClientPairs.AddAsync(wl).ConfigureAwait(false);
|
||||
|
||||
_cacheService.MarkAsStale(UserUID, otherUser.UID);
|
||||
var existingData = await _cacheService.GetPairData(UserUID, otherUser.UID, _dbContext).ConfigureAwait(false);
|
||||
var existingData = await GetPairInfo(UserUID, otherUser.UID).ConfigureAwait(false);
|
||||
|
||||
var permissions = existingData?.OwnPermissions;
|
||||
if (permissions == null || !permissions.Sticky)
|
||||
@@ -102,7 +101,6 @@ public partial class MareHub
|
||||
}
|
||||
|
||||
await _dbContext.SaveChangesAsync().ConfigureAwait(false);
|
||||
_cacheService.MarkAsStale(UserUID, otherUser.UID);
|
||||
|
||||
// get the opposite entry of the client pair
|
||||
var otherEntry = OppositeEntry(otherUser.UID);
|
||||
@@ -171,7 +169,7 @@ public partial class MareHub
|
||||
{
|
||||
_logger.LogCallInfo();
|
||||
|
||||
var pairs = await _cacheService.GetAllPairs(UserUID, _dbContext, true).ConfigureAwait(false);
|
||||
var pairs = await GetAllPairInfo(UserUID).ConfigureAwait(false);
|
||||
return pairs.Select(p =>
|
||||
{
|
||||
return new UserFullPairDto(new UserData(p.Key, p.Value.Alias),
|
||||
@@ -263,7 +261,7 @@ public partial class MareHub
|
||||
+ string.Join(Environment.NewLine, invalidFileSwapPaths.Select(p => "Invalid FileSwap Path: " + p)));
|
||||
}
|
||||
|
||||
var allPairs = await _cacheService.GetAllPairs(UserUID, _dbContext).ConfigureAwait(false);
|
||||
var allPairs = await GetAllPairInfo(UserUID).ConfigureAwait(false);
|
||||
allPairs.Where(p => !p.Value.OwnPermissions.IsPaused && p.Value.OtherPermissions != null && !p.Value.OtherPermissions.IsPaused).ToList();
|
||||
|
||||
var allPairedUsers = await GetAllPairedUnpausedUsers().ConfigureAwait(false);
|
||||
@@ -291,12 +289,11 @@ public partial class MareHub
|
||||
await _dbContext.ClientPairs.SingleOrDefaultAsync(w => w.UserUID == UserUID && w.OtherUserUID == dto.User.UID).ConfigureAwait(false);
|
||||
if (callerPair == null) return;
|
||||
|
||||
var pairData = await _cacheService.GetPairData(UserUID, dto.User.UID, _dbContext).ConfigureAwait(false);
|
||||
var pairData = await GetPairInfo(UserUID, dto.User.UID).ConfigureAwait(false);
|
||||
|
||||
// delete from database, send update info to users pair list
|
||||
_dbContext.ClientPairs.Remove(callerPair);
|
||||
await _dbContext.SaveChangesAsync().ConfigureAwait(false);
|
||||
_cacheService.MarkAsStale(UserUID, dto.User.UID);
|
||||
|
||||
_logger.LogCallInfo(MareHubLogger.Args(dto, "Success"));
|
||||
|
||||
@@ -323,7 +320,7 @@ public partial class MareHub
|
||||
// if the either had paused, do nothing
|
||||
if (callerHadPaused && otherHadPaused) return;
|
||||
|
||||
var currentPairData = await _cacheService.GetPairData(UserUID, dto.User.UID, _dbContext).ConfigureAwait(false);
|
||||
var currentPairData = await GetPairInfo(UserUID, dto.User.UID).ConfigureAwait(false);
|
||||
|
||||
// if neither user had paused each other and either is not in an unpaused group with each other, change state to offline
|
||||
if (!currentPairData?.IsSynced ?? true)
|
||||
@@ -384,7 +381,7 @@ public partial class MareHub
|
||||
UserPermissionSet prevPermissions = await _dbContext.Permissions.SingleOrDefaultAsync(w => w.UserUID == UserUID && w.OtherUserUID == dto.User.UID).ConfigureAwait(false);
|
||||
if (prevPermissions == null) return; // you always should have permissions to another user
|
||||
|
||||
var oldPairData = await _cacheService.GetPairData(UserUID, dto.User.UID, _dbContext).ConfigureAwait(false);
|
||||
var oldPairData = await GetPairInfo(UserUID, dto.User.UID).ConfigureAwait(false);
|
||||
bool setSticky = false;
|
||||
if (!oldPairData.GIDs.Contains(Constants.IndividualKeyword, StringComparer.Ordinal))
|
||||
{
|
||||
@@ -404,7 +401,6 @@ public partial class MareHub
|
||||
prevPermissions.Sticky = dto.Permissions.IsSticky() || setSticky;
|
||||
_dbContext.Update(prevPermissions);
|
||||
await _dbContext.SaveChangesAsync().ConfigureAwait(false);
|
||||
_cacheService.MarkAsStale(UserUID, dto.User.UID);
|
||||
|
||||
_logger.LogCallInfo(MareHubLogger.Args(dto, "Success"));
|
||||
|
||||
@@ -414,7 +410,7 @@ public partial class MareHub
|
||||
await Clients.User(UserUID).Client_UserUpdateSelfPairPermissions(new UserPermissionsDto(dto.User, permCopy)).ConfigureAwait(false);
|
||||
await Clients.User(dto.User.UID).Client_UserUpdateOtherPairPermissions(new UserPermissionsDto(new UserData(UserUID), dto.Permissions)).ConfigureAwait(false);
|
||||
|
||||
var newPairData = await _cacheService.GetPairData(UserUID, dto.User.UID, _dbContext).ConfigureAwait(false);
|
||||
var newPairData = await GetPairInfo(UserUID, dto.User.UID).ConfigureAwait(false);
|
||||
|
||||
if (newPairData.OwnPermissions.IsPaused != oldPairData.OwnPermissions.IsPaused)
|
||||
{
|
||||
|
||||
@@ -30,14 +30,13 @@ public partial class MareHub : Hub<IMareHub>, IMareHub
|
||||
private readonly int _maxJoinedGroupsByUser;
|
||||
private readonly int _maxGroupUserCount;
|
||||
private readonly IRedisDatabase _redis;
|
||||
private readonly UserPairCacheService _cacheService;
|
||||
private readonly Uri _fileServerAddress;
|
||||
private readonly Version _expectedClientVersion;
|
||||
|
||||
public MareHub(MareMetrics mareMetrics,
|
||||
MareDbContext mareDbContext, ILogger<MareHub> logger, SystemInfoService systemInfoService,
|
||||
IConfigurationService<ServerConfiguration> configuration, IHttpContextAccessor contextAccessor,
|
||||
IRedisDatabase redisDb, UserPairCacheService cacheService)
|
||||
IRedisDatabase redisDb)
|
||||
{
|
||||
_mareMetrics = mareMetrics;
|
||||
_systemInfoService = systemInfoService;
|
||||
@@ -49,7 +48,6 @@ public partial class MareHub : Hub<IMareHub>, IMareHub
|
||||
_expectedClientVersion = configuration.GetValueOrDefault(nameof(ServerConfiguration.ExpectedClientVersion), new Version(0, 0, 0));
|
||||
_contextAccessor = contextAccessor;
|
||||
_redis = redisDb;
|
||||
_cacheService = cacheService;
|
||||
_logger = new MareHubLogger(this, logger);
|
||||
_dbContext = mareDbContext;
|
||||
}
|
||||
@@ -144,7 +142,6 @@ public partial class MareHub : Hub<IMareHub>, IMareHub
|
||||
_logger.LogCallWarning(MareHubLogger.Args(_contextAccessor.GetIpAddress(), exception.Message, exception.StackTrace));
|
||||
|
||||
await RemoveUserFromRedis().ConfigureAwait(false);
|
||||
_cacheService.ClearCache(UserUID);
|
||||
|
||||
await SendOfflineToAllPairedUsers().ConfigureAwait(false);
|
||||
|
||||
|
||||
@@ -1,217 +0,0 @@
|
||||
using MareSynchronos.API.Data;
|
||||
using MareSynchronosShared.Data;
|
||||
using MareSynchronosShared.Models;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using System.Collections.Concurrent;
|
||||
|
||||
namespace MareSynchronosServer.Services;
|
||||
|
||||
public class UserPairCacheService : IHostedService
|
||||
{
|
||||
private readonly ConcurrentDictionary<string, Dictionary<string, UserInfo>> _cache;
|
||||
private readonly ILogger<UserPairCacheService> _logger;
|
||||
private readonly IDbContextFactory<MareDbContext> _dbContextFactory;
|
||||
private readonly ConcurrentQueue<(string? UID, string? OtherUID)> _staleUserData;
|
||||
public UserPairCacheService(ILogger<UserPairCacheService> logger, IDbContextFactory<MareDbContext> dbContextFactory)
|
||||
{
|
||||
_logger = logger;
|
||||
_dbContextFactory = dbContextFactory;
|
||||
_staleUserData = new();
|
||||
_cache = new(StringComparer.OrdinalIgnoreCase);
|
||||
}
|
||||
|
||||
public void ClearCache(string uid)
|
||||
{
|
||||
_cache.TryRemove(uid, out _);
|
||||
}
|
||||
|
||||
public async Task<Dictionary<string, UserInfo>> GetAllPairs(string uid, MareDbContext dbContext, bool ignoreCache = false)
|
||||
{
|
||||
if (ignoreCache)
|
||||
{
|
||||
_cache[uid] = await BuildFullCache(dbContext, uid).ConfigureAwait(false);
|
||||
return _cache[uid];
|
||||
}
|
||||
|
||||
await WaitForProcessing(uid).ConfigureAwait(false);
|
||||
|
||||
if (!_cache.ContainsKey(uid))
|
||||
{
|
||||
_logger.LogDebug("Building full cache: Did not find PairData for {uid}", uid);
|
||||
|
||||
_cache[uid] = await BuildFullCache(dbContext, uid).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
return _cache[uid];
|
||||
}
|
||||
|
||||
public async Task<UserInfo?> GetPairData(string uid, string otheruid, MareDbContext dbContext)
|
||||
{
|
||||
await WaitForProcessing(uid, otheruid).ConfigureAwait(false);
|
||||
|
||||
if (!_cache.TryGetValue(uid, out var cachedInfos))
|
||||
{
|
||||
_logger.LogDebug("Building full cache: Did not find PairData for {uid}:{otheruid}", uid, otheruid);
|
||||
try
|
||||
{
|
||||
_cache[uid] = cachedInfos = await BuildFullCache(dbContext, uid).ConfigureAwait(false);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Error during full PairCache calculation of {uid}:{otheruid}", uid, otheruid);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
if (!cachedInfos.TryGetValue(otheruid, out var info))
|
||||
{
|
||||
_logger.LogDebug("Building individual cache: Did not find PairData for {uid}:{otheruid}", uid, otheruid);
|
||||
try
|
||||
{
|
||||
info = await BuildIndividualCache(dbContext, uid, otheruid).ConfigureAwait(false);
|
||||
_cache[uid][otheruid] = info;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Error during individual PairCache calculation of {uid}:{otheruid}", uid, otheruid);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
return info;
|
||||
}
|
||||
|
||||
public void MarkAsStale(string? uid, string? otheruid)
|
||||
{
|
||||
if (!_staleUserData.Any(u => string.Equals(u.UID, uid, StringComparison.Ordinal)
|
||||
&& string.Equals(u.OtherUID, otheruid, StringComparison.OrdinalIgnoreCase)))
|
||||
{
|
||||
_staleUserData.Enqueue((uid, otheruid));
|
||||
}
|
||||
}
|
||||
|
||||
public Task StartAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
_ = ProcessStaleEntries();
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public Task StopAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
private async Task<Dictionary<string, UserInfo>> BuildFullCache(MareDbContext dbContext, string uid)
|
||||
{
|
||||
var pairs = await dbContext.GetAllPairsForUser(uid).ToListAsync().ConfigureAwait(false);
|
||||
|
||||
var pairsDict = pairs.GroupBy(g => g.OtherUserUID, StringComparer.Ordinal)
|
||||
.ToDictionary(g => g.Key, g =>
|
||||
{
|
||||
return new UserInfo(g.First().Alias,
|
||||
g.SingleOrDefault(p => string.IsNullOrEmpty(p.GID))?.Synced ?? false,
|
||||
g.Max(p => p.Synced),
|
||||
g.Select(p => string.IsNullOrEmpty(p.GID) ? Constants.IndividualKeyword : p.GID).ToList(),
|
||||
g.First().OwnPermissions,
|
||||
g.First().OtherPermissions);
|
||||
}, StringComparer.OrdinalIgnoreCase);
|
||||
|
||||
return pairsDict.Where(p => p.Value.OwnPermissions != null).ToDictionary(u => u.Key, u => u.Value, StringComparer.Ordinal);
|
||||
}
|
||||
|
||||
private async Task<UserInfo?> BuildIndividualCache(MareDbContext dbContext, string uid, string otheruid)
|
||||
{
|
||||
var pairs = (await dbContext.GetAllPairsForUser(uid).ToListAsync().ConfigureAwait(false)).Where(u => string.Equals(u.OtherUserUID, otheruid, StringComparison.Ordinal)).ToList();
|
||||
|
||||
if (!pairs.Any()) return null;
|
||||
|
||||
var groups = pairs.Select(g => g.GID).ToList();
|
||||
var userInfo = new UserInfo(pairs[0].Alias,
|
||||
pairs.SingleOrDefault(p => string.IsNullOrEmpty(p.GID))?.Synced ?? false,
|
||||
pairs.Max(p => p.Synced),
|
||||
pairs.Select(p => string.IsNullOrEmpty(p.GID) ? Constants.IndividualKeyword : p.GID).ToList(),
|
||||
pairs[0].OwnPermissions,
|
||||
pairs[0].OtherPermissions);
|
||||
|
||||
return userInfo.OwnPermissions == null ? null : userInfo;
|
||||
}
|
||||
|
||||
private async Task ProcessStaleEntries()
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
await Task.Delay(250).ConfigureAwait(false);
|
||||
if (_staleUserData.Any())
|
||||
{
|
||||
_logger.LogDebug("Processing Stale Entries");
|
||||
|
||||
using var dbContext = await _dbContextFactory.CreateDbContextAsync().ConfigureAwait(false);
|
||||
while (_staleUserData.TryDequeue(out var staleUserPair))
|
||||
{
|
||||
try
|
||||
{
|
||||
if (staleUserPair.UID == null)
|
||||
{
|
||||
foreach (var entry in _cache.Where(c => c.Value.ContainsKey(staleUserPair.OtherUID)).Select(k => k.Key).Distinct(StringComparer.Ordinal).ToList())
|
||||
{
|
||||
_logger.LogDebug("UID is null; Building Individual Cache for {user}:{user2}", staleUserPair.UID, entry);
|
||||
_staleUserData.Enqueue(new(staleUserPair.OtherUID, entry));
|
||||
}
|
||||
}
|
||||
else if (staleUserPair.OtherUID == null)
|
||||
{
|
||||
foreach (var entry in _cache.Where(c => c.Value.ContainsKey(staleUserPair.UID)).Select(k => k.Key).Distinct(StringComparer.Ordinal).ToList())
|
||||
{
|
||||
_logger.LogDebug("OtherUID is null; Building Individual Cache for {user}:{user2}", staleUserPair.UID, entry);
|
||||
_staleUserData.Enqueue(new(staleUserPair.UID, entry));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (_cache.ContainsKey(staleUserPair.UID))
|
||||
{
|
||||
_logger.LogDebug("Building Individual Cache for {user}:{user2}", staleUserPair.UID, staleUserPair.OtherUID);
|
||||
|
||||
var userInfo = await BuildIndividualCache(dbContext, staleUserPair.UID, staleUserPair.OtherUID).ConfigureAwait(false);
|
||||
|
||||
if (userInfo == null) _cache[staleUserPair.UID].Remove(staleUserPair.OtherUID);
|
||||
else _cache[staleUserPair.UID][staleUserPair.OtherUID] = userInfo;
|
||||
|
||||
if (_cache.ContainsKey(staleUserPair.OtherUID))
|
||||
{
|
||||
_logger.LogDebug("Building Individual Cache for {user}:{user2}", staleUserPair.OtherUID, staleUserPair.UID);
|
||||
var otherUserInfo = await BuildIndividualCache(dbContext, staleUserPair.OtherUID, staleUserPair.UID).ConfigureAwait(false);
|
||||
if (otherUserInfo == null) _cache[staleUserPair.OtherUID].Remove(staleUserPair.UID);
|
||||
else _cache[staleUserPair.OtherUID][staleUserPair.UID] = otherUserInfo;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Error during Stale entry processing");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private async Task WaitForProcessing(string uid)
|
||||
{
|
||||
while (_staleUserData.Any(u => string.Equals(u.UID, uid, StringComparison.Ordinal)))
|
||||
{
|
||||
await Task.Delay(50).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
private async Task WaitForProcessing(string uid, string otheruid)
|
||||
{
|
||||
while (_staleUserData.Any(u => string.Equals(u.UID, uid, StringComparison.Ordinal) && string.Equals(u.OtherUID, otheruid, StringComparison.Ordinal)))
|
||||
{
|
||||
await Task.Delay(50).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
public record UserInfo(string Alias, bool IndividuallyPaired, bool IsSynced, List<string> GIDs, UserPermissionSet? OwnPermissions, UserPermissionSet? OtherPermissions);
|
||||
}
|
||||
@@ -90,9 +90,7 @@ public class Startup
|
||||
|
||||
services.AddSingleton<ServerTokenGenerator>();
|
||||
services.AddSingleton<SystemInfoService>();
|
||||
services.AddSingleton<UserPairCacheService>();
|
||||
services.AddHostedService(provider => provider.GetService<SystemInfoService>());
|
||||
services.AddHostedService(p => p.GetService<UserPairCacheService>());
|
||||
// configure services based on main server status
|
||||
ConfigureServicesBasedOnShardType(services, mareConfig, isMainServer);
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
using MareSynchronos.API.Data.Enum;
|
||||
using MareSynchronos.API.Data.Extensions;
|
||||
using MareSynchronosShared.Models;
|
||||
using static MareSynchronosServer.Services.UserPairCacheService;
|
||||
using static MareSynchronosServer.Hubs.MareHub;
|
||||
|
||||
namespace MareSynchronosServer.Utils;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user