Files
mattermost/server/channels/store/sqlstore/testpool.go
T
Jesse HallamandMattermost Build cad4a4509c Disable morph logging in NewTestPool (#36308)
Every test binary that uses TestPool builds 16 stores in parallel, each
running the full migration set. Without DisableMorphLogging() the morph
debug stream from each store flows through to the test logger (which is
configured at LvlTrace), producing tens of thousands of "migrating (up)"
lines per shard — amplified further on shards that re-run flaky tests,
since every re-run spawns a fresh TestMain and a fresh pool.

Migration failures are still surfaced: engine.ApplyAll returns the
error, sqlstore.New wraps it as "failed to apply database migrations",
and both NewTestPool callers panic on a non-nil result.

Co-authored-by: Mattermost Build <build@mattermost.com>
2026-04-30 17:28:50 +02:00

116 lines
2.7 KiB
Go

// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
package sqlstore
import (
"sync"
"testing"
"golang.org/x/sync/errgroup"
"github.com/mattermost/mattermost/server/public/model"
"github.com/mattermost/mattermost/server/public/shared/mlog"
"github.com/mattermost/mattermost/server/v8/channels/store/storetest"
)
// TestPool is used to facilitate the efficient (and safe) use of test stores in parallel tests (e.g. in api4, app, sqlstore).
type TestPool struct {
entries map[string]*TestPoolEntry
mut sync.Mutex
logger mlog.LoggerIFace
}
type TestPoolEntry struct {
Store *SqlStore
Settings *model.SqlSettings
}
func NewTestPool(logger mlog.LoggerIFace, driverName string, poolSize int) (*TestPool, error) {
logger.Info("Creating test store pool", mlog.Int("poolSize", poolSize))
entries := make(map[string]*TestPoolEntry, poolSize)
var mut sync.Mutex
var eg errgroup.Group
for range poolSize {
eg.Go(func() error {
settings := storetest.MakeSqlSettings(driverName)
sqlStore, err := New(*settings, logger, nil, WithFeatureFlags(func() *model.FeatureFlags {
return &model.FeatureFlags{CJKSearch: true}
}), DisableMorphLogging())
if err != nil {
return err
}
mut.Lock()
logger.Info("Initializing test store in pool", mlog.String("datasource", *settings.DataSource))
entries[*settings.DataSource] = &TestPoolEntry{
Store: sqlStore,
Settings: settings,
}
mut.Unlock()
return nil
})
}
if err := eg.Wait(); err != nil {
return nil, err
}
return &TestPool{
entries: entries,
logger: logger,
}, nil
}
func (p *TestPool) Get(t testing.TB) *TestPoolEntry {
p.mut.Lock()
defer p.mut.Unlock()
p.logger.Info("Getting from test store pool", mlog.Int("poolSize", len(p.entries)))
var poolEntry *TestPoolEntry
for _, entry := range p.entries {
poolEntry = entry
delete(p.entries, *entry.Settings.DataSource)
break
}
// No more stores available in the pool
if poolEntry == nil {
return nil
}
p.logger.Info("Got store from pool", mlog.String("datasource", *poolEntry.Settings.DataSource), mlog.Int("poolSize", len(p.entries)))
dataSource := *poolEntry.Settings.DataSource
// Return store to pool on test cleanup
t.Cleanup(func() {
p.mut.Lock()
defer p.mut.Unlock()
p.logger.Info("Returning to test store pool", mlog.String("datasource", dataSource), mlog.Int("poolSize", len(p.entries)))
p.entries[dataSource] = poolEntry
})
return poolEntry
}
func (p *TestPool) Close() {
p.mut.Lock()
defer p.mut.Unlock()
var wg sync.WaitGroup
wg.Add(len(p.entries))
for _, entry := range p.entries {
go func() {
defer wg.Done()
entry.Store.Close()
storetest.CleanupSqlSettings(entry.Settings)
}()
}
wg.Wait()
}