datarhei-dragonfork-core/restream/fs/fs.go

203 lines
4 KiB
Go
Raw Normal View History

2022-05-13 13:26:45 -04:00
package fs
import (
"context"
"sort"
"sync"
"time"
"github.com/datarhei/core/v16/io/fs"
"github.com/datarhei/core/v16/log"
2022-05-13 13:26:45 -04:00
)
type Config struct {
FS fs.Filesystem
Logger log.Logger
}
type Pattern struct {
2022-06-03 11:21:52 -04:00
Pattern string
MaxFiles uint
MaxFileAge time.Duration
PurgeOnDelete bool
2022-05-13 13:26:45 -04:00
}
type Filesystem interface {
fs.Filesystem
// SetCleanup
SetCleanup(id string, patterns []Pattern)
// UnsetCleanup
UnsetCleanup(id string)
// Start
Start()
// Stop()
Stop()
}
type filesystem struct {
fs.Filesystem
cleanupPatterns map[string][]Pattern
cleanupLock sync.RWMutex
stopTicker context.CancelFunc
startOnce sync.Once
stopOnce sync.Once
logger log.Logger
}
func New(config Config) Filesystem {
rfs := &filesystem{
2022-05-13 13:26:45 -04:00
Filesystem: config.FS,
logger: config.Logger,
}
if rfs.logger == nil {
rfs.logger = log.New("")
2022-05-13 13:26:45 -04:00
}
2023-01-31 08:45:58 -05:00
rfs.logger = rfs.logger.WithFields(log.Fields{
"name": config.FS.Name(),
"type": config.FS.Type(),
})
rfs.cleanupPatterns = make(map[string][]Pattern)
2022-05-13 13:26:45 -04:00
// already drain the stop
rfs.stopOnce.Do(func() {})
2022-05-13 13:26:45 -04:00
return rfs
2022-05-13 13:26:45 -04:00
}
func (rfs *filesystem) Start() {
rfs.startOnce.Do(func() {
2022-05-13 13:26:45 -04:00
ctx, cancel := context.WithCancel(context.Background())
rfs.stopTicker = cancel
go rfs.cleanupTicker(ctx, time.Second)
2022-05-13 13:26:45 -04:00
rfs.stopOnce = sync.Once{}
2022-05-13 13:26:45 -04:00
rfs.logger.Debug().Log("Starting cleanup")
2022-05-13 13:26:45 -04:00
})
}
func (rfs *filesystem) Stop() {
rfs.stopOnce.Do(func() {
rfs.stopTicker()
2022-05-13 13:26:45 -04:00
rfs.startOnce = sync.Once{}
2022-05-13 13:26:45 -04:00
rfs.logger.Debug().Log("Stopping cleanup")
2022-05-13 13:26:45 -04:00
})
}
func (rfs *filesystem) SetCleanup(id string, patterns []Pattern) {
2022-05-13 13:26:45 -04:00
if len(patterns) == 0 {
return
}
for _, p := range patterns {
rfs.logger.Debug().WithFields(log.Fields{
2022-05-13 13:26:45 -04:00
"id": id,
"pattern": p.Pattern,
"max_files": p.MaxFiles,
"max_file_age": p.MaxFileAge.Seconds(),
}).Log("Add pattern")
}
rfs.cleanupLock.Lock()
defer rfs.cleanupLock.Unlock()
2022-05-13 13:26:45 -04:00
rfs.cleanupPatterns[id] = append(rfs.cleanupPatterns[id], patterns...)
2022-05-13 13:26:45 -04:00
}
2022-08-18 05:00:37 -04:00
func (rfs *filesystem) UnsetCleanup(id string) {
rfs.logger.Debug().WithField("id", id).Log("Remove pattern group")
2022-05-13 13:26:45 -04:00
rfs.cleanupLock.Lock()
defer rfs.cleanupLock.Unlock()
2022-05-13 13:26:45 -04:00
patterns := rfs.cleanupPatterns[id]
delete(rfs.cleanupPatterns, id)
2022-06-03 11:21:52 -04:00
rfs.purge(patterns)
2022-05-13 13:26:45 -04:00
}
func (rfs *filesystem) cleanup() {
rfs.cleanupLock.RLock()
defer rfs.cleanupLock.RUnlock()
2022-05-13 13:26:45 -04:00
for _, patterns := range rfs.cleanupPatterns {
2022-05-13 13:26:45 -04:00
for _, pattern := range patterns {
filesAndDirs := rfs.Filesystem.List(pattern.Pattern)
files := []fs.FileInfo{}
for _, f := range filesAndDirs {
if f.IsDir() {
continue
}
files = append(files, f)
}
2022-05-13 13:26:45 -04:00
sort.Slice(files, func(i, j int) bool { return files[i].ModTime().Before(files[j].ModTime()) })
if pattern.MaxFiles > 0 && uint(len(files)) > pattern.MaxFiles {
for i := uint(0); i < uint(len(files))-pattern.MaxFiles; i++ {
rfs.logger.Debug().WithField("path", files[i].Name()).Log("Remove file because MaxFiles is exceeded")
rfs.Filesystem.Delete(files[i].Name())
2022-05-13 13:26:45 -04:00
}
}
if pattern.MaxFileAge > 0 {
bestBefore := time.Now().Add(-pattern.MaxFileAge)
for _, f := range files {
if f.ModTime().Before(bestBefore) {
rfs.logger.Debug().WithField("path", f.Name()).Log("Remove file because MaxFileAge is exceeded")
rfs.Filesystem.Delete(f.Name())
2022-05-13 13:26:45 -04:00
}
}
}
}
}
}
func (rfs *filesystem) purge(patterns []Pattern) (nfiles uint64) {
2022-06-03 11:21:52 -04:00
for _, pattern := range patterns {
if !pattern.PurgeOnDelete {
continue
}
files := rfs.Filesystem.List(pattern.Pattern)
sort.Slice(files, func(i, j int) bool { return len(files[i].Name()) > len(files[j].Name()) })
2022-06-03 11:21:52 -04:00
for _, f := range files {
rfs.logger.Debug().WithField("path", f.Name()).Log("Purging file")
rfs.Filesystem.Delete(f.Name())
2022-08-18 05:00:37 -04:00
nfiles++
2022-06-03 11:21:52 -04:00
}
}
2022-08-18 05:00:37 -04:00
return
2022-06-03 11:21:52 -04:00
}
func (rfs *filesystem) cleanupTicker(ctx context.Context, interval time.Duration) {
2022-05-13 13:26:45 -04:00
ticker := time.NewTicker(interval)
defer ticker.Stop()
for {
select {
case <-ctx.Done():
return
case <-ticker.C:
rfs.cleanup()
2022-05-13 13:26:45 -04:00
}
}
}