feat(kol): cache marketplace and generation reads with singleflight

Add cache-aside + singleflight to KOL marketplace list/detail, my-subscription
prompts, and generation schema lookups. Invalidate the shared public-facing
caches when subscriptions are added/revoked (ops) or KOL profile is updated.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-05-24 11:33:17 +08:00
parent 09c8fe1e56
commit 8761e47f78
10 changed files with 339 additions and 102 deletions
@@ -5,6 +5,7 @@ import (
"time"
"github.com/geo-platform/tenant-api/internal/shared/auth"
sharedcache "github.com/geo-platform/tenant-api/internal/shared/cache"
"github.com/geo-platform/tenant-api/internal/shared/objectstorage"
"github.com/geo-platform/tenant-api/internal/shared/response"
"github.com/geo-platform/tenant-api/internal/tenant/repository"
@@ -15,6 +16,7 @@ var ErrNotActiveKol = response.ErrForbidden(40341, "kol_not_active", "user is no
type KolProfileService struct {
repo repository.KolProfileRepository
objectStorage objectstorage.Client
cache sharedcache.Cache
}
type UpdateKolProfileServiceInput struct {
@@ -45,6 +47,11 @@ func (s *KolProfileService) WithObjectStorage(client objectstorage.Client) *KolP
return s
}
func (s *KolProfileService) WithCache(c sharedcache.Cache) *KolProfileService {
s.cache = c
return s
}
func (s *KolProfileService) RequireActiveKol(ctx context.Context, actor auth.Actor) (*repository.KolProfile, error) {
profile, err := s.repo.GetByUser(ctx, actor.TenantID, actor.UserID)
if err != nil {
@@ -91,6 +98,7 @@ func (s *KolProfileService) UpdateProfile(ctx context.Context, actor auth.Actor,
if updated == nil {
return nil, ErrNotActiveKol
}
InvalidateKolPublicCaches(ctx, s.cache)
return updated, nil
}