mirror of
https://github.com/git-pkgs/proxy.git
synced 2026-11-09 15:49:16 -05:00
77 lines
2.7 KiB
Go
77 lines
2.7 KiB
Go
|
|
package database
|
||
|
|
|
||
|
|
import (
|
||
|
|
"database/sql"
|
||
|
|
"errors"
|
||
|
|
"fmt"
|
||
|
|
"time"
|
||
|
|
)
|
||
|
|
|
||
|
|
// MetadataDate is the Last-Modified a package's rewritten metadata is served
|
||
|
|
// with, and the ETag of the rewrite it dates.
|
||
|
|
//
|
||
|
|
// Rewritten metadata has no date of its own, so the proxy assigns one when a
|
||
|
|
// package starts serving a new rewrite. Keeping it in the database rather than
|
||
|
|
// in process memory is what lets replicas sharing the database agree on it: a
|
||
|
|
// date one replica gave out is never given by another to a different rewrite.
|
||
|
|
type MetadataDate struct {
|
||
|
|
ETag string
|
||
|
|
LastModified time.Time
|
||
|
|
}
|
||
|
|
|
||
|
|
type metadataDateRow struct {
|
||
|
|
ETag string `db:"etag"`
|
||
|
|
LastModified int64 `db:"last_modified"`
|
||
|
|
}
|
||
|
|
|
||
|
|
// GetMetadataDate returns the date recorded for a package's metadata as
|
||
|
|
// rewritten for proxyURL, or nil when there is none.
|
||
|
|
func (db *DB) GetMetadataDate(ecosystem, proxyURL, name string) (*MetadataDate, error) {
|
||
|
|
var row metadataDateRow
|
||
|
|
err := db.Get(&row, db.Rebind(`
|
||
|
|
SELECT etag, last_modified FROM metadata_dates
|
||
|
|
WHERE ecosystem = ? AND proxy_url = ? AND name = ?
|
||
|
|
`), ecosystem, proxyURL, name)
|
||
|
|
if errors.Is(err, sql.ErrNoRows) {
|
||
|
|
return nil, nil
|
||
|
|
}
|
||
|
|
if err != nil {
|
||
|
|
return nil, fmt.Errorf("getting metadata date: %w", err)
|
||
|
|
}
|
||
|
|
return &MetadataDate{ETag: row.ETag, LastModified: time.Unix(row.LastModified, 0).UTC()}, nil
|
||
|
|
}
|
||
|
|
|
||
|
|
// InsertMetadataDate records the first date of a package's metadata and
|
||
|
|
// reports whether it did; it leaves a date another replica recorded first.
|
||
|
|
func (db *DB) InsertMetadataDate(ecosystem, proxyURL, name string, date MetadataDate) (bool, error) {
|
||
|
|
result, err := db.Exec(db.Rebind(`
|
||
|
|
INSERT INTO metadata_dates (ecosystem, proxy_url, name, etag, last_modified)
|
||
|
|
VALUES (?, ?, ?, ?, ?)
|
||
|
|
ON CONFLICT (ecosystem, proxy_url, name) DO NOTHING
|
||
|
|
`), ecosystem, proxyURL, name, date.ETag, date.LastModified.Unix())
|
||
|
|
if err != nil {
|
||
|
|
return false, fmt.Errorf("inserting metadata date: %w", err)
|
||
|
|
}
|
||
|
|
return affectedOne(result)
|
||
|
|
}
|
||
|
|
|
||
|
|
// SwapMetadataDate replaces a package's date with next if it is still old,
|
||
|
|
// and reports whether it did; it leaves a date another replica swapped in.
|
||
|
|
func (db *DB) SwapMetadataDate(ecosystem, proxyURL, name string, old, next MetadataDate) (bool, error) {
|
||
|
|
result, err := db.Exec(db.Rebind(`
|
||
|
|
UPDATE metadata_dates SET etag = ?, last_modified = ?
|
||
|
|
WHERE ecosystem = ? AND proxy_url = ? AND name = ? AND etag = ? AND last_modified = ?
|
||
|
|
`), next.ETag, next.LastModified.Unix(), ecosystem, proxyURL, name, old.ETag, old.LastModified.Unix())
|
||
|
|
if err != nil {
|
||
|
|
return false, fmt.Errorf("swapping metadata date: %w", err)
|
||
|
|
}
|
||
|
|
return affectedOne(result)
|
||
|
|
}
|
||
|
|
|
||
|
|
func affectedOne(result sql.Result) (bool, error) {
|
||
|
|
n, err := result.RowsAffected()
|
||
|
|
if err != nil {
|
||
|
|
return false, fmt.Errorf("counting affected rows: %w", err)
|
||
|
|
}
|
||
|
|
return n == 1, nil
|
||
|
|
}
|