Add cache for user agent/name
This commit is contained in:
@@ -99,6 +99,10 @@ public class ManagingDbContext : DbContext
|
||||
entity.Property(e => e.AgentName).HasMaxLength(255);
|
||||
entity.Property(e => e.AvatarUrl).HasMaxLength(500);
|
||||
entity.Property(e => e.TelegramChannel).HasMaxLength(255);
|
||||
|
||||
// Create indexes for performance
|
||||
entity.HasIndex(e => e.Name).IsUnique();
|
||||
entity.HasIndex(e => e.AgentName);
|
||||
});
|
||||
|
||||
// Configure GeneticRequest entity
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user