Files
RenewCMS/internal/infrastructure/useCases/GetVariantCacheStats.go
T

171 lines
4.8 KiB
Go

package useCases
import (
"RenewCMS/internal/domain/image"
"sort"
"time"
)
type VariantCacheEntry struct {
Name string
Enabled bool
Files int
Bytes int64
}
type VariantCacheImage struct {
ID uint32
Filename string
URL string
Files int
Bytes int64
}
type VariantCacheStats struct {
GeneratedFiles int
DiskUsed int64
VariantsDefined int
VariantsEnabled int
VariantsDisabled int
RejectedRequests int
RejectedWindowDays int
Unused image.VariantFileTotals
UnusedDays int
ByVariant []VariantCacheEntry
TopImages []VariantCacheImage
}
type GetVariantCacheStatsUseCase struct {
variants image.VariantRepository
files image.VariantFileRepository
cache image.VariantCacheRepository
images image.Repository
usage image.VariantUsageRepository
tracker image.VariantUsageTracker
now func() time.Time
}
func NewGetVariantCacheStatsUseCase(variants image.VariantRepository, files image.VariantFileRepository, cache image.VariantCacheRepository, images image.Repository, usage image.VariantUsageRepository, tracker image.VariantUsageTracker, now func() time.Time) *GetVariantCacheStatsUseCase {
return &GetVariantCacheStatsUseCase{variants, files, cache, images, usage, tracker, now}
}
func (u *GetVariantCacheStatsUseCase) Get() (VariantCacheStats, error) {
_ = u.tracker.Flush()
variants, err := u.variants.List()
if err != nil {
return VariantCacheStats{}, err
}
totals, err := u.files.Totals()
if err != nil {
return VariantCacheStats{}, err
}
now := u.now()
unused, err := u.cache.UnusedTotals(image.UnusedFilter{VariantNames: enabledNames(variants), Cutoff: image.UnusedCutoff(now)})
if err != nil {
return VariantCacheStats{}, err
}
rejected, err := u.usage.RejectedSince(image.RejectionWindowStart(now))
if err != nil {
return VariantCacheStats{}, err
}
top, err := u.topImages()
if err != nil {
return VariantCacheStats{}, err
}
stats := VariantCacheStats{
VariantsDefined: len(variants),
RejectedRequests: rejected,
RejectedWindowDays: image.RejectionWindowDays,
Unused: unused,
UnusedDays: image.UnusedVariantDays,
ByVariant: cacheEntries(variants, totals),
TopImages: top,
}
for _, entry := range stats.ByVariant {
stats.GeneratedFiles += entry.Files
stats.DiskUsed += entry.Bytes
if entry.Enabled {
stats.VariantsEnabled++
}
}
stats.VariantsDisabled = stats.VariantsDefined - stats.VariantsEnabled
return stats, nil
}
func enabledNames(variants []image.Variant) []string {
names := []string{}
for _, variant := range variants {
if variant.Enabled {
names = append(names, variant.Name)
}
}
return names
}
func cacheEntries(variants []image.Variant, totals map[string]image.VariantFileTotals) []VariantCacheEntry {
entries := make([]VariantCacheEntry, 0, len(variants))
for _, variant := range variants {
own := totals[variant.Name]
entries = append(entries, VariantCacheEntry{Name: variant.Name, Enabled: variant.Enabled, Files: own.Files, Bytes: own.Bytes})
}
sort.Slice(entries, func(i, j int) bool {
if entries[i].Bytes != entries[j].Bytes {
return entries[i].Bytes > entries[j].Bytes
}
return entries[i].Name < entries[j].Name
})
return entries
}
func (u *GetVariantCacheStatsUseCase) topImages() ([]VariantCacheImage, error) {
limit := image.TopImagesLimit + 1
for {
sources, err := u.cache.TopSources(limit)
if err != nil {
return nil, err
}
ranked, err := u.rank(sources)
if err != nil {
return nil, err
}
if len(sources) < limit || topIsSettled(ranked, sources) {
return ranked[:min(len(ranked), image.TopImagesLimit)], nil
}
limit *= 2
}
}
func topIsSettled(ranked []VariantCacheImage, sources []image.SourceTotals) bool {
return len(ranked) >= image.TopImagesLimit && ranked[image.TopImagesLimit-1].Bytes > sources[len(sources)-1].Bytes
}
func (u *GetVariantCacheStatsUseCase) rank(sources []image.SourceTotals) ([]VariantCacheImage, error) {
names := make([]string, 0, len(sources))
for _, source := range sources {
names = append(names, source.SourceName)
}
found, err := u.images.ListByStoredNames(names)
if err != nil {
return nil, err
}
byName := make(map[string]image.Image, len(found))
for _, img := range found {
byName[image.StoredName(img.Path)] = img
}
ranked := make([]VariantCacheImage, 0, len(sources))
for _, source := range sources {
if img, known := byName[source.SourceName]; known {
ranked = append(ranked, VariantCacheImage{ID: img.ID, Filename: img.Filename, URL: image.PublicPath(source.SourceName), Files: source.Files, Bytes: source.Bytes})
}
}
sort.SliceStable(ranked, func(i, j int) bool {
if ranked[i].Bytes != ranked[j].Bytes {
return ranked[i].Bytes > ranked[j].Bytes
}
return ranked[i].ID < ranked[j].ID
})
return ranked, nil
}