diff --git a/pkg/storage/unified/sql/test/benchmark_test.go b/pkg/storage/unified/sql/test/benchmark_test.go index 128a344d891..f9665c83b7d 100644 --- a/pkg/storage/unified/sql/test/benchmark_test.go +++ b/pkg/storage/unified/sql/test/benchmark_test.go @@ -1,44 +1,19 @@ package test import ( - "context" "testing" "time" - "github.com/stretchr/testify/require" - "github.com/grafana/grafana/pkg/infra/db" - "github.com/grafana/grafana/pkg/setting" - "github.com/grafana/grafana/pkg/storage/unified/resource" - "github.com/grafana/grafana/pkg/storage/unified/sql" - "github.com/grafana/grafana/pkg/storage/unified/sql/db/dbimpl" test "github.com/grafana/grafana/pkg/storage/unified/testing" "github.com/grafana/grafana/pkg/util/testutil" ) -func newTestBackend(b testing.TB) resource.StorageBackend { - dbstore := db.InitTestDB(b) - eDB, err := dbimpl.ProvideResourceDB(dbstore, setting.NewCfg(), nil) - require.NoError(b, err) - require.NotNil(b, eDB) - - backend, err := sql.NewBackend(sql.BackendOptions{ - DBProvider: eDB, - IsHA: true, - SimulatedNetworkLatency: 2 * time.Millisecond, // to simulate some network latency - }) - require.NoError(b, err) - require.NotNil(b, backend) - err = backend.Init(context.Background()) - require.NoError(b, err) - return backend -} - func TestIntegrationBenchmarkSQLStorageBackend(t *testing.T) { testutil.SkipIntegrationTestInShortMode(t) opts := test.DefaultBenchmarkOptions() if db.IsTestDbSQLite() { opts.Concurrency = 1 // to avoid SQLite database is locked error } - test.BenchmarkStorageBackend(t, newTestBackend(t), opts) + test.BenchmarkStorageBackend(t, newTestBackend(t, true, 2*time.Millisecond), opts) } diff --git a/pkg/storage/unified/sql/test/integration_test.go b/pkg/storage/unified/sql/test/integration_test.go index b8ccd7cef1b..0166a4a96f7 100644 --- a/pkg/storage/unified/sql/test/integration_test.go +++ b/pkg/storage/unified/sql/test/integration_test.go @@ -3,6 +3,7 @@ package test import ( "context" "os" + "sync" "testing" "time" @@ -30,6 +31,39 @@ import ( "github.com/grafana/grafana/pkg/util/testutil" ) +// initMutex is a global lock to ensure that database initialization and migration +// only happens once at a time across all concurrent integration tests in this package. +// This prevents race conditions where multiple tests try to alter the same table simultaneously. +var initMutex = &sync.Mutex{} + +// newTestBackend creates a fresh database and backend for a test. +// It uses a mutex to ensure the entire initialization and migration +// process is atomic and does not race with other parallel tests. +func newTestBackend(t *testing.T, isHA bool, simulatedNetworkLatency time.Duration) resource.StorageBackend { + // Lock to ensure the entire init block is atomic. + initMutex.Lock() + // Unlock once the function returns the initialized backend. + defer initMutex.Unlock() + + dbstore := db.InitTestDB(t) + eDB, err := dbimpl.ProvideResourceDB(dbstore, setting.NewCfg(), nil) + require.NoError(t, err) + require.NotNil(t, eDB) + + backend, err := sql.NewBackend(sql.BackendOptions{ + DBProvider: eDB, + IsHA: isHA, + SimulatedNetworkLatency: simulatedNetworkLatency, + }) + require.NoError(t, err) + require.NotNil(t, backend) + + // Use a context with a reasonable timeout for migrations. + err = backend.Init(testutil.NewTestContext(t, time.Now().Add(1*time.Minute))) + require.NoError(t, err) + return backend +} + func TestMain(m *testing.M) { testsuite.Run(m) } @@ -38,20 +72,7 @@ func TestIntegrationStorageServer(t *testing.T) { testutil.SkipIntegrationTestInShortMode(t) unitest.RunStorageServerTest(t, func(ctx context.Context) resource.StorageBackend { - dbstore := db.InitTestDB(t) - eDB, err := dbimpl.ProvideResourceDB(dbstore, setting.NewCfg(), nil) - require.NoError(t, err) - require.NotNil(t, eDB) - - backend, err := sql.NewBackend(sql.BackendOptions{ - DBProvider: eDB, - IsHA: true, - }) - require.NoError(t, err) - require.NotNil(t, backend) - err = backend.Init(testutil.NewTestContext(t, time.Now().Add(1*time.Minute))) - require.NoError(t, err) - return backend + return newTestBackend(t, true, 0) }) } @@ -61,39 +82,13 @@ func TestIntegrationSQLStorageBackend(t *testing.T) { t.Run("IsHA (polling notifier)", func(t *testing.T) { unitest.RunStorageBackendTest(t, func(ctx context.Context) resource.StorageBackend { - dbstore := db.InitTestDB(t) - eDB, err := dbimpl.ProvideResourceDB(dbstore, setting.NewCfg(), nil) - require.NoError(t, err) - require.NotNil(t, eDB) - - backend, err := sql.NewBackend(sql.BackendOptions{ - DBProvider: eDB, - IsHA: true, - }) - require.NoError(t, err) - require.NotNil(t, backend) - err = backend.Init(testutil.NewTestContext(t, time.Now().Add(1*time.Minute))) - require.NoError(t, err) - return backend + return newTestBackend(t, true, 0) }, nil) }) t.Run("NotHA (in process notifier)", func(t *testing.T) { unitest.RunStorageBackendTest(t, func(ctx context.Context) resource.StorageBackend { - dbstore := db.InitTestDB(t) - eDB, err := dbimpl.ProvideResourceDB(dbstore, setting.NewCfg(), nil) - require.NoError(t, err) - require.NotNil(t, eDB) - - backend, err := sql.NewBackend(sql.BackendOptions{ - DBProvider: eDB, - IsHA: false, - }) - require.NoError(t, err) - require.NotNil(t, backend) - err = backend.Init(testutil.NewTestContext(t, time.Now().Add(1*time.Minute))) - require.NoError(t, err) - return backend + return newTestBackend(t, false, 0) }, nil) }) } @@ -118,20 +113,10 @@ func TestIntegrationSearchAndStorage(t *testing.T) { t.Cleanup(search.CloseAllIndexes) // Create a new resource backend - dbstore := db.InitTestDB(t) - eDB, err := dbimpl.ProvideResourceDB(dbstore, setting.NewCfg(), nil) - require.NoError(t, err) - require.NotNil(t, eDB) - - storage, err := sql.NewBackend(sql.BackendOptions{ - DBProvider: eDB, - IsHA: false, - }) - require.NoError(t, err) + storage := newTestBackend(t, false, 0) require.NotNil(t, storage) - err = storage.Init(ctx) - require.NoError(t, err) + // Run the shared storage and search tests unitest.RunTestSearchAndStorage(t, ctx, storage, search) }