diff --git a/etcdserver/server.go b/etcdserver/server.go index 976acd684c1..4db1998d6fc 100644 --- a/etcdserver/server.go +++ b/etcdserver/server.go @@ -807,12 +807,13 @@ func (s *EtcdServer) start() { func (s *EtcdServer) purgeFile() { var dberrc, serrc, werrc <-chan error + var dbdonec, sdonec, wdonec <-chan struct{} if s.Cfg.MaxSnapFiles > 0 { - dberrc = fileutil.PurgeFile(s.getLogger(), s.Cfg.SnapDir(), "snap.db", s.Cfg.MaxSnapFiles, purgeFileInterval, s.done) - serrc = fileutil.PurgeFile(s.getLogger(), s.Cfg.SnapDir(), "snap", s.Cfg.MaxSnapFiles, purgeFileInterval, s.done) + dbdonec, dberrc = fileutil.PurgeFileWithDoneNotify(s.getLogger(), s.Cfg.SnapDir(), "snap.db", s.Cfg.MaxSnapFiles, purgeFileInterval, s.stopping) + sdonec, serrc = fileutil.PurgeFileWithDoneNotify(s.getLogger(), s.Cfg.SnapDir(), "snap", s.Cfg.MaxSnapFiles, purgeFileInterval, s.stopping) } if s.Cfg.MaxWALFiles > 0 { - werrc = fileutil.PurgeFile(s.getLogger(), s.Cfg.WALDir(), "wal", s.Cfg.MaxWALFiles, purgeFileInterval, s.done) + wdonec, werrc = fileutil.PurgeFileWithDoneNotify(s.getLogger(), s.Cfg.WALDir(), "wal", s.Cfg.MaxWALFiles, purgeFileInterval, s.stopping) } lg := s.getLogger() @@ -836,6 +837,15 @@ func (s *EtcdServer) purgeFile() { plog.Fatalf("failed to purge wal file %v", e) } case <-s.stopping: + if dbdonec != nil { + <-dbdonec + } + if sdonec != nil { + <-sdonec + } + if wdonec != nil { + <-wdonec + } return } } diff --git a/pkg/fileutil/purge.go b/pkg/fileutil/purge.go index fda96c37114..d116f340b6f 100644 --- a/pkg/fileutil/purge.go +++ b/pkg/fileutil/purge.go @@ -25,13 +25,23 @@ import ( ) func PurgeFile(lg *zap.Logger, dirname string, suffix string, max uint, interval time.Duration, stop <-chan struct{}) <-chan error { - return purgeFile(lg, dirname, suffix, max, interval, stop, nil) + return purgeFile(lg, dirname, suffix, max, interval, stop, nil, nil) +} + +func PurgeFileWithDoneNotify(lg *zap.Logger, dirname string, suffix string, max uint, interval time.Duration, stop <-chan struct{}) (<-chan struct{}, <-chan error) { + doneC := make(chan struct{}) + errC := purgeFile(lg, dirname, suffix, max, interval, stop, nil, doneC) + return doneC, errC } // purgeFile is the internal implementation for PurgeFile which can post purged files to purgec if non-nil. -func purgeFile(lg *zap.Logger, dirname string, suffix string, max uint, interval time.Duration, stop <-chan struct{}, purgec chan<- string) <-chan error { +// if donec is non-nil, the function closes it to notify its exit. +func purgeFile(lg *zap.Logger, dirname string, suffix string, max uint, interval time.Duration, stop <-chan struct{}, purgec chan<- string, donec chan<- struct{}) <-chan error { errC := make(chan error, 1) go func() { + if donec != nil { + defer close(donec) + } for { fnames, err := ReadDir(dirname) if err != nil { diff --git a/pkg/fileutil/purge_test.go b/pkg/fileutil/purge_test.go index 908e5f8db2a..f0e7d6b566f 100644 --- a/pkg/fileutil/purge_test.go +++ b/pkg/fileutil/purge_test.go @@ -45,7 +45,7 @@ func TestPurgeFile(t *testing.T) { stop, purgec := make(chan struct{}), make(chan string, 10) // keep 3 most recent files - errch := purgeFile(zap.NewExample(), dir, "test", 3, time.Millisecond, stop, purgec) + errch := purgeFile(zap.NewExample(), dir, "test", 3, time.Millisecond, stop, purgec, nil) select { case f := <-purgec: t.Errorf("unexpected purge on %q", f) @@ -116,7 +116,7 @@ func TestPurgeFileHoldingLockFile(t *testing.T) { } stop, purgec := make(chan struct{}), make(chan string, 10) - errch := purgeFile(zap.NewExample(), dir, "test", 3, time.Millisecond, stop, purgec) + errch := purgeFile(zap.NewExample(), dir, "test", 3, time.Millisecond, stop, purgec, nil) for i := 0; i < 5; i++ { select {