blob: f25cda1c3cfae2ad98c6322d68bd67ae57d763f4 [file] [log] [blame]
// Copyright 2021 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Functions for cleaning the database of unwanted module versions.
package postgres
import (
"context"
"database/sql"
"golang.org/x/pkgsite/internal/derrors"
)
// A ModuleVersion holds a module path and version.
type ModuleVersion struct {
ModulePath string
Version string
}
func (mv ModuleVersion) String() string {
return mv.ModulePath + "@" + mv.Version
}
// GetModuleVersionsToClean returns module versions that can be removed from the database.
// Only module versions that were updated more than daysOld days ago will be considered.
// At most limit module versions will be returned.
func (db *DB) GetModuleVersionsToClean(ctx context.Context, daysOld, limit int) (modvers []ModuleVersion, err error) {
defer derrors.WrapStack(&err, "GetModuleVersionsToClean(%d, %d)", daysOld, limit)
// Get all pseudo-versions that were added before the given number of days.
// Then remove:
// - The ones that are the latest versions for their module,
// - The ones in search_documents (since the latest version of a package might be at an older version),
// - The ones that the master or main branch resolves to.
query := `
SELECT module_path, version
FROM modules
WHERE version_type = 'pseudo'
AND CURRENT_TIMESTAMP - updated_at > make_interval(days => $1)
EXCEPT (
SELECT p.path, l.good_version
FROM latest_module_versions l
INNER JOIN paths p ON p.id = l.module_path_id
WHERE good_version != ''
)
EXCEPT (
SELECT module_path, version
FROM search_documents
)
EXCEPT (
SELECT module_path, resolved_version
FROM version_map
WHERE requested_version IN ('master', 'main')
)
LIMIT $2
`
err = db.db.RunQuery(ctx, query, func(rows *sql.Rows) error {
var mv ModuleVersion
if err := rows.Scan(&mv.ModulePath, &mv.Version); err != nil {
return err
}
modvers = append(modvers, mv)
return nil
}, daysOld, limit)
if err != nil {
return nil, err
}
return modvers, nil
}
// CleanModuleVersions deletes each module version from the DB and marks it as cleaned
// in module_version_states.
func (db *DB) CleanModuleVersions(ctx context.Context, mvs []ModuleVersion, reason string) (err error) {
defer derrors.Wrap(&err, "CleanModuleVersions(%d modules)", len(mvs))
status := derrors.ToStatus(derrors.Cleaned)
for _, mv := range mvs {
if err := db.UpdateModuleVersionStatus(ctx, mv.ModulePath, mv.Version, status, reason); err != nil {
return err
}
if err := db.DeleteModule(ctx, mv.ModulePath, mv.Version); err != nil {
return err
}
}
return nil
}
// CleanModule deletes all versions of the given module path from the DB and marks them
// as cleaned in module_version_states.
func (db *DB) CleanModule(ctx context.Context, modulePath, reason string) (err error) {
defer derrors.Wrap(&err, "CleanModule(%q)", modulePath)
var mvs []ModuleVersion
err = db.db.RunQuery(ctx, `
SELECT version
FROM modules
WHERE module_path = $1
`, func(rows *sql.Rows) error {
var v string
if err := rows.Scan(&v); err != nil {
return err
}
mvs = append(mvs, ModuleVersion{modulePath, v})
return nil
}, modulePath)
if err != nil {
return err
}
return db.CleanModuleVersions(ctx, mvs, reason)
}