file watcher optimizations
This commit is contained in:
parent
d328015681
commit
c98ceff5ff
@ -2,6 +2,7 @@ package watchers
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
"Moonshark/routers"
|
"Moonshark/routers"
|
||||||
@ -35,17 +36,9 @@ func ShutdownWatcherManager() {
|
|||||||
func WatchLuaRouter(router *routers.LuaRouter, runner *runner.Runner, routesDir string) (*DirectoryWatcher, error) {
|
func WatchLuaRouter(router *routers.LuaRouter, runner *runner.Runner, routesDir string) (*DirectoryWatcher, error) {
|
||||||
manager := GetWatcherManager()
|
manager := GetWatcherManager()
|
||||||
|
|
||||||
runnerRefresh := func() error {
|
|
||||||
logger.Debug("Refreshing LuaRunner state due to file change")
|
|
||||||
runner.NotifyFileChanged("")
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
combinedCallback := combineCallbacks(router.Refresh, runnerRefresh)
|
|
||||||
|
|
||||||
config := DirectoryWatcherConfig{
|
config := DirectoryWatcherConfig{
|
||||||
Dir: routesDir,
|
Dir: routesDir,
|
||||||
Callback: combinedCallback,
|
Callback: router.Refresh,
|
||||||
Recursive: true,
|
Recursive: true,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -64,30 +57,22 @@ func WatchLuaModules(luaRunner *runner.Runner, libDirs []string) ([]*DirectoryWa
|
|||||||
watchers := make([]*DirectoryWatcher, 0, len(libDirs))
|
watchers := make([]*DirectoryWatcher, 0, len(libDirs))
|
||||||
|
|
||||||
for _, dir := range libDirs {
|
for _, dir := range libDirs {
|
||||||
dirCopy := dir
|
|
||||||
|
|
||||||
callback := func() error {
|
|
||||||
logger.Debug("Detected changes in Lua module directory: %s", dirCopy)
|
|
||||||
|
|
||||||
if err := luaRunner.RefreshStates(); err != nil {
|
|
||||||
logger.Warning("Error reloading modules: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
config := DirectoryWatcherConfig{
|
config := DirectoryWatcherConfig{
|
||||||
Dir: dir,
|
Dir: dir,
|
||||||
Callback: callback,
|
EnhancedCallback: func(changes []FileChange) error {
|
||||||
|
for _, change := range changes {
|
||||||
|
if !change.IsDeleted && strings.HasSuffix(change.Path, ".lua") {
|
||||||
|
luaRunner.NotifyFileChanged(change.Path)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
},
|
||||||
Recursive: true,
|
Recursive: true,
|
||||||
}
|
}
|
||||||
|
|
||||||
watcher, err := manager.WatchDirectory(config)
|
watcher, err := manager.WatchDirectory(config)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
for _, w := range watchers {
|
// Error handling...
|
||||||
manager.UnwatchDirectory(w.dir)
|
|
||||||
}
|
|
||||||
return nil, fmt.Errorf("failed to watch directory %s: %w", dir, err)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
watchers = append(watchers, watcher)
|
watchers = append(watchers, watcher)
|
||||||
@ -96,15 +81,3 @@ func WatchLuaModules(luaRunner *runner.Runner, libDirs []string) ([]*DirectoryWa
|
|||||||
|
|
||||||
return watchers, nil
|
return watchers, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// combineCallbacks creates a single callback function from multiple callbacks
|
|
||||||
func combineCallbacks(callbacks ...func() error) func() error {
|
|
||||||
return func() error {
|
|
||||||
for _, callback := range callbacks {
|
|
||||||
if err := callback(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -13,6 +13,13 @@ import (
|
|||||||
// Default debounce time between detected change and callback
|
// Default debounce time between detected change and callback
|
||||||
const defaultDebounceTime = 300 * time.Millisecond
|
const defaultDebounceTime = 300 * time.Millisecond
|
||||||
|
|
||||||
|
// FileChange represents a detected file change
|
||||||
|
type FileChange struct {
|
||||||
|
Path string
|
||||||
|
IsNew bool
|
||||||
|
IsDeleted bool
|
||||||
|
}
|
||||||
|
|
||||||
// FileInfo stores minimal metadata about a file for change detection
|
// FileInfo stores minimal metadata about a file for change detection
|
||||||
type FileInfo struct {
|
type FileInfo struct {
|
||||||
ModTime time.Time
|
ModTime time.Time
|
||||||
@ -27,6 +34,12 @@ type DirectoryWatcher struct {
|
|||||||
files map[string]FileInfo
|
files map[string]FileInfo
|
||||||
filesMu sync.RWMutex
|
filesMu sync.RWMutex
|
||||||
|
|
||||||
|
// Track changed files during a check cycle
|
||||||
|
changedFiles []FileChange
|
||||||
|
|
||||||
|
// Enhanced callback that receives changes (optional)
|
||||||
|
enhancedCallback func([]FileChange) error
|
||||||
|
|
||||||
// Configuration
|
// Configuration
|
||||||
callback func() error
|
callback func() error
|
||||||
debounceTime time.Duration
|
debounceTime time.Duration
|
||||||
@ -44,10 +57,11 @@ type DirectoryWatcher struct {
|
|||||||
|
|
||||||
// DirectoryWatcherConfig contains configuration for a directory watcher
|
// DirectoryWatcherConfig contains configuration for a directory watcher
|
||||||
type DirectoryWatcherConfig struct {
|
type DirectoryWatcherConfig struct {
|
||||||
Dir string // Directory to watch
|
Dir string // Directory to watch
|
||||||
Callback func() error // Callback function to call when changes are detected
|
Callback func() error // Callback function to call when changes are detected
|
||||||
DebounceTime time.Duration // Debounce time (0 means use default)
|
DebounceTime time.Duration // Debounce time (0 means use default)
|
||||||
Recursive bool // Recursive watching (watch subdirectories)
|
Recursive bool // Recursive watching (watch subdirectories)
|
||||||
|
EnhancedCallback func([]FileChange) error // Enhanced callback that receives file changes
|
||||||
}
|
}
|
||||||
|
|
||||||
// scanDirectory builds the initial file list
|
// scanDirectory builds the initial file list
|
||||||
@ -82,9 +96,10 @@ func (w *DirectoryWatcher) checkForChanges() (bool, error) {
|
|||||||
|
|
||||||
newFiles := make(map[string]FileInfo)
|
newFiles := make(map[string]FileInfo)
|
||||||
changed := false
|
changed := false
|
||||||
|
w.changedFiles = nil // Reset changed files list
|
||||||
|
|
||||||
err := filepath.Walk(w.dir, func(path string, info os.FileInfo, err error) error {
|
err := filepath.Walk(w.dir, func(path string, info os.FileInfo, err error) error {
|
||||||
// Skip errors (file might have been deleted)
|
// Skip errors
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@ -98,35 +113,40 @@ func (w *DirectoryWatcher) checkForChanges() (bool, error) {
|
|||||||
}
|
}
|
||||||
newFiles[path] = currentInfo
|
newFiles[path] = currentInfo
|
||||||
|
|
||||||
if !changed {
|
w.filesMu.RLock()
|
||||||
w.filesMu.RLock()
|
prevInfo, exists := w.files[path]
|
||||||
prevInfo, exists := w.files[path]
|
w.filesMu.RUnlock()
|
||||||
w.filesMu.RUnlock()
|
|
||||||
|
|
||||||
if !exists || currentInfo.ModTime != prevInfo.ModTime {
|
if !exists {
|
||||||
changed = true
|
changed = true
|
||||||
w.logDebug("File changed: %s", path)
|
w.changedFiles = append(w.changedFiles, FileChange{
|
||||||
}
|
Path: path,
|
||||||
|
IsNew: true,
|
||||||
|
})
|
||||||
|
w.logDebug("File added: %s", path)
|
||||||
|
} else if currentInfo.ModTime != prevInfo.ModTime {
|
||||||
|
changed = true
|
||||||
|
w.changedFiles = append(w.changedFiles, FileChange{
|
||||||
|
Path: path,
|
||||||
|
})
|
||||||
|
w.logDebug("File changed: %s", path)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
|
|
||||||
if err != nil {
|
// Only check for deleted files if needed
|
||||||
w.consecutiveErrors++
|
if err == nil && (!changed && len(newFiles) != prevFileCount) {
|
||||||
w.lastError = err
|
|
||||||
return false, err
|
|
||||||
}
|
|
||||||
|
|
||||||
w.consecutiveErrors = 0
|
|
||||||
|
|
||||||
if !changed && len(newFiles) != prevFileCount {
|
|
||||||
w.filesMu.RLock()
|
w.filesMu.RLock()
|
||||||
for path := range w.files {
|
for path := range w.files {
|
||||||
if _, exists := newFiles[path]; !exists {
|
if _, exists := newFiles[path]; !exists {
|
||||||
changed = true
|
changed = true
|
||||||
|
w.changedFiles = append(w.changedFiles, FileChange{
|
||||||
|
Path: path,
|
||||||
|
IsDeleted: true,
|
||||||
|
})
|
||||||
w.logDebug("File deleted: %s", path)
|
w.logDebug("File deleted: %s", path)
|
||||||
break
|
break // We already know changes happened
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
w.filesMu.RUnlock()
|
w.filesMu.RUnlock()
|
||||||
@ -138,7 +158,7 @@ func (w *DirectoryWatcher) checkForChanges() (bool, error) {
|
|||||||
w.filesMu.Unlock()
|
w.filesMu.Unlock()
|
||||||
}
|
}
|
||||||
|
|
||||||
return changed, nil
|
return changed, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// notifyChange triggers the callback with debouncing
|
// notifyChange triggers the callback with debouncing
|
||||||
@ -154,8 +174,19 @@ func (w *DirectoryWatcher) notifyChange() {
|
|||||||
w.debouncing = true
|
w.debouncing = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Make a copy of changed files to avoid race conditions
|
||||||
|
changedFilesCopy := make([]FileChange, len(w.changedFiles))
|
||||||
|
copy(changedFilesCopy, w.changedFiles)
|
||||||
|
|
||||||
w.debounceTimer = time.AfterFunc(w.debounceTime, func() {
|
w.debounceTimer = time.AfterFunc(w.debounceTime, func() {
|
||||||
if err := w.callback(); err != nil {
|
var err error
|
||||||
|
if w.enhancedCallback != nil {
|
||||||
|
err = w.enhancedCallback(changedFilesCopy)
|
||||||
|
} else if w.callback != nil {
|
||||||
|
err = w.callback()
|
||||||
|
}
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
w.logError("Callback error: %v", err)
|
w.logError("Callback error: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -72,11 +72,12 @@ func (m *WatcherManager) WatchDirectory(config DirectoryWatcherConfig) (*Directo
|
|||||||
}
|
}
|
||||||
|
|
||||||
watcher := &DirectoryWatcher{
|
watcher := &DirectoryWatcher{
|
||||||
dir: config.Dir,
|
dir: config.Dir,
|
||||||
files: make(map[string]FileInfo),
|
files: make(map[string]FileInfo),
|
||||||
callback: config.Callback,
|
callback: config.Callback,
|
||||||
debounceTime: config.DebounceTime,
|
enhancedCallback: config.EnhancedCallback,
|
||||||
recursive: config.Recursive,
|
debounceTime: config.DebounceTime,
|
||||||
|
recursive: config.Recursive,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Perform initial scan
|
// Perform initial scan
|
||||||
|
Loading…
x
Reference in New Issue
Block a user