mirror of
https://github.com/git-pkgs/proxy.git
synced 2026-11-09 15:49:16 -05:00
* max_size now counts cached metadata as well as artifacts, and eviction drops whichever was used least recently, so metadata no longer piles up for good (#461). Metadata rows record when they were last served, an artifact not yet served counts from its download, and Arch stores a database blob again if eviction removed it under a refreshed row. * Shorter comments on the metadata last-access migration. * Show evicted metadata on the analytics page next to evicted artifacts, and cover the new counter in the metric surface test. * Evicting metadata now drops the row and queues its path in one transaction, so a crash before the blob is deleted leaves it to reclaim instead of orphaning it. * The last-access backfill now runs in the background in batches of 1000 instead of inside the migration, resuming after a restart, and until a row is backfilled eviction orders it by its write time. --------- Co-authored-by: Andrew Nesbitt <andrewnez@gmail.com>
62 lines
2.1 KiB
Go
62 lines
2.1 KiB
Go
package handler
|
|
|
|
import (
|
|
"bytes"
|
|
"context"
|
|
"database/sql"
|
|
"fmt"
|
|
"time"
|
|
|
|
"github.com/git-pkgs/proxy/internal/database"
|
|
)
|
|
|
|
func (h *ContainerHandler) storeContainerMetadata(ctx context.Context, ecosystem, cacheKey string, body []byte, etag, link, contentType, contentDigest string, lastModified, fetchedAt time.Time) (int64, error) {
|
|
if h.proxy.DB == nil || h.proxy.Storage == nil {
|
|
return int64(len(body)), nil
|
|
}
|
|
|
|
storagePath := metadataStoragePath(ecosystem, cacheKey)
|
|
size, _, err := h.proxy.Storage.Store(ctx, storagePath, bytes.NewReader(body))
|
|
if err != nil {
|
|
return 0, fmt.Errorf("storing metadata: %w", err)
|
|
}
|
|
err = h.proxy.DB.UpsertMetadataCache(&database.MetadataCacheEntry{
|
|
Ecosystem: ecosystem,
|
|
Name: cacheKey,
|
|
StoragePath: storagePath,
|
|
ETag: sql.NullString{String: etag, Valid: etag != ""},
|
|
Link: sql.NullString{String: link, Valid: link != ""},
|
|
ContentType: sql.NullString{String: contentType, Valid: contentType != ""},
|
|
ContentDigest: sql.NullString{String: contentDigest, Valid: contentDigest != ""},
|
|
Size: sql.NullInt64{Int64: size, Valid: true},
|
|
LastModified: sql.NullTime{Time: lastModified, Valid: !lastModified.IsZero()},
|
|
FetchedAt: sql.NullTime{Time: fetchedAt, Valid: !fetchedAt.IsZero()},
|
|
})
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
return size, nil
|
|
}
|
|
|
|
// loadContainerMetadata returns a cached metadata row and its body, or nil
|
|
// when nothing usable is cached.
|
|
func (h *ContainerHandler) loadContainerMetadata(ctx context.Context, ecosystem, cacheKey string) (*database.MetadataCacheEntry, []byte, error) {
|
|
if h.proxy.DB == nil || h.proxy.Storage == nil {
|
|
return nil, nil, nil
|
|
}
|
|
entry, err := h.proxy.DB.GetMetadataCache(ecosystem, cacheKey)
|
|
if err != nil || entry == nil {
|
|
return nil, nil, err
|
|
}
|
|
reader, err := h.proxy.Storage.Open(ctx, entry.StoragePath)
|
|
if err != nil {
|
|
return nil, nil, nil
|
|
}
|
|
defer func() { _ = reader.Close() }()
|
|
body, err := h.proxy.ReadMetadata(reader)
|
|
if err != nil {
|
|
return nil, nil, err
|
|
}
|
|
h.proxy.recordMetadataRead(entry)
|
|
return entry, body, nil
|
|
}
|