mirror of
https://github.com/foxcpp/maddy.git
synced 2025-04-05 05:57:39 +03:00
Merge pull request #559 from domcyrus/fix-race-on-table-file
fix race condition on table.file #557
This commit is contained in:
commit
01ff37a9c5
2 changed files with 60 additions and 47 deletions
|
@ -124,24 +124,31 @@ func (f *File) reloader() {
|
||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
case <-t.C:
|
case <-t.C:
|
||||||
|
f.reload()
|
||||||
|
|
||||||
|
case <-f.forceReload:
|
||||||
|
f.reload()
|
||||||
|
|
||||||
|
case <-f.stopReloader:
|
||||||
|
f.stopReloader <- struct{}{}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *File) reload() {
|
||||||
info, err := os.Stat(f.file)
|
info, err := os.Stat(f.file)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if os.IsNotExist(err) {
|
if os.IsNotExist(err) {
|
||||||
f.mLck.Lock()
|
f.mLck.Lock()
|
||||||
f.m = map[string][]string{}
|
f.m = map[string][]string{}
|
||||||
f.mStamp = time.Now()
|
|
||||||
f.mLck.Unlock()
|
f.mLck.Unlock()
|
||||||
continue
|
return
|
||||||
}
|
}
|
||||||
f.log.Error("os stat", err)
|
f.log.Error("os stat", err)
|
||||||
}
|
}
|
||||||
if info.ModTime().Before(f.mStamp) {
|
if info.ModTime().Before(f.mStamp) || time.Since(info.ModTime()) < (reloadInterval/2) {
|
||||||
continue // reload not necessary
|
return // reload not necessary
|
||||||
}
|
|
||||||
case <-f.forceReload:
|
|
||||||
case <-f.stopReloader:
|
|
||||||
f.stopReloader <- struct{}{}
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
f.log.Debugf("reloading")
|
f.log.Debugf("reloading")
|
||||||
|
@ -150,18 +157,28 @@ func (f *File) reloader() {
|
||||||
if err := readFile(f.file, newm); err != nil {
|
if err := readFile(f.file, newm); err != nil {
|
||||||
if os.IsNotExist(err) {
|
if os.IsNotExist(err) {
|
||||||
f.log.Printf("ignoring non-existent file: %s", f.file)
|
f.log.Printf("ignoring non-existent file: %s", f.file)
|
||||||
continue
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
f.log.Println(err)
|
f.log.Println(err)
|
||||||
continue
|
return
|
||||||
|
}
|
||||||
|
// after reading we need to check whether file has changed in between
|
||||||
|
info2, err := os.Stat(f.file)
|
||||||
|
if err != nil {
|
||||||
|
f.log.Println(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if !info2.ModTime().Equal(info.ModTime()) {
|
||||||
|
// file has changed in the meantime
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
f.mLck.Lock()
|
f.mLck.Lock()
|
||||||
f.m = newm
|
f.m = newm
|
||||||
f.mStamp = time.Now()
|
f.mStamp = info.ModTime()
|
||||||
f.mLck.Unlock()
|
f.mLck.Unlock()
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *File) Close() error {
|
func (f *File) Close() error {
|
||||||
|
|
|
@ -111,27 +111,26 @@ func TestFileReload(t *testing.T) {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// This delay is somehow important. Not sure why.
|
// ensure it is correctly loaded at first time.
|
||||||
time.Sleep(500 * time.Millisecond)
|
m.mLck.RLock()
|
||||||
|
if m.m["cat"] == nil {
|
||||||
|
t.Fatalf("wrong content loaded, new m were not loaded, %v", m.m)
|
||||||
|
}
|
||||||
|
m.mLck.RUnlock()
|
||||||
|
|
||||||
if err := ioutil.WriteFile(f.Name(), []byte("dog: cat"), os.ModePerm); err != nil {
|
for i := 0; i < 100; i++ {
|
||||||
|
// try to provoke race condition on file writing
|
||||||
|
if i%2 == 0 {
|
||||||
|
if err := os.WriteFile(f.Name(), []byte("dog: cat"), os.ModePerm); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
for i := 0; i < 10; i++ {
|
|
||||||
time.Sleep(reloadInterval + 50*time.Millisecond)
|
|
||||||
m.mLck.RLock()
|
|
||||||
if m.m["dog"] != nil {
|
|
||||||
m.mLck.RUnlock()
|
|
||||||
break
|
|
||||||
}
|
}
|
||||||
m.mLck.RUnlock()
|
time.Sleep(reloadInterval + 5*time.Millisecond)
|
||||||
}
|
|
||||||
|
|
||||||
m.mLck.RLock()
|
m.mLck.RLock()
|
||||||
defer m.mLck.RUnlock()
|
|
||||||
if m.m["dog"] == nil {
|
if m.m["dog"] == nil {
|
||||||
t.Fatal("New m were not loaded")
|
t.Fatalf("wrong content loaded, new m were not loaded, %v", m.m)
|
||||||
|
}
|
||||||
|
m.mLck.RUnlock()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -208,9 +207,6 @@ func TestFileReload_Removed(t *testing.T) {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// This delay is somehow important. Not sure why.
|
|
||||||
time.Sleep(250 * time.Millisecond)
|
|
||||||
|
|
||||||
os.Remove(f.Name())
|
os.Remove(f.Name())
|
||||||
|
|
||||||
time.Sleep(3 * reloadInterval)
|
time.Sleep(3 * reloadInterval)
|
||||||
|
@ -223,5 +219,5 @@ func TestFileReload_Removed(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
reloadInterval = 250 * time.Millisecond
|
reloadInterval = 10 * time.Millisecond
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue