diff --git a/src/Managing.Api/appsettings.json b/src/Managing.Api/appsettings.json index 45eafb83..c9dc36a7 100644 --- a/src/Managing.Api/appsettings.json +++ b/src/Managing.Api/appsettings.json @@ -95,9 +95,9 @@ "MaxMethodExecutionsPerWindow": 50, "LongRunningQueryThresholdMs": 1000, "SentryAlertThreshold": 5, - "SlowQueryThresholdMs": 2000, + "SlowQueryThresholdMs": 1500, "LogSlowQueriesOnly": false, "LogErrorsOnly": false, - "DataRetentionMinutes": 30 + "DataRetentionMinutes": 300 } } \ No newline at end of file diff --git a/src/Managing.Infrastructure.Database/PostgreSql/PostgreSqlAccountRepository.cs b/src/Managing.Infrastructure.Database/PostgreSql/PostgreSqlAccountRepository.cs index 2184907f..7c826ee8 100644 --- a/src/Managing.Infrastructure.Database/PostgreSql/PostgreSqlAccountRepository.cs +++ b/src/Managing.Infrastructure.Database/PostgreSql/PostgreSqlAccountRepository.cs @@ -27,6 +27,16 @@ public class PostgreSqlAccountRepository : IAccountRepository { _context.Accounts.Remove(accountEntity); _context.SaveChanges(); + + // Invalidate all relevant caches for this account + var nameCacheKey = $"account_{name}"; + var keyCacheKey = $"account_key_{accountEntity.Key}"; + var idCacheKey = $"account_id_{accountEntity.Id}"; + + _cacheService.RemoveValue(nameCacheKey); + _cacheService.RemoveValue(keyCacheKey); + _cacheService.RemoveValue(idCacheKey); + _cacheService.RemoveValue("all_accounts"); } } @@ -34,6 +44,14 @@ public class PostgreSqlAccountRepository : IAccountRepository { try { + // Check cache first for account by key + var cacheKey = $"account_key_{key}"; + var cachedAccount = _cacheService.GetValue(cacheKey); + if (cachedAccount != null) + { + return cachedAccount; + } + await PostgreSqlConnectionHelper.EnsureConnectionOpenAsync(_context); var accountEntity = await _context.Accounts @@ -42,7 +60,12 @@ public class PostgreSqlAccountRepository : IAccountRepository .FirstOrDefaultAsync(a => a.Key == key) .ConfigureAwait(false); - return PostgreSqlMappers.Map(accountEntity); + var account = PostgreSqlMappers.Map(accountEntity); + + // Cache account for 1 hour since accounts rarely change + _cacheService.SaveValue(cacheKey, account, TimeSpan.FromHours(1)); + + return account; } finally { @@ -86,6 +109,14 @@ public class PostgreSqlAccountRepository : IAccountRepository { try { + // Check cache first for account by ID + var cacheKey = $"account_id_{id}"; + var cachedAccount = _cacheService.GetValue(cacheKey); + if (cachedAccount != null) + { + return cachedAccount; + } + await PostgreSqlConnectionHelper.EnsureConnectionOpenAsync(_context); var accountEntity = await _context.Accounts @@ -94,7 +125,12 @@ public class PostgreSqlAccountRepository : IAccountRepository .FirstOrDefaultAsync(a => a.Id == id) .ConfigureAwait(false); - return PostgreSqlMappers.Map(accountEntity); + var account = PostgreSqlMappers.Map(accountEntity); + + // Cache account for 1 hour since accounts rarely change + _cacheService.SaveValue(cacheKey, account, TimeSpan.FromHours(1)); + + return account; } finally { @@ -107,6 +143,14 @@ public class PostgreSqlAccountRepository : IAccountRepository { try { + // Check cache first for all accounts + var cacheKey = "all_accounts"; + var cachedAccounts = _cacheService.GetValue>(cacheKey); + if (cachedAccounts != null) + { + return cachedAccounts; + } + await PostgreSqlConnectionHelper.EnsureConnectionOpenAsync(_context); // Use proper async operations with AsNoTracking for optimal performance @@ -116,7 +160,12 @@ public class PostgreSqlAccountRepository : IAccountRepository .ToListAsync() .ConfigureAwait(false); - return PostgreSqlMappers.Map(accountEntities); + var accounts = PostgreSqlMappers.Map(accountEntities).ToList(); + + // Cache all accounts for 1 hour since accounts rarely change + _cacheService.SaveValue(cacheKey, accounts, TimeSpan.FromHours(1)); + + return accounts; } finally { @@ -137,6 +186,18 @@ public class PostgreSqlAccountRepository : IAccountRepository _context.Accounts.Add(accountEntity); await _context.SaveChangesAsync().ConfigureAwait(false); + + // Invalidate all accounts cache since we added a new account + _cacheService.RemoveValue("all_accounts"); + + // Cache the new account for future lookups + var nameCacheKey = $"account_{account.Name}"; + var keyCacheKey = $"account_key_{account.Key}"; + var idCacheKey = $"account_id_{account.Id}"; + + _cacheService.SaveValue(nameCacheKey, account, TimeSpan.FromHours(1)); + _cacheService.SaveValue(keyCacheKey, account, TimeSpan.FromHours(1)); + _cacheService.SaveValue(idCacheKey, account, TimeSpan.FromHours(1)); } public async Task UpdateAccountAsync(Account account) @@ -159,9 +220,15 @@ public class PostgreSqlAccountRepository : IAccountRepository existingEntity.IsGmxInitialized = account.IsGmxInitialized; await _context.SaveChangesAsync().ConfigureAwait(false); - // Clear cache for this account - var cacheKey = $"account_{account.Name}"; - _cacheService.RemoveValue(cacheKey); + // Invalidate all relevant caches for this account + var nameCacheKey = $"account_{account.Name}"; + var keyCacheKey = $"account_key_{account.Key}"; + var idCacheKey = $"account_id_{account.Id}"; + + _cacheService.RemoveValue(nameCacheKey); + _cacheService.RemoveValue(keyCacheKey); + _cacheService.RemoveValue(idCacheKey); + _cacheService.RemoveValue("all_accounts"); } finally { diff --git a/src/Managing.Infrastructure.Database/PostgreSql/PostgreSqlUserRepository.cs b/src/Managing.Infrastructure.Database/PostgreSql/PostgreSqlUserRepository.cs index 8ee16a40..2157b26e 100644 --- a/src/Managing.Infrastructure.Database/PostgreSql/PostgreSqlUserRepository.cs +++ b/src/Managing.Infrastructure.Database/PostgreSql/PostgreSqlUserRepository.cs @@ -118,16 +118,38 @@ public class PostgreSqlUserRepository : BaseRepositoryWithLogging, IUserReposito { return await ExecuteWithLoggingAsync(async () => { + // Check cache first for all users + var cacheKey = "all_users"; + var cachedUsers = _cacheService.GetValue>(cacheKey); + if (cachedUsers != null) + { + return cachedUsers; + } + try { await PostgreSqlConnectionHelper.EnsureConnectionOpenAsync(_context); + // Optimized query with explicit SELECT to avoid loading unnecessary data var userEntities = await _context.Users .AsNoTracking() + .Select(u => new UserEntity + { + Id = u.Id, + Name = u.Name, + AgentName = u.AgentName, + AvatarUrl = u.AvatarUrl, + TelegramChannel = u.TelegramChannel + }) .ToListAsync() .ConfigureAwait(false); - return userEntities.Select(PostgreSqlMappers.Map); + var users = userEntities.Select(PostgreSqlMappers.Map).ToList(); + + // Cache all users for 10 minutes since this data changes infrequently + _cacheService.SaveValue(cacheKey, users, TimeSpan.FromMinutes(10)); + + return users; } finally { @@ -182,6 +204,9 @@ public class PostgreSqlUserRepository : BaseRepositoryWithLogging, IUserReposito { _cacheService.SaveValue(newUserAgentCacheKey, user, TimeSpan.FromMinutes(5)); } + + // Invalidate all users cache since we added a new user + _cacheService.RemoveValue("all_users"); return; // Exit early since we already saved } @@ -204,6 +229,9 @@ public class PostgreSqlUserRepository : BaseRepositoryWithLogging, IUserReposito var newAgentCacheKey = $"user_agent_{user.AgentName}"; _cacheService.RemoveValue(newAgentCacheKey); } + + // Invalidate all users cache since we updated a user + _cacheService.RemoveValue("all_users"); } catch (Exception e) {