Merge pull request #680 from printfcoder/master
fix file watcher event bug on Linux
This commit is contained in:
commit
718ae42808
@ -1,6 +1,7 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
@ -8,10 +9,15 @@ import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"github.com/micro/go-micro/config/source/env"
|
||||
"github.com/micro/go-micro/config/source/file"
|
||||
)
|
||||
|
||||
var (
|
||||
sep = string(os.PathSeparator)
|
||||
)
|
||||
|
||||
func createFileForIssue18(t *testing.T, content string) *os.File {
|
||||
data := []byte(content)
|
||||
path := filepath.Join(os.TempDir(), fmt.Sprintf("file.%d", time.Now().UnixNano()))
|
||||
@ -116,3 +122,57 @@ func TestConfigMerge(t *testing.T) {
|
||||
actualHost)
|
||||
}
|
||||
}
|
||||
|
||||
func TestFileChange(t *testing.T) {
|
||||
// create a temp file
|
||||
fileName := uuid.New().String() + "testWatcher.json"
|
||||
f, err := os.OpenFile("."+sep+fileName, os.O_WRONLY|os.O_CREATE, 0666)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
defer f.Close()
|
||||
defer os.Remove("." + sep + fileName)
|
||||
|
||||
// load the file
|
||||
if err := Load(file.NewSource(
|
||||
file.WithPath("." + sep + fileName),
|
||||
)); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
// watch changes
|
||||
watcher, err := Watch()
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
changeTimes := 0
|
||||
go func() {
|
||||
for {
|
||||
v, err := watcher.Next()
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
return
|
||||
}
|
||||
changeTimes++
|
||||
t.Logf("file change,%s", string(v.Bytes()))
|
||||
}
|
||||
}()
|
||||
|
||||
content := map[int]string{}
|
||||
// change the file
|
||||
for i := 0; i < 5; i++ {
|
||||
content[i] = time.Now().String()
|
||||
bytes, _ := json.Marshal(content)
|
||||
f.Truncate(0)
|
||||
f.Seek(0, 0)
|
||||
if _, err := f.Write(bytes); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
time.Sleep(time.Second)
|
||||
}
|
||||
|
||||
if changeTimes != 4 {
|
||||
t.Error(fmt.Errorf("watcher error: change times %d is not enough", changeTimes))
|
||||
}
|
||||
}
|
||||
|
@ -1,3 +1,5 @@
|
||||
//+build !linux
|
||||
|
||||
package file
|
||||
|
||||
import (
|
||||
|
72
config/source/file/watcher_linux.go
Normal file
72
config/source/file/watcher_linux.go
Normal file
@ -0,0 +1,72 @@
|
||||
//+build linux
|
||||
|
||||
package file
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"os"
|
||||
|
||||
"github.com/fsnotify/fsnotify"
|
||||
"github.com/micro/go-micro/config/source"
|
||||
)
|
||||
|
||||
type watcher struct {
|
||||
f *file
|
||||
|
||||
fw *fsnotify.Watcher
|
||||
exit chan bool
|
||||
}
|
||||
|
||||
func newWatcher(f *file) (source.Watcher, error) {
|
||||
fw, err := fsnotify.NewWatcher()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
fw.Add(f.path)
|
||||
|
||||
return &watcher{
|
||||
f: f,
|
||||
fw: fw,
|
||||
exit: make(chan bool),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (w *watcher) Next() (*source.ChangeSet, error) {
|
||||
// is it closed?
|
||||
select {
|
||||
case <-w.exit:
|
||||
return nil, errors.New("watcher stopped")
|
||||
default:
|
||||
}
|
||||
|
||||
// try get the event
|
||||
select {
|
||||
case event, _ := <-w.fw.Events:
|
||||
if event.Op == fsnotify.Rename {
|
||||
// check existence of file, and add watch again
|
||||
_, err := os.Stat(event.Name)
|
||||
if err == nil || os.IsExist(err) {
|
||||
w.fw.Add(event.Name)
|
||||
}
|
||||
}
|
||||
|
||||
c, err := w.f.Read()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// add path again for the event bug of fsnotify
|
||||
w.fw.Add(w.f.path)
|
||||
|
||||
return c, nil
|
||||
case err := <-w.fw.Errors:
|
||||
return nil, err
|
||||
case <-w.exit:
|
||||
return nil, errors.New("watcher stopped")
|
||||
}
|
||||
}
|
||||
|
||||
func (w *watcher) Stop() error {
|
||||
return w.fw.Close()
|
||||
}
|
Loading…
Reference in New Issue
Block a user