113 lines
2.7 KiB
Go
113 lines
2.7 KiB
Go
package processor
|
|
|
|
import (
|
|
"context"
|
|
"sync"
|
|
"time"
|
|
|
|
"eslogad-be/internal/contract"
|
|
"eslogad-be/internal/repository"
|
|
"eslogad-be/internal/transformer"
|
|
|
|
"github.com/google/uuid"
|
|
)
|
|
|
|
// CachedUserProcessor wraps UserProcessor with caching for frequently accessed users
|
|
type CachedUserProcessor struct {
|
|
userRepo *repository.UserRepositoryImpl
|
|
profileRepo *repository.UserProfileRepository
|
|
cache map[uuid.UUID]*cacheEntry
|
|
mu sync.RWMutex
|
|
ttl time.Duration
|
|
}
|
|
|
|
type cacheEntry struct {
|
|
user *contract.UserResponse
|
|
expiresAt time.Time
|
|
}
|
|
|
|
func NewCachedUserProcessor(userRepo *repository.UserRepositoryImpl, profileRepo *repository.UserProfileRepository) *CachedUserProcessor {
|
|
return &CachedUserProcessor{
|
|
userRepo: userRepo,
|
|
profileRepo: profileRepo,
|
|
cache: make(map[uuid.UUID]*cacheEntry),
|
|
ttl: 5 * time.Minute, // Cache for 5 minutes
|
|
}
|
|
}
|
|
|
|
func (p *CachedUserProcessor) GetUserByIDCached(ctx context.Context, id uuid.UUID) (*contract.UserResponse, error) {
|
|
p.mu.RLock()
|
|
if entry, exists := p.cache[id]; exists {
|
|
if entry.expiresAt.After(time.Now()) {
|
|
p.mu.RUnlock()
|
|
return entry.user, nil
|
|
}
|
|
}
|
|
p.mu.RUnlock()
|
|
|
|
// Not in cache or expired, fetch from database using the light method
|
|
user, err := p.userRepo.GetByIDLight(ctx, id)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// Convert to contract response
|
|
resp := &contract.UserResponse{
|
|
ID: user.ID,
|
|
Email: user.Email,
|
|
Name: user.Name,
|
|
IsActive: user.IsActive,
|
|
CreatedAt: user.CreatedAt,
|
|
UpdatedAt: user.UpdatedAt,
|
|
}
|
|
|
|
// Store in cache
|
|
p.mu.Lock()
|
|
p.cache[id] = &cacheEntry{
|
|
user: resp,
|
|
expiresAt: time.Now().Add(p.ttl),
|
|
}
|
|
p.mu.Unlock()
|
|
|
|
// Clean expired entries periodically
|
|
go p.cleanExpiredEntries()
|
|
|
|
return resp, nil
|
|
}
|
|
|
|
// GetUserByIDFull retrieves full user with all relationships - no caching
|
|
func (p *CachedUserProcessor) GetUserByIDFull(ctx context.Context, id uuid.UUID) (*contract.UserResponse, error) {
|
|
user, err := p.userRepo.GetByID(ctx, id)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
resp := transformer.EntityToContract(user)
|
|
if resp != nil {
|
|
if roles, err := p.userRepo.GetRolesByUserID(ctx, resp.ID); err == nil {
|
|
resp.Roles = transformer.RolesToContract(roles)
|
|
}
|
|
}
|
|
return resp, nil
|
|
}
|
|
|
|
// InvalidateCache removes a user from cache
|
|
func (p *CachedUserProcessor) InvalidateCache(userID uuid.UUID) {
|
|
p.mu.Lock()
|
|
delete(p.cache, userID)
|
|
p.mu.Unlock()
|
|
}
|
|
|
|
// cleanExpiredEntries removes expired cache entries
|
|
func (p *CachedUserProcessor) cleanExpiredEntries() {
|
|
p.mu.Lock()
|
|
defer p.mu.Unlock()
|
|
|
|
now := time.Now()
|
|
for id, entry := range p.cache {
|
|
if entry.expiresAt.Before(now) {
|
|
delete(p.cache, id)
|
|
}
|
|
}
|
|
}
|