tercul-backend/internal/repositories/cached_work_repository.go
Damir Mukimov fa336cacf3
wip
2025-09-01 00:43:59 +02:00

287 lines
7.5 KiB
Go

package repositories
import (
"context"
"tercul/internal/models"
"time"
"tercul/internal/platform/cache"
"tercul/internal/platform/log"
)
// CachedWorkRepository wraps a WorkRepository with caching functionality
type CachedWorkRepository struct {
*CachedRepository[models.Work]
workRepo WorkRepository
}
// NewCachedWorkRepository creates a new CachedWorkRepository
func NewCachedWorkRepository(
workRepo WorkRepository,
cache cache.Cache,
keyGenerator cache.KeyGenerator,
cacheExpiry time.Duration,
) *CachedWorkRepository {
if keyGenerator == nil {
keyGenerator = &simpleKeyGenerator{prefix: "tercul:"}
}
if cacheExpiry == 0 {
cacheExpiry = 30 * time.Minute // Default expiry of 30 minutes
}
return &CachedWorkRepository{
CachedRepository: NewCachedRepository[models.Work](
workRepo,
cache,
keyGenerator,
"work",
cacheExpiry,
),
workRepo: workRepo,
}
}
// FindByTitle finds works by title (partial match)
func (r *CachedWorkRepository) FindByTitle(ctx context.Context, title string) ([]models.Work, error) {
if !r.cacheEnabled {
return r.workRepo.FindByTitle(ctx, title)
}
cacheKey := r.keyGenerator.QueryKey(r.entityType, "title", title)
var result []models.Work
err := r.cache.Get(ctx, cacheKey, &result)
if err == nil {
// Cache hit
log.LogDebug("Cache hit for FindByTitle",
log.F("entityType", r.entityType),
log.F("title", title))
return result, nil
}
// Cache miss, get from database
log.LogDebug("Cache miss for FindByTitle",
log.F("entityType", r.entityType),
log.F("title", title))
result, err = r.workRepo.FindByTitle(ctx, title)
if err != nil {
return nil, err
}
// Store in cache
if err := r.cache.Set(ctx, cacheKey, result, r.cacheExpiry); err != nil {
log.LogWarn("Failed to cache FindByTitle result",
log.F("entityType", r.entityType),
log.F("title", title),
log.F("error", err))
}
return result, nil
}
// FindByAuthor finds works by author ID
func (r *CachedWorkRepository) FindByAuthor(ctx context.Context, authorID uint) ([]models.Work, error) {
if !r.cacheEnabled {
return r.workRepo.FindByAuthor(ctx, authorID)
}
cacheKey := r.keyGenerator.QueryKey(r.entityType, "author", authorID)
var result []models.Work
err := r.cache.Get(ctx, cacheKey, &result)
if err == nil {
// Cache hit
log.LogDebug("Cache hit for FindByAuthor",
log.F("entityType", r.entityType),
log.F("authorID", authorID))
return result, nil
}
// Cache miss, get from database
log.LogDebug("Cache miss for FindByAuthor",
log.F("entityType", r.entityType),
log.F("authorID", authorID))
result, err = r.workRepo.FindByAuthor(ctx, authorID)
if err != nil {
return nil, err
}
// Store in cache
if err := r.cache.Set(ctx, cacheKey, result, r.cacheExpiry); err != nil {
log.LogWarn("Failed to cache FindByAuthor result",
log.F("entityType", r.entityType),
log.F("authorID", authorID),
log.F("error", err))
}
return result, nil
}
// FindByCategory finds works by category ID
func (r *CachedWorkRepository) FindByCategory(ctx context.Context, categoryID uint) ([]models.Work, error) {
if !r.cacheEnabled {
return r.workRepo.FindByCategory(ctx, categoryID)
}
cacheKey := r.keyGenerator.QueryKey(r.entityType, "category", categoryID)
var result []models.Work
err := r.cache.Get(ctx, cacheKey, &result)
if err == nil {
// Cache hit
log.LogDebug("Cache hit for FindByCategory",
log.F("entityType", r.entityType),
log.F("categoryID", categoryID))
return result, nil
}
// Cache miss, get from database
log.LogDebug("Cache miss for FindByCategory",
log.F("entityType", r.entityType),
log.F("categoryID", categoryID))
result, err = r.workRepo.FindByCategory(ctx, categoryID)
if err != nil {
return nil, err
}
// Store in cache
if err := r.cache.Set(ctx, cacheKey, result, r.cacheExpiry); err != nil {
log.LogWarn("Failed to cache FindByCategory result",
log.F("entityType", r.entityType),
log.F("categoryID", categoryID),
log.F("error", err))
}
return result, nil
}
// FindByLanguage finds works by language with pagination
func (r *CachedWorkRepository) FindByLanguage(ctx context.Context, language string, page, pageSize int) (*PaginatedResult[models.Work], error) {
if !r.cacheEnabled {
return r.workRepo.FindByLanguage(ctx, language, page, pageSize)
}
cacheKey := r.keyGenerator.QueryKey(r.entityType, "language", language, page, pageSize)
var result PaginatedResult[models.Work]
err := r.cache.Get(ctx, cacheKey, &result)
if err == nil {
// Cache hit
log.LogDebug("Cache hit for FindByLanguage",
log.F("entityType", r.entityType),
log.F("language", language),
log.F("page", page),
log.F("pageSize", pageSize))
return &result, nil
}
// Cache miss, get from database
log.LogDebug("Cache miss for FindByLanguage",
log.F("entityType", r.entityType),
log.F("language", language),
log.F("page", page),
log.F("pageSize", pageSize))
result_ptr, err := r.workRepo.FindByLanguage(ctx, language, page, pageSize)
if err != nil {
return nil, err
}
// Store in cache
if err := r.cache.Set(ctx, cacheKey, result_ptr, r.cacheExpiry); err != nil {
log.LogWarn("Failed to cache FindByLanguage result",
log.F("entityType", r.entityType),
log.F("language", language),
log.F("page", page),
log.F("pageSize", pageSize),
log.F("error", err))
}
return result_ptr, nil
}
// GetWithTranslations gets a work with its translations
func (r *CachedWorkRepository) GetWithTranslations(ctx context.Context, id uint) (*models.Work, error) {
if !r.cacheEnabled {
return r.workRepo.GetWithTranslations(ctx, id)
}
cacheKey := r.keyGenerator.QueryKey(r.entityType, "with_translations", id)
var result models.Work
err := r.cache.Get(ctx, cacheKey, &result)
if err == nil {
// Cache hit
log.LogDebug("Cache hit for GetWithTranslations",
log.F("entityType", r.entityType),
log.F("id", id))
return &result, nil
}
// Cache miss, get from database
log.LogDebug("Cache miss for GetWithTranslations",
log.F("entityType", r.entityType),
log.F("id", id))
result_ptr, err := r.workRepo.GetWithTranslations(ctx, id)
if err != nil {
return nil, err
}
// Store in cache
if err := r.cache.Set(ctx, cacheKey, result_ptr, r.cacheExpiry); err != nil {
log.LogWarn("Failed to cache GetWithTranslations result",
log.F("entityType", r.entityType),
log.F("id", id),
log.F("error", err))
}
return result_ptr, nil
}
// ListWithTranslations lists works with their translations
func (r *CachedWorkRepository) ListWithTranslations(ctx context.Context, page, pageSize int) (*PaginatedResult[models.Work], error) {
if !r.cacheEnabled {
return r.workRepo.ListWithTranslations(ctx, page, pageSize)
}
cacheKey := r.keyGenerator.QueryKey(r.entityType, "list_with_translations", page, pageSize)
var result PaginatedResult[models.Work]
err := r.cache.Get(ctx, cacheKey, &result)
if err == nil {
// Cache hit
log.LogDebug("Cache hit for ListWithTranslations",
log.F("entityType", r.entityType),
log.F("page", page),
log.F("pageSize", pageSize))
return &result, nil
}
// Cache miss, get from database
log.LogDebug("Cache miss for ListWithTranslations",
log.F("entityType", r.entityType),
log.F("page", page),
log.F("pageSize", pageSize))
result_ptr, err := r.workRepo.ListWithTranslations(ctx, page, pageSize)
if err != nil {
return nil, err
}
// Store in cache
if err := r.cache.Set(ctx, cacheKey, result_ptr, r.cacheExpiry); err != nil {
log.LogWarn("Failed to cache ListWithTranslations result",
log.F("entityType", r.entityType),
log.F("page", page),
log.F("pageSize", pageSize),
log.F("error", err))
}
return result_ptr, nil
}