Watch
1
0
Fork
You've already forked pkg-proxy
1
mirror of https://github.com/git-pkgs/proxy.git synced 2026-11-09 23:59:16 -05:00
pkg-proxy/internal/handler/metadata_dates.go

83 lines
2.9 KiB
Go
Raw Permalink Normal View History

package handler
import (
"time"
"github.com/git-pkgs/proxy/internal/database"
)
// metadataDateAttempts bounds how often metadataDate retries when other
// replicas keep changing a package's date between its read and its write.
const metadataDateAttempts = 3
// metadataDate returns the Last-Modified to serve a package's metadata with,
// as rewritten for proxyURL into the representation with the given ETag, or
// zero for none.
//
// The date is the second the package started serving that representation,
// recorded in the database so that every replica sharing it serves the same
// date. A switch to another representation is dated only in a second later
// than the recorded date, and then with that second, so a date is never given
// to two representations of a package: a client holding one never gets 304
// for another, whichever replica it asks. Until such a second the
// representation goes undated, and If-Modified-Since is not evaluated.
//
// A package's first date is likewise later than the second this process
// started, as the process it replaced may have dated the package then without
// recording it. A date ahead of this process's clock, recorded by a replica
// whose clock is ahead, is not served until the clock catches up. Without a
// database, or when it fails, nothing is dated.
func (p *Proxy) metadataDate(ecosystem, proxyURL, name, etag string) time.Time {
if p.DB == nil || etag == "" {
return time.Time{}
}
now := p.clock().Truncate(time.Second)
for range metadataDateAttempts {
date, settled, err := p.claimMetadataDate(ecosystem, proxyURL, name, etag, now)
if err != nil {
p.Logger.Warn("failed to date rewritten metadata", "ecosystem", ecosystem, "name", name, "error", err)
return time.Time{}
}
if settled {
if date.After(now) {
return time.Time{}
}
return date
}
}
return time.Time{}
}
// claimMetadataDate makes one attempt at recording the date of etag for a
// package at now. It reports false when another replica changed the date
// first, and the attempt should be made again.
func (p *Proxy) claimMetadataDate(ecosystem, proxyURL, name, etag string, now time.Time) (time.Time, bool, error) {
current, err := p.DB.GetMetadataDate(ecosystem, proxyURL, name)
if err != nil {
return time.Time{}, false, err
}
next := database.MetadataDate{ETag: etag, LastModified: now}
switch {
case current == nil:
if !now.After(p.started.Truncate(time.Second)) {
return time.Time{}, true, nil
}
inserted, err := p.DB.InsertMetadataDate(ecosystem, proxyURL, name, next)
return now, inserted, err
case current.ETag == etag:
return current.LastModified, true, nil
case !now.After(current.LastModified):
return time.Time{}, true, nil
default:
swapped, err := p.DB.SwapMetadataDate(ecosystem, proxyURL, name, *current, next)
return now, swapped, err
}
}
// clock reads the time metadata is dated by.
func (p *Proxy) clock() time.Time {
if p.now == nil {
return time.Now()
}
return p.now()
}