Files
hakurei/internal/pkg/clean.go
Ophestra 6c2f7089b6
All checks were successful
Test / Create distribution (push) Successful in 1m28s
Test / Sandbox (push) Successful in 2m39s
Test / ShareFS (push) Successful in 3m39s
Test / Hakurei (push) Successful in 3m52s
Test / Sandbox (race detector) (push) Successful in 8m30s
Test / Hakurei (race detector) (push) Successful in 11m35s
Test / Flake checks (push) Successful in 8m54s
internal/pkg: destroy unreachable status entries
These must be destroyed alongside their corresponding identifier or substitute entries to avoid inconsistent state.

Signed-off-by: Ophestra <cat@gensokyo.uk>
2026-06-02 15:50:30 +09:00

153 lines
3.6 KiB
Go

package pkg
import (
"errors"
"os"
"unique"
)
// Clean destroys checksum backing entries without any identifier or substitute
// entry referring to it. If at least one keep [Artifact] is specified,
// identifier and substitute entries not kept alive by them are destroyed first.
func (c *Cache) Clean(dry, inputs bool, keep ...Artifact) (
[]unique.Handle[ID],
[]unique.Handle[Checksum],
error,
) {
c.identMu.Lock()
defer c.identMu.Unlock()
c.checksumMu.Lock()
defer c.checksumMu.Unlock()
dents, err := os.ReadDir(c.base.Append(dirChecksum).String())
if err != nil {
return nil, nil, err
}
checksums := make(map[unique.Handle[Checksum]]string, len(dents))
var buf Checksum
for _, dent := range dents {
name := dent.Name()
if err = Decode(&buf, name); err != nil {
return nil, nil, err
}
checksums[unique.Make(buf)] = name
}
type identPair struct {
id unique.Handle[ID]
name string
}
dents, err = os.ReadDir(c.base.Append(dirIdentifier).String())
if err != nil {
return nil, nil, err
}
keepIdents := make(map[unique.Handle[ID]]struct{})
if inputs {
for _, id := range Inputs((*Collect)(&keep)) {
keepIdents[id] = struct{}{}
}
} else {
for _, a := range keep {
keepIdents[c.Ident(a)] = struct{}{}
}
}
idents := make([]identPair, 0, len(dents))
for _, dent := range dents {
name := dent.Name()
if err = Decode(&buf, name); err != nil {
return nil, nil, err
}
id := unique.Make(ID(buf))
if _, ok := keepIdents[id]; len(keep) == 0 || ok {
if err = readlinkChecksum(c.base.Append(
dirIdentifier,
name,
), &buf); err != nil {
return nil, nil, err
}
delete(checksums, unique.Make(buf))
continue
}
c.msg.Verbosef("arranging for destruction of %s...", name)
idents = append(idents, identPair{id, name})
}
destroyedIdents := make([]unique.Handle[ID], 0, len(idents))
for _, pair := range idents {
if !dry {
if err = os.Remove(c.base.Append(
dirStatus,
pair.name,
).String()); err != nil && !errors.Is(err, os.ErrNotExist) {
return destroyedIdents, nil, err
}
if err = os.Remove(c.base.Append(
dirIdentifier,
pair.name,
).String()); err != nil {
return destroyedIdents, nil, err
}
}
destroyedIdents = append(destroyedIdents, pair.id)
}
destroyedChecksums := make([]unique.Handle[Checksum], 0, len(checksums))
for checksum, name := range checksums {
if err = c.parent.Err(); err != nil {
return destroyedIdents, destroyedChecksums, err
}
c.msg.Verbosef("destroying checksum %s...", name)
if !dry {
if err = errors.Join(removeAll(c.base.Append(
dirChecksum,
name,
))); err != nil {
return destroyedIdents, destroyedChecksums, err
}
}
destroyedChecksums = append(destroyedChecksums, checksum)
}
dents, err = os.ReadDir(c.base.Append(dirSubstitute).String())
if err != nil {
return destroyedIdents, destroyedChecksums, err
}
for _, dent := range dents {
name := dent.Name()
if err = readlinkChecksum(c.base.Append(
dirSubstitute,
name,
), &buf); err != nil {
return destroyedIdents, destroyedChecksums, err
}
if _, ok := checksums[unique.Make(buf)]; !ok {
continue
}
c.msg.Verbosef("destroying substitute %s...", name)
if !dry {
if err = os.Remove(c.base.Append(
dirStatus,
name,
).String()); err != nil && !errors.Is(err, os.ErrNotExist) {
return destroyedIdents, nil, err
}
if err = os.Remove(c.base.Append(
dirSubstitute,
name,
).String()); err != nil {
return destroyedIdents, destroyedChecksums, err
}
}
}
return destroyedIdents, destroyedChecksums, nil
}