using Disco.Data.Repository; using Disco.Models.Repository; using Disco.Services.Authorization; using System; using System.Collections.Concurrent; using System.Linq; namespace Disco.Services.Users { internal static class Cache { private static ConcurrentDictionary> _Cache = new ConcurrentDictionary>(StringComparer.OrdinalIgnoreCase); private const long CacheTimeoutTicks = 6000000000; // 10 Minutes internal static AuthorizationToken GetAuthorization(string UserId, DiscoDataContext Database, bool ForceRefresh) { Tuple record = Get(UserId, Database, ForceRefresh); if (record == null) return null; else return record.Item2; } internal static AuthorizationToken GetAuthorization(string UserId, bool ForceRefresh) { Tuple record = Get(UserId, ForceRefresh); if (record == null) return null; else return record.Item2; } internal static AuthorizationToken GetAuthorization(string UserId, DiscoDataContext Database) { return GetAuthorization(UserId, Database, false); } internal static AuthorizationToken GetAuthorization(string UserId) { return GetAuthorization(UserId, false); } internal static bool TryGetUser(string UserId, DiscoDataContext Database, bool ForceRefresh, out User User) { return TryGet(UserId, Database, ForceRefresh, out User, out _, out _); } internal static User GetUser(string UserId, DiscoDataContext Database, bool ForceRefresh) { Tuple record = Get(UserId, Database, ForceRefresh); if (record == null) return null; else return record.Item1; } internal static User GetUser(string UserId, bool ForceRefresh) { Tuple record = Get(UserId, ForceRefresh); if (record == null) return null; else return record.Item1; } internal static User GetUser(string UserId, DiscoDataContext Database) { return GetUser(UserId, Database, false); } internal static User GetUser(string UserId) { return GetUser(UserId, false); } internal static bool TryGet(string UserId, DiscoDataContext Database, bool ForceRefresh, out User User, out AuthorizationToken AuthToken, out DateTime CacheExpirationTimestamp) { Tuple record = null; // Check Cache if (!ForceRefresh) record = TryUserCache(UserId); if (record == null && UserService.TryImportUser(Database, UserId, out var user, out var authorizationToken)) record = SetValue(UserId, Tuple.Create(user, authorizationToken)); if (record != null) { User = record.Item1; AuthToken = record.Item2; CacheExpirationTimestamp = record.Item3; return true; } User = default; AuthToken = default; CacheExpirationTimestamp = default; return false; } internal static Tuple Get(string UserId, DiscoDataContext Database, bool ForceRefresh) { Tuple record = null; // Check Cache if (!ForceRefresh) record = TryUserCache(UserId); if (record == null) { var importedUser = UserService.ImportUser(Database, UserId); record = SetValue(UserId, importedUser); } return record; } internal static Tuple Get(string UserId, DiscoDataContext Database) { return Get(UserId, Database, false); } internal static Tuple Get(string UserId, bool ForceRefresh) { // Check Cache Tuple record = null; if (!ForceRefresh) record = TryUserCache(UserId); if (record == null) { // Load from Repository using (DiscoDataContext database = new DiscoDataContext()) { record = Get(UserId, database, true); } } return record; } internal static Tuple Get(string UserId) { return Get(UserId, false); } internal static Tuple TryUserCache(string UserId) { var cache = _Cache; if (cache.TryGetValue(UserId, out var record)) { if (record.Item3 > DateTime.Now) return record; else cache.TryRemove(UserId, out _); } return null; } internal static Tuple SetValue(string UserId, Tuple Record) { var cache = _Cache; var record = Tuple.Create(Record.Item1, Record.Item2, DateTime.Now.AddTicks(CacheTimeoutTicks)); if (cache.ContainsKey(UserId)) { if (cache.TryGetValue(UserId, out var oldRecord)) { cache.TryUpdate(UserId, record, oldRecord); return record; } } cache.TryAdd(UserId, record); return record; } internal static bool InvalidateRecord(string UserId) { return _Cache.TryRemove(UserId, out _); } internal static void CleanStaleCache() { var cache = _Cache; var userIds = cache.Keys.ToArray(); foreach (string userId in userIds) { if (cache.TryGetValue(userId, out var record)) { if (record.Item3 <= DateTime.Now) cache.TryRemove(userId, out _); } } } internal static void FlushCache() { _Cache = new ConcurrentDictionary>(StringComparer.OrdinalIgnoreCase); } } }