app/platform: fix an issue about accessing to a closed db on shutdown (#21549)

Этот коммит содержится в:
Ibrahim Serdar Acikgoz
2022-10-31 15:58:35 +03:00
коммит произвёл GitHub
родитель b31e6f2149
Коммит c6b6b3313e
4 изменённых файлов: 32 добавлений и 9 удалений

Просмотреть файл

@@ -21,8 +21,8 @@ func (ps *PlatformService) Go(f func()) {
}() }()
} }
// WaitForGoroutines blocks until all goroutines created by App.Go exit. // waitForGoroutines blocks until all goroutines created by PlatformService.Go() exit.
func (ps *PlatformService) WaitForGoroutines() { func (ps *PlatformService) waitForGoroutines() {
for atomic.LoadInt32(&ps.goroutineCount) != 0 { for atomic.LoadInt32(&ps.goroutineCount) != 0 {
<-ps.goroutineExitSignal <-ps.goroutineExitSignal
} }

Просмотреть файл

@@ -390,6 +390,12 @@ func (ps *PlatformService) Shutdown() error {
ps.RemoveLicenseListener(ps.licenseListenerId) ps.RemoveLicenseListener(ps.licenseListenerId)
// we need to wait the goroutines to finish before closing the store
// and this needs to be called after hub stop because hub generates goroutines
// when it is active. If we wait first we have no mechanism to prevent adding
// more go routines hence they still going to be invoked.
ps.waitForGoroutines()
if ps.Store != nil { if ps.Store != nil {
ps.Store.Close() ps.Store.Close()
} }

Просмотреть файл

@@ -4,10 +4,13 @@
package platform package platform
import ( import (
"math/rand"
"net/http" "net/http"
"os" "os"
"strings" "strings"
"sync/atomic"
"testing" "testing"
"time"
"github.com/mattermost/mattermost-server/v6/config" "github.com/mattermost/mattermost-server/v6/config"
"github.com/mattermost/mattermost-server/v6/einterfaces/mocks" "github.com/mattermost/mattermost-server/v6/einterfaces/mocks"
@@ -154,3 +157,24 @@ func TestMetrics(t *testing.T) {
mockMetricsImpl.AssertExpectations(t) mockMetricsImpl.AssertExpectations(t)
}) })
} }
func TestShutdown(t *testing.T) {
t.Run("should shutdown gracefully", func(t *testing.T) {
th := Setup(t)
rand.Seed(time.Now().UnixNano())
// we create plenty of go routines to make sure we wait for all of them
// to finish before shutting down
for i := 0; i < 1000; i++ {
th.Service.Go(func() {
time.Sleep(time.Millisecond * time.Duration(rand.Intn(20)))
})
}
err := th.Service.Shutdown()
require.NoError(t, err)
// assert that there are no more go routines running
require.Zero(t, atomic.LoadInt32(&th.Service.goroutineCount))
})
}

Просмотреть файл

@@ -706,8 +706,6 @@ func (s *Server) Shutdown() {
s.StopPushNotificationsHubWorkers() s.StopPushNotificationsHubWorkers()
s.htmlTemplateWatcher.Close() s.htmlTemplateWatcher.Close()
s.WaitForGoroutines()
s.platform.StopSearchEngine() s.platform.StopSearchEngine()
s.Audit.Shutdown() s.Audit.Shutdown()
@@ -814,11 +812,6 @@ func (s *Server) GoBuffered(f func()) {
s.platform.GoBuffered(f) s.platform.GoBuffered(f)
} }
// WaitForGoroutines blocks until all goroutines created by App.Go exit.
func (s *Server) WaitForGoroutines() {
s.platform.WaitForGoroutines()
}
var corsAllowedMethods = []string{ var corsAllowedMethods = []string{
"POST", "POST",
"GET", "GET",