Files
mcr/internal/db/gc.go
Kyle Isom d5580f01f2 Migrate module path from kyle/ to mc/ org
All import paths updated to git.wntrmute.dev/mc/. Bumps mcdsl to v1.2.0.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-27 02:05:59 -07:00

78 lines
2.0 KiB
Go

package db
import (
"database/sql"
"errors"
"fmt"
"git.wntrmute.dev/mc/mcr/internal/gc"
)
// FindAndDeleteUnreferencedBlobs finds all blob rows with no manifest_blobs
// entries, deletes them in a single transaction, and returns the digests
// and sizes of the deleted blobs.
func (d *DB) FindAndDeleteUnreferencedBlobs() ([]gc.UnreferencedBlob, error) {
tx, err := d.Begin()
if err != nil {
return nil, fmt.Errorf("db: begin gc transaction: %w", err)
}
// Find unreferenced blobs.
rows, err := tx.Query(
`SELECT b.id, b.digest, b.size FROM blobs b
LEFT JOIN manifest_blobs mb ON mb.blob_id = b.id
WHERE mb.manifest_id IS NULL`,
)
if err != nil {
_ = tx.Rollback()
return nil, fmt.Errorf("db: find unreferenced blobs: %w", err)
}
var unreferenced []gc.UnreferencedBlob
var ids []int64
for rows.Next() {
var id int64
var blob gc.UnreferencedBlob
if err := rows.Scan(&id, &blob.Digest, &blob.Size); err != nil {
_ = rows.Close()
_ = tx.Rollback()
return nil, fmt.Errorf("db: scan unreferenced blob: %w", err)
}
unreferenced = append(unreferenced, blob)
ids = append(ids, id)
}
if err := rows.Err(); err != nil {
_ = rows.Close()
_ = tx.Rollback()
return nil, fmt.Errorf("db: iterate unreferenced blobs: %w", err)
}
_ = rows.Close()
// Delete the unreferenced blob rows.
for _, id := range ids {
if _, err := tx.Exec(`DELETE FROM blobs WHERE id = ?`, id); err != nil {
_ = tx.Rollback()
return nil, fmt.Errorf("db: delete blob %d: %w", id, err)
}
}
if err := tx.Commit(); err != nil {
return nil, fmt.Errorf("db: commit gc transaction: %w", err)
}
return unreferenced, nil
}
// BlobExistsByDigest checks whether a blob row exists for the given digest.
func (d *DB) BlobExistsByDigest(digest string) (bool, error) {
var count int
err := d.QueryRow(`SELECT COUNT(*) FROM blobs WHERE digest = ?`, digest).Scan(&count)
if err != nil {
if errors.Is(err, sql.ErrNoRows) {
return false, nil
}
return false, fmt.Errorf("db: blob exists by digest: %w", err)
}
return count > 0, nil
}