Files
silo-server/internal/playback/transcode_cleanup.go
T

66 lines
1.8 KiB
Go
Raw Normal View History

2026-05-22 20:26:11 -04:00
package playback
import (
"fmt"
"os"
"path/filepath"
"time"
2026-05-22 20:26:11 -04:00
)
// CleanupOrphanedTranscodeDirs removes per-session transcode directories that
// are not associated with any currently active session IDs.
//
// minAge spares a dir whose most recent modification is younger than the given
// age even when it is absent from activeSessionIDs. Under token-carried
// reconstruction there is no durable card index, so an in-memory miss does not
// prove a session is dead — a client holding a still-valid token may yet
// reconstruct it. Sparing dirs younger than the maximum token lifetime closes
// that race; once a dir is older than any surviving token, it is safe to reap.
// Pass 0 to disable age-sparing (e.g. a dedicated node's boot-time full wipe,
// where node restart is an accepted session loss).
func CleanupOrphanedTranscodeDirs(root string, activeSessionIDs map[string]struct{}, minAge time.Duration) (int, error) {
2026-05-22 20:26:11 -04:00
if root == "" {
return 0, nil
}
entries, err := os.ReadDir(root)
if err != nil {
if os.IsNotExist(err) {
return 0, nil
}
return 0, fmt.Errorf("read transcode dir %q: %w", root, err)
}
removed := 0
cutoff := time.Now().Add(-minAge)
2026-05-22 20:26:11 -04:00
for _, entry := range entries {
if !entry.IsDir() {
continue
}
if _, ok := activeSessionIDs[entry.Name()]; ok {
continue
}
dir := filepath.Join(root, entry.Name())
if minAge > 0 {
info, statErr := entry.Info()
if statErr != nil {
// Can't verify age: conservatively retain rather than risk
// reaping a dir a surviving token could still reconstruct.
continue
}
if info.ModTime().After(cutoff) {
// Recently active: a surviving token could still reconstruct it.
continue
}
}
2026-05-22 20:26:11 -04:00
if err := os.RemoveAll(dir); err != nil {
return removed, fmt.Errorf("remove orphaned transcode dir %q: %w", dir, err)
}
removed++
}
return removed, nil
}