forked from mirrors/pkg-proxy
105 lines
2.2 KiB
Go
105 lines
2.2 KiB
Go
|
|
package server
|
||
|
|
|
||
|
|
import (
|
||
|
|
"context"
|
||
|
|
"log/slog"
|
||
|
|
"time"
|
||
|
|
|
||
|
|
"github.com/git-pkgs/proxy/internal/database"
|
||
|
|
"github.com/git-pkgs/proxy/internal/storage"
|
||
|
|
)
|
||
|
|
|
||
|
|
const (
|
||
|
|
evictionInterval = 1 * time.Minute
|
||
|
|
evictionBatch = 50
|
||
|
|
)
|
||
|
|
|
||
|
|
func (s *Server) startEvictionLoop(ctx context.Context) {
|
||
|
|
maxSize := s.cfg.ParseMaxSize()
|
||
|
|
if maxSize <= 0 {
|
||
|
|
return
|
||
|
|
}
|
||
|
|
|
||
|
|
s.logger.Info("cache eviction enabled", "max_size", s.cfg.Storage.MaxSize)
|
||
|
|
|
||
|
|
ticker := time.NewTicker(evictionInterval)
|
||
|
|
defer ticker.Stop()
|
||
|
|
|
||
|
|
s.runEviction(ctx, maxSize)
|
||
|
|
|
||
|
|
for {
|
||
|
|
select {
|
||
|
|
case <-ctx.Done():
|
||
|
|
return
|
||
|
|
case <-ticker.C:
|
||
|
|
s.runEviction(ctx, maxSize)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
func (s *Server) runEviction(ctx context.Context, maxSize int64) {
|
||
|
|
evictLRU(ctx, s.db, s.storage, s.logger, maxSize)
|
||
|
|
}
|
||
|
|
|
||
|
|
func evictLRU(ctx context.Context, db *database.DB, store storage.Storage, logger *slog.Logger, maxSize int64) {
|
||
|
|
totalSize, err := db.GetTotalCacheSize()
|
||
|
|
if err != nil {
|
||
|
|
logger.Warn("eviction: failed to get cache size", "error", err)
|
||
|
|
return
|
||
|
|
}
|
||
|
|
|
||
|
|
if totalSize <= maxSize {
|
||
|
|
return
|
||
|
|
}
|
||
|
|
|
||
|
|
logger.Info("eviction: cache size exceeds limit, evicting",
|
||
|
|
"current_size", totalSize, "max_size", maxSize)
|
||
|
|
|
||
|
|
evicted := 0
|
||
|
|
freedBytes := int64(0)
|
||
|
|
|
||
|
|
for totalSize-freedBytes > maxSize {
|
||
|
|
artifacts, err := db.GetLeastRecentlyUsedArtifacts(evictionBatch)
|
||
|
|
if err != nil {
|
||
|
|
logger.Warn("eviction: failed to get LRU artifacts", "error", err)
|
||
|
|
return
|
||
|
|
}
|
||
|
|
if len(artifacts) == 0 {
|
||
|
|
break
|
||
|
|
}
|
||
|
|
|
||
|
|
for _, art := range artifacts {
|
||
|
|
if totalSize-freedBytes <= maxSize {
|
||
|
|
break
|
||
|
|
}
|
||
|
|
|
||
|
|
if !art.StoragePath.Valid {
|
||
|
|
continue
|
||
|
|
}
|
||
|
|
|
||
|
|
if err := store.Delete(ctx, art.StoragePath.String); err != nil {
|
||
|
|
logger.Warn("eviction: failed to delete from storage",
|
||
|
|
"path", art.StoragePath.String, "error", err)
|
||
|
|
continue
|
||
|
|
}
|
||
|
|
|
||
|
|
if err := db.ClearArtifactCache(art.VersionPURL, art.Filename); err != nil {
|
||
|
|
logger.Warn("eviction: failed to clear artifact record",
|
||
|
|
"version_purl", art.VersionPURL, "filename", art.Filename, "error", err)
|
||
|
|
continue
|
||
|
|
}
|
||
|
|
|
||
|
|
size := int64(0)
|
||
|
|
if art.Size.Valid {
|
||
|
|
size = art.Size.Int64
|
||
|
|
}
|
||
|
|
freedBytes += size
|
||
|
|
evicted++
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
if evicted > 0 {
|
||
|
|
logger.Info("eviction: completed",
|
||
|
|
"evicted", evicted, "freed_bytes", freedBytes)
|
||
|
|
}
|
||
|
|
}
|