Add cache for user agent/name

This commit is contained in:
2025-10-10 03:03:41 +07:00
parent 3128e3e9d9
commit ba3b0f6232
5 changed files with 1596 additions and 7 deletions

View File

@@ -1,5 +1,7 @@
using Managing.Application.Abstractions.Repositories;
using Managing.Application.Abstractions.Services;
using Managing.Domain.Users;
using Managing.Infrastructure.Databases.PostgreSql.Entities;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging;
@@ -7,29 +9,58 @@ namespace Managing.Infrastructure.Databases.PostgreSql;
public class PostgreSqlUserRepository : BaseRepositoryWithLogging, IUserRepository
{
public PostgreSqlUserRepository(ManagingDbContext context, ILogger<SqlQueryLogger> logger, SentrySqlMonitoringService sentryMonitoringService)
private readonly ICacheService _cacheService;
public PostgreSqlUserRepository(ManagingDbContext context, ILogger<SqlQueryLogger> logger,
SentrySqlMonitoringService sentryMonitoringService, ICacheService cacheService)
: base(context, logger, sentryMonitoringService)
{
_cacheService = cacheService;
}
public async Task<User> GetUserByAgentNameAsync(string agentName)
{
return await ExecuteWithLoggingAsync(async () =>
{
// Check cache first for frequently accessed users
var cacheKey = $"user_agent_{agentName}";
var cachedUser = _cacheService.GetValue<User>(cacheKey);
if (cachedUser != null)
{
return cachedUser;
}
try
{
await PostgreSqlConnectionHelper.EnsureConnectionOpenAsync(_context);
// Optimized query with explicit SELECT to avoid loading unnecessary data
var userEntity = await _context.Users
.AsNoTracking()
.FirstOrDefaultAsync(u => u.AgentName == agentName)
.Where(u => u.AgentName == agentName)
.Select(u => new UserEntity
{
Id = u.Id,
Name = u.Name,
AgentName = u.AgentName,
AvatarUrl = u.AvatarUrl,
TelegramChannel = u.TelegramChannel
})
.FirstOrDefaultAsync()
.ConfigureAwait(false);
return PostgreSqlMappers.Map(userEntity ?? throw new InvalidOperationException("User not found"));
if (userEntity == null)
throw new InvalidOperationException($"User with agent name '{agentName}' not found");
var user = PostgreSqlMappers.Map(userEntity);
// Cache user for 5 minutes since user data doesn't change frequently
_cacheService.SaveValue(cacheKey, user, TimeSpan.FromMinutes(5));
return user;
}
finally
{
// Always ensure the connection is closed after the operation
await PostgreSqlConnectionHelper.SafeCloseConnectionAsync(_context);
}
}, nameof(GetUserByAgentNameAsync), ("agentName", agentName));
@@ -39,20 +70,45 @@ public class PostgreSqlUserRepository : BaseRepositoryWithLogging, IUserReposito
{
return await ExecuteWithLoggingAsync(async () =>
{
// Check cache first for frequently accessed users
var cacheKey = $"user_name_{name}";
var cachedUser = _cacheService.GetValue<User>(cacheKey);
if (cachedUser != null)
{
return cachedUser;
}
try
{
await PostgreSqlConnectionHelper.EnsureConnectionOpenAsync(_context);
// Optimized query with explicit SELECT to avoid loading unnecessary data
var userEntity = await _context.Users
.AsNoTracking()
.FirstOrDefaultAsync(u => u.Name == name)
.Where(u => u.Name == name)
.Select(u => new UserEntity
{
Id = u.Id,
Name = u.Name,
AgentName = u.AgentName,
AvatarUrl = u.AvatarUrl,
TelegramChannel = u.TelegramChannel
})
.FirstOrDefaultAsync()
.ConfigureAwait(false);
return PostgreSqlMappers.Map(userEntity ?? throw new InvalidOperationException("User not found"));
if (userEntity == null)
throw new InvalidOperationException($"User with name '{name}' not found");
var user = PostgreSqlMappers.Map(userEntity);
// Cache user for 5 minutes since user data doesn't change frequently
_cacheService.SaveValue(cacheKey, user, TimeSpan.FromMinutes(5));
return user;
}
finally
{
// Always ensure the connection is closed after the operation
await PostgreSqlConnectionHelper.SafeCloseConnectionAsync(_context);
}
}, nameof(GetUserByNameAsync), ("name", name));
@@ -92,8 +148,12 @@ public class PostgreSqlUserRepository : BaseRepositoryWithLogging, IUserReposito
.FirstOrDefaultAsync(u => u.Name == user.Name)
.ConfigureAwait(false);
string? oldAgentName = null;
if (existingUser != null)
{
// Capture old AgentName before updating for cache invalidation
oldAgentName = existingUser.AgentName;
// Update existing user
existingUser.AgentName = user.AgentName;
existingUser.AvatarUrl = user.AvatarUrl;
@@ -113,10 +173,37 @@ public class PostgreSqlUserRepository : BaseRepositoryWithLogging, IUserReposito
// Update the user object with the database-generated ID after save
await _context.SaveChangesAsync().ConfigureAwait(false);
user.Id = userEntity.Id;
// Cache the new user
var newUserNameCacheKey = $"user_name_{user.Name}";
var newUserAgentCacheKey = $"user_agent_{user.AgentName}";
_cacheService.SaveValue(newUserNameCacheKey, user, TimeSpan.FromMinutes(5));
if (!string.IsNullOrEmpty(user.AgentName))
{
_cacheService.SaveValue(newUserAgentCacheKey, user, TimeSpan.FromMinutes(5));
}
return; // Exit early since we already saved
}
await _context.SaveChangesAsync().ConfigureAwait(false);
// Invalidate cache for updated user - handle both old and new AgentName
var nameCacheKey = $"user_name_{user.Name}";
_cacheService.RemoveValue(nameCacheKey);
// Invalidate old AgentName cache if it existed
if (!string.IsNullOrEmpty(oldAgentName))
{
var oldAgentCacheKey = $"user_agent_{oldAgentName}";
_cacheService.RemoveValue(oldAgentCacheKey);
}
// Invalidate new AgentName cache if it exists
if (!string.IsNullOrEmpty(user.AgentName))
{
var newAgentCacheKey = $"user_agent_{user.AgentName}";
_cacheService.RemoveValue(newAgentCacheKey);
}
}
catch (Exception e)
{