namespace Supercell.Laser.Server.Database.Cache { using Supercell.Laser.Server.Database.Models; using System.Collections.Concurrent; using System.Threading.Tasks; public static class AccountCache { private static ConcurrentDictionary CachedAccounts; private static Timer SaveTimer; private static volatile bool _isRunning; public static bool Started { get => _isRunning; set => _isRunning = value; } public static int Count => CachedAccounts.Count; public static void Init() { CachedAccounts = new ConcurrentDictionary(); _isRunning = true; SaveTimer = new Timer(SaveAllParallel, null, 30000, 30000); } public static void Shutdown() { _isRunning = false; SaveTimer?.Dispose(); SaveAllParallel(null); } public static void SaveAll() { SaveAllParallel(null); } private static void SaveAllParallel(object state) { if (!_isRunning || CachedAccounts.IsEmpty) return; try { var accounts = CachedAccounts.Values.ToList(); int total = accounts.Count; if (total == 0) return; int batchSize = Math.Max(1, total / Environment.ProcessorCount); var batches = accounts.Chunk(batchSize); Parallel.ForEach(batches, new ParallelOptions { MaxDegreeOfParallelism = Environment.ProcessorCount }, batch => { foreach (var account in batch) { try { Accounts.Save(account); } catch (Exception ex) { Logger.Print($"Save error for {account.AccountId}: {ex.Message}"); } } }); } catch (Exception ex) { Logger.Print($"SaveAll error: {ex.Message}"); } } public static bool IsAccountCached(long id) { return CachedAccounts.ContainsKey(id); } public static Account GetAccount(long id) { CachedAccounts.TryGetValue(id, out var account); return account; } public static void RemoveInCache(Account account) { if (account == null) return; CachedAccounts.TryRemove(account.AccountId, out _); } // Сохранить и выселить из кеша при выходе игрока. Без этого кеш растёт // бессрочно за время жизни процесса, а таймер SaveAllParallel каждые 30с // сериализует и пишет в БД весь накопленный кеш, а не только онлайн-игроков. public static void SaveAndEvict(long accountId) { if (CachedAccounts.TryRemove(accountId, out var account) && account != null) { try { Accounts.Save(account); } catch (Exception ex) { Logger.Print($"SaveAndEvict error for {accountId}: {ex.Message}"); } } } public static void Cache(Account account) { CachedAccounts[account.AccountId] = account; } } }