feat: add working watchdog cycle
This commit is contained in:
@@ -1,30 +1,35 @@
|
||||
package database
|
||||
|
||||
import (
|
||||
"eden-server/internal/runtime"
|
||||
"eden-server/internal/utility"
|
||||
"log/slog"
|
||||
"path/filepath"
|
||||
"time"
|
||||
|
||||
"gorm.io/driver/sqlite"
|
||||
"gorm.io/gorm"
|
||||
"gorm.io/gorm/logger"
|
||||
)
|
||||
|
||||
//var watchdogStop = make(chan struct{})
|
||||
|
||||
func KickoffDatabase(workDir string) (*gorm.DB, error) {
|
||||
dbLoc := filepath.Join(workDir, "garden.db")
|
||||
db, err := gorm.Open(sqlite.Open(dbLoc), &gorm.Config{})
|
||||
db, err := gorm.Open(sqlite.Open(dbLoc), &gorm.Config{
|
||||
Logger: logger.Discard, // disable gorm logging since its not slog (yet)
|
||||
TranslateError: true,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := db.AutoMigrate(&State{}); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := db.AutoMigrate(&Device{}); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := db.AutoMigrate(&File{}); err != nil {
|
||||
// try to use GORM automigrate if the schema changes
|
||||
slog.Info("performing migration")
|
||||
if err := db.AutoMigrate(
|
||||
&State{},
|
||||
&Device{},
|
||||
&File{},
|
||||
); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -39,7 +44,7 @@ func KickoffDatabase(workDir string) (*gorm.DB, error) {
|
||||
return db, nil
|
||||
}
|
||||
|
||||
func KickoffDatabaseWatchdog(env runtime.Environment, db *gorm.DB) {
|
||||
func KickoffDatabaseWatchdog(env utility.Environment, db *gorm.DB) {
|
||||
timeInterval := time.Second * time.Duration(env.WatchInterval)
|
||||
ticker := time.NewTicker(timeInterval)
|
||||
|
||||
@@ -58,11 +63,11 @@ func KickoffDatabaseWatchdog(env runtime.Environment, db *gorm.DB) {
|
||||
*/
|
||||
|
||||
// run the watchdog function once to see if all is well.
|
||||
watchdog(env.DataDirectory, db)
|
||||
watchdog(env, db)
|
||||
// then defer to a decoupled/disowned golang goroutine
|
||||
|
||||
for range ticker.C {
|
||||
watchdog(env.DataDirectory, db)
|
||||
watchdog(env, db)
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
@@ -41,9 +41,10 @@ type Device struct {
|
||||
type File struct {
|
||||
ID int `gorm:"primaryKey"`
|
||||
MediaType MediaType `gorm:"type:varchar(20);not null;"`
|
||||
GivenName string
|
||||
Filepath string
|
||||
Checksum string // base64 encoded sha512 checksum
|
||||
MetaName string
|
||||
FileName string
|
||||
FilePath string
|
||||
Checksum string `gorm:"uniqueIndex"` // hex encoded sha512 checksum
|
||||
CreatedAt time.Time
|
||||
UpdatedAt time.Time
|
||||
}
|
||||
|
||||
@@ -19,3 +19,7 @@ func GetFiles(db *gorm.DB) ([]File, error) {
|
||||
func RegisterFile(db *gorm.DB, f File) error {
|
||||
return db.Create(&f).Error
|
||||
}
|
||||
|
||||
func DeregisterFile(db *gorm.DB, f File) error {
|
||||
return db.Delete(&f).Error
|
||||
}
|
||||
|
||||
@@ -1,36 +1,73 @@
|
||||
package database
|
||||
|
||||
import (
|
||||
"eden-server/internal/utility"
|
||||
"log/slog"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
func watchdog(w string, db *gorm.DB) {
|
||||
func watchdog(env utility.Environment, db *gorm.DB) {
|
||||
slog.Info("performing the watchdog cycle")
|
||||
|
||||
files, err := GetFiles(db)
|
||||
fsFiles, err := os.ReadDir(env.ContentDirectory)
|
||||
if err != nil {
|
||||
slog.Error("failed to read the content directory on the filesystem", "error", err)
|
||||
return
|
||||
}
|
||||
|
||||
dbFiles, err := GetFiles(db)
|
||||
if err != nil {
|
||||
slog.Error("failed to retrieve the files indexed from the database", "error", err)
|
||||
return
|
||||
}
|
||||
|
||||
var purgeList []string
|
||||
for _, f := range files {
|
||||
i, err := os.Stat(f.Filepath)
|
||||
if err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
purgeList = append(purgeList, f.Filepath)
|
||||
continue
|
||||
}
|
||||
slog.Warn("stat failed", "file", f.Filepath, "error", err)
|
||||
continue
|
||||
}
|
||||
// generate a set of filesystem contents
|
||||
fsSet := make(map[string]struct{}) // cool name for the files that are (now) marked for annihilation
|
||||
for _, f := range fsFiles {
|
||||
// absolute path creation
|
||||
fullPath := filepath.Join(env.ContentDirectory, f.Name())
|
||||
fsSet[fullPath] = struct{}{}
|
||||
}
|
||||
|
||||
if i.IsDir() {
|
||||
purgeList = append(purgeList, f.Filepath) // also mark it for purger if its a directory. We do not want that here
|
||||
// generate a set of Database contents
|
||||
dbSet := make(map[string]File) // cool name for the files that are going to be deregistered from the database
|
||||
for _, f := range dbFiles {
|
||||
dbSet[f.FilePath] = f
|
||||
}
|
||||
|
||||
// FS -> DB
|
||||
// check for orphaned filesystem files
|
||||
var fsPurgeScroll []string
|
||||
for path := range fsSet {
|
||||
if _, exists := dbSet[path]; !exists {
|
||||
fsPurgeScroll = append(fsPurgeScroll, path)
|
||||
}
|
||||
}
|
||||
|
||||
// DB -> FS
|
||||
// check stale database records
|
||||
var dbPurgeScroll []File
|
||||
for path, f := range dbSet {
|
||||
if _, exists := fsSet[path]; !exists {
|
||||
dbPurgeScroll = append(dbPurgeScroll, f)
|
||||
}
|
||||
}
|
||||
|
||||
if len(fsPurgeScroll) > 0 {
|
||||
slog.Info("filesystem purge scroll is populated, engaging purge")
|
||||
//filepath is stored in the slice, the filename or file object, see above
|
||||
for _, fp := range fsPurgeScroll {
|
||||
utility.RemoveFile(fp)
|
||||
}
|
||||
}
|
||||
|
||||
if len(dbPurgeScroll) > 0 {
|
||||
slog.Info("database purge scroll is populated, engaging purge")
|
||||
for _, f := range dbPurgeScroll {
|
||||
DeregisterFile(db, f)
|
||||
}
|
||||
}
|
||||
slog.Info("purge list", "files", purgeList)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user