Fail vs. fatal on store startup (#24170)
If the store fails to initialize (e.g. run a migration), it would `log.Fatal` and then `os.Exit`. Unfortunately, this trips up `TestMain`, which happily keeps running tests, now guaranteed to fail. Avoid this by instead returning an error from the store initialization, handling appropriately at the layer above.
Этот коммит содержится в:
коммит произвёл
GitHub
родитель
c030bb44f5
Коммит
e39b485c4b
@@ -204,7 +204,11 @@ func New(sc ServiceConfig, options ...Option) (*PlatformService, error) {
|
|||||||
// Timer layer
|
// Timer layer
|
||||||
// |
|
// |
|
||||||
// Cache layer
|
// Cache layer
|
||||||
ps.sqlStore = sqlstore.New(ps.Config().SqlSettings, ps.metricsIFace)
|
var err error
|
||||||
|
ps.sqlStore, err = sqlstore.New(ps.Config().SqlSettings, ps.metricsIFace)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
searchStore := searchlayer.NewSearchLayer(
|
searchStore := searchlayer.NewSearchLayer(
|
||||||
retrylayer.New(ps.sqlStore),
|
retrylayer.New(ps.sqlStore),
|
||||||
|
|||||||
@@ -31,7 +31,10 @@ func (p *MyPlugin) OnConfigurationChange() error {
|
|||||||
func (p *MyPlugin) MessageWillBePosted(_ *plugin.Context, _ *model.Post) (*model.Post, string) {
|
func (p *MyPlugin) MessageWillBePosted(_ *plugin.Context, _ *model.Post) (*model.Post, string) {
|
||||||
settings := p.API.GetUnsanitizedConfig().SqlSettings
|
settings := p.API.GetUnsanitizedConfig().SqlSettings
|
||||||
settings.Trace = model.NewBool(false)
|
settings.Trace = model.NewBool(false)
|
||||||
store := sqlstore.New(settings, nil)
|
store, err := sqlstore.New(settings, nil)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
store.GetMasterX().Close()
|
store.GetMasterX().Close()
|
||||||
|
|
||||||
for _, isMaster := range []bool{true, false} {
|
for _, isMaster := range []bool{true, false} {
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import (
|
|||||||
"github.com/mattermost/mattermost/server/v8/channels/store"
|
"github.com/mattermost/mattermost/server/v8/channels/store"
|
||||||
"github.com/mattermost/mattermost/server/v8/channels/store/sqlstore"
|
"github.com/mattermost/mattermost/server/v8/channels/store/sqlstore"
|
||||||
"github.com/mattermost/mattermost/server/v8/channels/store/storetest"
|
"github.com/mattermost/mattermost/server/v8/channels/store/storetest"
|
||||||
|
"golang.org/x/sync/errgroup"
|
||||||
)
|
)
|
||||||
|
|
||||||
type storeType struct {
|
type storeType struct {
|
||||||
@@ -91,23 +92,29 @@ func initStores() {
|
|||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
var wg sync.WaitGroup
|
var eg errgroup.Group
|
||||||
for _, st := range storeTypes {
|
for _, st := range storeTypes {
|
||||||
st := st
|
st := st
|
||||||
wg.Add(1)
|
eg.Go(func() error {
|
||||||
go func() {
|
|
||||||
var err error
|
var err error
|
||||||
defer wg.Done()
|
|
||||||
st.SqlStore = sqlstore.New(*st.SqlSettings, nil)
|
st.SqlStore, err = sqlstore.New(*st.SqlSettings, nil)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
st.Store, err = NewLocalCacheLayer(st.SqlStore, nil, nil, getMockCacheProvider())
|
st.Store, err = NewLocalCacheLayer(st.SqlStore, nil, nil, getMockCacheProvider())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
return err
|
||||||
}
|
}
|
||||||
st.Store.DropAllTables()
|
st.Store.DropAllTables()
|
||||||
st.Store.MarkSystemRanUnitTests()
|
st.Store.MarkSystemRanUnitTests()
|
||||||
}()
|
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
}
|
||||||
|
if err := eg.Wait(); err != nil {
|
||||||
|
panic(err)
|
||||||
}
|
}
|
||||||
wg.Wait()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var tearDownStoresOnce sync.Once
|
var tearDownStoresOnce sync.Once
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ import (
|
|||||||
"github.com/mattermost/mattermost/server/v8/channels/store/storetest"
|
"github.com/mattermost/mattermost/server/v8/channels/store/storetest"
|
||||||
"github.com/mattermost/mattermost/server/v8/channels/testlib"
|
"github.com/mattermost/mattermost/server/v8/channels/testlib"
|
||||||
"github.com/mattermost/mattermost/server/v8/platform/services/searchengine"
|
"github.com/mattermost/mattermost/server/v8/platform/services/searchengine"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Test to verify race condition on UpdateConfig. The test must run with -race flag in order to verify
|
// Test to verify race condition on UpdateConfig. The test must run with -race flag in order to verify
|
||||||
@@ -24,7 +25,8 @@ func TestUpdateConfigRace(t *testing.T) {
|
|||||||
driverName = model.DatabaseDriverPostgres
|
driverName = model.DatabaseDriverPostgres
|
||||||
}
|
}
|
||||||
settings := storetest.MakeSqlSettings(driverName, false)
|
settings := storetest.MakeSqlSettings(driverName, false)
|
||||||
store := sqlstore.New(*settings, nil)
|
store, err := sqlstore.New(*settings, nil)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
cfg := &model.Config{}
|
cfg := &model.Config{}
|
||||||
cfg.SetDefaults()
|
cfg.SetDefaults()
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import (
|
|||||||
|
|
||||||
"github.com/mattermost/mattermost/server/public/model"
|
"github.com/mattermost/mattermost/server/public/model"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestUpAndDownMigrations(t *testing.T) {
|
func TestUpAndDownMigrations(t *testing.T) {
|
||||||
@@ -23,7 +24,8 @@ func TestUpAndDownMigrations(t *testing.T) {
|
|||||||
t.Skip(err)
|
t.Skip(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
store := New(*settings, nil)
|
store, err := New(*settings, nil)
|
||||||
|
require.NoError(t, err)
|
||||||
defer store.Close()
|
defer store.Close()
|
||||||
|
|
||||||
err = store.migrate(migrationsDirectionDown, false)
|
err = store.migrate(migrationsDirectionDown, false)
|
||||||
|
|||||||
@@ -135,7 +135,7 @@ type SqlStore struct {
|
|||||||
wgMonitor *sync.WaitGroup
|
wgMonitor *sync.WaitGroup
|
||||||
}
|
}
|
||||||
|
|
||||||
func New(settings model.SqlSettings, metrics einterfaces.MetricsInterface) *SqlStore {
|
func New(settings model.SqlSettings, metrics einterfaces.MetricsInterface) (*SqlStore, error) {
|
||||||
store := &SqlStore{
|
store := &SqlStore{
|
||||||
rrCounter: 0,
|
rrCounter: 0,
|
||||||
srCounter: 0,
|
srCounter: 0,
|
||||||
@@ -147,7 +147,7 @@ func New(settings model.SqlSettings, metrics einterfaces.MetricsInterface) *SqlS
|
|||||||
|
|
||||||
err := store.initConnection()
|
err := store.initConnection()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
mlog.Fatal("Error setting up connections", mlog.Err(err))
|
return nil, errors.Wrap(err, "error setting up connections")
|
||||||
}
|
}
|
||||||
|
|
||||||
store.wgMonitor.Add(1)
|
store.wgMonitor.Add(1)
|
||||||
@@ -155,32 +155,32 @@ func New(settings model.SqlSettings, metrics einterfaces.MetricsInterface) *SqlS
|
|||||||
|
|
||||||
ver, err := store.GetDbVersion(true)
|
ver, err := store.GetDbVersion(true)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
mlog.Fatal("Error while getting DB version.", mlog.Err(err))
|
return nil, errors.Wrap(err, "error while getting DB version")
|
||||||
}
|
}
|
||||||
|
|
||||||
ok, err := store.ensureMinimumDBVersion(ver)
|
ok, err := store.ensureMinimumDBVersion(ver)
|
||||||
if !ok {
|
if !ok {
|
||||||
mlog.Fatal("Error while checking DB version.", mlog.Err(err))
|
return nil, errors.Wrap(err, "error while checking DB version")
|
||||||
}
|
}
|
||||||
|
|
||||||
err = store.ensureDatabaseCollation()
|
err = store.ensureDatabaseCollation()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
mlog.Fatal("Error while checking DB collation.", mlog.Err(err))
|
return nil, errors.Wrap(err, "error while checking DB collation")
|
||||||
}
|
}
|
||||||
|
|
||||||
err = store.migrate(migrationsDirectionUp, false)
|
err = store.migrate(migrationsDirectionUp, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
mlog.Fatal("Failed to apply database migrations.", mlog.Err(err))
|
return nil, errors.Wrap(err, "failed to apply database migrations")
|
||||||
}
|
}
|
||||||
|
|
||||||
store.isBinaryParam, err = store.computeBinaryParam()
|
store.isBinaryParam, err = store.computeBinaryParam()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
mlog.Fatal("Failed to compute binary param", mlog.Err(err))
|
return nil, errors.Wrap(err, "failed to compute binary param")
|
||||||
}
|
}
|
||||||
|
|
||||||
store.pgDefaultTextSearchConfig, err = store.computeDefaultTextSearchConfig()
|
store.pgDefaultTextSearchConfig, err = store.computeDefaultTextSearchConfig()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
mlog.Fatal("Failed to compute default text search config", mlog.Err(err))
|
return nil, errors.Wrap(err, "failed to compute default text search config")
|
||||||
}
|
}
|
||||||
|
|
||||||
store.stores.team = newSqlTeamStore(store)
|
store.stores.team = newSqlTeamStore(store)
|
||||||
@@ -229,7 +229,7 @@ func New(settings model.SqlSettings, metrics einterfaces.MetricsInterface) *SqlS
|
|||||||
|
|
||||||
store.stores.preference.(*SqlPreferenceStore).deleteUnusedFeatures()
|
store.stores.preference.(*SqlPreferenceStore).deleteUnusedFeatures()
|
||||||
|
|
||||||
return store
|
return store, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetupConnection sets up the connection to the database and pings it to make sure it's alive.
|
// SetupConnection sets up the connection to the database and pings it to make sure it's alive.
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ import (
|
|||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
|
"golang.org/x/sync/errgroup"
|
||||||
|
|
||||||
"github.com/mattermost/mattermost/server/public/model"
|
"github.com/mattermost/mattermost/server/public/model"
|
||||||
"github.com/mattermost/mattermost/server/public/plugin/plugintest/mock"
|
"github.com/mattermost/mattermost/server/public/plugin/plugintest/mock"
|
||||||
@@ -126,19 +127,26 @@ func initStores() {
|
|||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
var wg sync.WaitGroup
|
|
||||||
|
var eg errgroup.Group
|
||||||
for _, st := range storeTypes {
|
for _, st := range storeTypes {
|
||||||
st := st
|
st := st
|
||||||
wg.Add(1)
|
eg.Go(func() error {
|
||||||
go func() {
|
var err error
|
||||||
defer wg.Done()
|
st.SqlStore, err = New(*st.SqlSettings, nil)
|
||||||
st.SqlStore = New(*st.SqlSettings, nil)
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
st.Store = st.SqlStore
|
st.Store = st.SqlStore
|
||||||
st.Store.DropAllTables()
|
st.Store.DropAllTables()
|
||||||
st.Store.MarkSystemRanUnitTests()
|
st.Store.MarkSystemRanUnitTests()
|
||||||
}()
|
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
}
|
||||||
|
if err := eg.Wait(); err != nil {
|
||||||
|
panic(err)
|
||||||
}
|
}
|
||||||
wg.Wait()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var tearDownStoresOnce sync.Once
|
var tearDownStoresOnce sync.Once
|
||||||
@@ -175,7 +183,8 @@ func TestStoreLicenseRace(t *testing.T) {
|
|||||||
t.Skip(err)
|
t.Skip(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
store := New(*settings, nil)
|
store, err := New(*settings, nil)
|
||||||
|
require.NoError(t, err)
|
||||||
defer func() {
|
defer func() {
|
||||||
store.Close()
|
store.Close()
|
||||||
storetest.CleanupSqlSettings(settings)
|
storetest.CleanupSqlSettings(settings)
|
||||||
@@ -275,7 +284,8 @@ func TestGetReplica(t *testing.T) {
|
|||||||
|
|
||||||
settings.DataSourceReplicas = dataSourceReplicas
|
settings.DataSourceReplicas = dataSourceReplicas
|
||||||
settings.DataSourceSearchReplicas = dataSourceSearchReplicas
|
settings.DataSourceSearchReplicas = dataSourceSearchReplicas
|
||||||
store := New(*settings, nil)
|
store, err := New(*settings, nil)
|
||||||
|
require.NoError(t, err)
|
||||||
defer func() {
|
defer func() {
|
||||||
store.Close()
|
store.Close()
|
||||||
storetest.CleanupSqlSettings(settings)
|
storetest.CleanupSqlSettings(settings)
|
||||||
@@ -348,7 +358,8 @@ func TestGetReplica(t *testing.T) {
|
|||||||
|
|
||||||
settings.DataSourceReplicas = dataSourceReplicas
|
settings.DataSourceReplicas = dataSourceReplicas
|
||||||
settings.DataSourceSearchReplicas = dataSourceSearchReplicas
|
settings.DataSourceSearchReplicas = dataSourceSearchReplicas
|
||||||
store := New(*settings, nil)
|
store, err := New(*settings, nil)
|
||||||
|
require.NoError(t, err)
|
||||||
defer func() {
|
defer func() {
|
||||||
store.Close()
|
store.Close()
|
||||||
storetest.CleanupSqlSettings(settings)
|
storetest.CleanupSqlSettings(settings)
|
||||||
@@ -417,7 +428,8 @@ func TestGetDbVersion(t *testing.T) {
|
|||||||
t.Skip(err)
|
t.Skip(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
store := New(*settings, nil)
|
store, err := New(*settings, nil)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
version, err := store.GetDbVersion(false)
|
version, err := store.GetDbVersion(false)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
@@ -641,7 +653,8 @@ func TestGetAllConns(t *testing.T) {
|
|||||||
|
|
||||||
settings.DataSourceReplicas = dataSourceReplicas
|
settings.DataSourceReplicas = dataSourceReplicas
|
||||||
settings.DataSourceSearchReplicas = dataSourceSearchReplicas
|
settings.DataSourceSearchReplicas = dataSourceSearchReplicas
|
||||||
store := New(*settings, nil)
|
store, err := New(*settings, nil)
|
||||||
|
require.NoError(t, err)
|
||||||
defer func() {
|
defer func() {
|
||||||
store.Close()
|
store.Close()
|
||||||
storetest.CleanupSqlSettings(settings)
|
storetest.CleanupSqlSettings(settings)
|
||||||
@@ -853,7 +866,8 @@ func TestGetDBSchemaVersion(t *testing.T) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
t.Skip(err)
|
t.Skip(err)
|
||||||
}
|
}
|
||||||
store := New(*settings, nil)
|
store, err := New(*settings, nil)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
assetsList, err := assets.ReadDir(filepath.Join("migrations", driver))
|
assetsList, err := assets.ReadDir(filepath.Join("migrations", driver))
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
@@ -891,7 +905,8 @@ func TestGetAppliedMigrations(t *testing.T) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
t.Skip(err)
|
t.Skip(err)
|
||||||
}
|
}
|
||||||
store := New(*settings, nil)
|
store, err := New(*settings, nil)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
assetsList, err := assets.ReadDir(filepath.Join("migrations", driver))
|
assetsList, err := assets.ReadDir(filepath.Join("migrations", driver))
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|||||||
@@ -118,7 +118,12 @@ func (h *MainHelper) setupStore(withReadReplica bool) {
|
|||||||
|
|
||||||
h.SearchEngine = searchengine.NewBroker(config)
|
h.SearchEngine = searchengine.NewBroker(config)
|
||||||
h.ClusterInterface = &FakeClusterInterface{}
|
h.ClusterInterface = &FakeClusterInterface{}
|
||||||
h.SQLStore = sqlstore.New(*h.Settings, nil)
|
|
||||||
|
var err error
|
||||||
|
h.SQLStore, err = sqlstore.New(*h.Settings, nil)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
h.Store = searchlayer.NewSearchLayer(&TestStore{
|
h.Store = searchlayer.NewSearchLayer(&TestStore{
|
||||||
h.SQLStore,
|
h.SQLStore,
|
||||||
}, h.SearchEngine, config)
|
}, h.SearchEngine, config)
|
||||||
@@ -130,7 +135,12 @@ func (h *MainHelper) ToggleReplicasOff() {
|
|||||||
}
|
}
|
||||||
h.Settings.DataSourceReplicas = []string{}
|
h.Settings.DataSourceReplicas = []string{}
|
||||||
lic := h.SQLStore.GetLicense()
|
lic := h.SQLStore.GetLicense()
|
||||||
h.SQLStore = sqlstore.New(*h.Settings, nil)
|
|
||||||
|
var err error
|
||||||
|
h.SQLStore, err = sqlstore.New(*h.Settings, nil)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
h.SQLStore.UpdateLicense(lic)
|
h.SQLStore.UpdateLicense(lic)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -140,7 +150,13 @@ func (h *MainHelper) ToggleReplicasOn() {
|
|||||||
}
|
}
|
||||||
h.Settings.DataSourceReplicas = h.replicas
|
h.Settings.DataSourceReplicas = h.replicas
|
||||||
lic := h.SQLStore.GetLicense()
|
lic := h.SQLStore.GetLicense()
|
||||||
h.SQLStore = sqlstore.New(*h.Settings, nil)
|
|
||||||
|
var err error
|
||||||
|
h.SQLStore, err = sqlstore.New(*h.Settings, nil)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
h.SQLStore.UpdateLicense(lic)
|
h.SQLStore.UpdateLicense(lic)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -113,7 +113,10 @@ func initDbCmdF(command *cobra.Command, _ []string) error {
|
|||||||
}
|
}
|
||||||
defer configStore.Close()
|
defer configStore.Close()
|
||||||
|
|
||||||
sqlStore := sqlstore.New(configStore.Get().SqlSettings, nil)
|
sqlStore, err := sqlstore.New(configStore.Get().SqlSettings, nil)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrap(err, "failed to initialize store")
|
||||||
|
}
|
||||||
defer sqlStore.Close()
|
defer sqlStore.Close()
|
||||||
|
|
||||||
CommandPrettyPrintln("Database store correctly initialised")
|
CommandPrettyPrintln("Database store correctly initialised")
|
||||||
|
|||||||
@@ -64,5 +64,5 @@ func initStoreCommandContextCobra(command *cobra.Command) (store.Store, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
config := cfgStore.Get()
|
config := cfgStore.Get()
|
||||||
return sqlstore.New(config.SqlSettings, nil), nil
|
return sqlstore.New(config.SqlSettings, nil)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -49,7 +49,12 @@ func (s *BleveEngineTestSuite) setupStore() {
|
|||||||
driverName = model.DatabaseDriverPostgres
|
driverName = model.DatabaseDriverPostgres
|
||||||
}
|
}
|
||||||
s.SQLSettings = storetest.MakeSqlSettings(driverName, false)
|
s.SQLSettings = storetest.MakeSqlSettings(driverName, false)
|
||||||
s.SQLStore = sqlstore.New(*s.SQLSettings, nil)
|
|
||||||
|
var err error
|
||||||
|
s.SQLStore, err = sqlstore.New(*s.SQLSettings, nil)
|
||||||
|
if err != nil {
|
||||||
|
s.Require().FailNow("Cannot initialize store: %s", err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
cfg := &model.Config{}
|
cfg := &model.Config{}
|
||||||
cfg.SetDefaults()
|
cfg.SetDefaults()
|
||||||
|
|||||||
Ссылка в новой задаче
Block a user