storj/private/testplanet/run.go
Michal Niewrzal 36cbf4f0b8 private/testplanet: enable full table scan detection
Testplanet tests will print into logs (WARN) if full table scan will
be detected. Test won't be failed automatically. That's because
currently we have multiple queries which are doing full table scan and it's not trivial to change.

https://github.com/storj/storj/issues/5471

Change-Id: Ia2fcbfb9102424d58f95e00071329454a8c1066e
2023-02-03 12:11:18 +00:00

186 lines
5.3 KiB
Go

// Copyright (C) 2019 Storj Labs, Inc.
// See LICENSE for copying information
package testplanet
import (
"context"
"runtime/pprof"
"testing"
"github.com/google/go-cmp/cmp"
"go.uber.org/zap"
"storj.io/common/context2"
"storj.io/common/grant"
"storj.io/common/storj"
"storj.io/common/testcontext"
"storj.io/private/dbutil"
"storj.io/private/dbutil/pgtest"
"storj.io/private/dbutil/pgutil"
"storj.io/private/tagsql"
"storj.io/storj/private/testmonkit"
"storj.io/storj/satellite/satellitedb/satellitedbtest"
"storj.io/uplink"
)
// Run runs testplanet in multiple configurations.
func Run(t *testing.T, config Config, test func(t *testing.T, ctx *testcontext.Context, planet *Planet)) {
databases := satellitedbtest.Databases()
if len(databases) == 0 {
t.Fatal("Databases flag missing, set at least one:\n" +
"-postgres-test-db=" + pgtest.DefaultPostgres + "\n" +
"-cockroach-test-db=" + pgtest.DefaultCockroach)
}
for _, satelliteDB := range databases {
satelliteDB := satelliteDB
t.Run(satelliteDB.Name, func(t *testing.T) {
parallel := !config.NonParallel
if parallel {
t.Parallel()
}
if satelliteDB.MasterDB.URL == "" {
t.Skipf("Database %s connection string not provided. %s", satelliteDB.MasterDB.Name, satelliteDB.MasterDB.Message)
}
planetConfig := config
if planetConfig.Name == "" {
planetConfig.Name = t.Name()
}
log := NewLogger(t)
testmonkit.Run(context.Background(), t, func(parent context.Context) {
defer pprof.SetGoroutineLabels(parent)
parent = pprof.WithLabels(parent, pprof.Labels("test", t.Name()))
timeout := config.Timeout
if timeout == 0 {
timeout = testcontext.DefaultTimeout
}
ctx := testcontext.NewWithContextAndTimeout(parent, t, timeout)
defer ctx.Cleanup()
planetConfig.applicationName = "testplanet" + pgutil.CreateRandomTestingSchemaName(6)
planet, err := NewCustom(ctx, log, planetConfig, satelliteDB)
if err != nil {
t.Fatalf("%+v", err)
}
defer ctx.Check(planet.Shutdown)
planet.Start(ctx)
provisionUplinks(ctx, t, planet)
var rawDB tagsql.DB
var queriesBefore []string
if len(planet.Satellites) > 0 && satelliteDB.Name == "Cockroach" {
db := planet.Satellites[0].DB
// not perfect but didn't find better way to do this
rawDB = db.(interface{ DebugGetDBHandle() tagsql.DB }).DebugGetDBHandle()
var err error
queriesBefore, err = satellitedbtest.FullTableScanQueries(ctx, rawDB, dbutil.Cockroach, planetConfig.applicationName)
if err != nil {
t.Fatalf("%+v", err)
}
}
test(t, ctx, planet)
if rawDB != nil {
queriesAfter, err := satellitedbtest.FullTableScanQueries(context2.WithoutCancellation(ctx), rawDB, dbutil.Cockroach, planetConfig.applicationName)
if err != nil {
t.Fatalf("%+v", err)
}
diff := cmp.Diff(queriesBefore, queriesAfter)
if diff != "" {
log.Sugar().Warnf("FULL TABLE SCAN DETECTED\n%s", diff)
}
}
})
})
}
}
// Bench makes benchmark with testplanet as easy as running unit tests with Run method.
func Bench(b *testing.B, config Config, bench func(b *testing.B, ctx *testcontext.Context, planet *Planet)) {
databases := satellitedbtest.Databases()
if len(databases) == 0 {
b.Fatal("Databases flag missing, set at least one:\n" +
"-postgres-test-db=" + pgtest.DefaultPostgres + "\n" +
"-cockroach-test-db=" + pgtest.DefaultCockroach)
}
for _, satelliteDB := range databases {
satelliteDB := satelliteDB
b.Run(satelliteDB.Name, func(b *testing.B) {
if satelliteDB.MasterDB.URL == "" {
b.Skipf("Database %s connection string not provided. %s", satelliteDB.MasterDB.Name, satelliteDB.MasterDB.Message)
}
log := zap.NewNop()
planetConfig := config
if planetConfig.Name == "" {
planetConfig.Name = b.Name()
}
testmonkit.Run(context.Background(), b, func(parent context.Context) {
defer pprof.SetGoroutineLabels(parent)
parent = pprof.WithLabels(parent, pprof.Labels("test", b.Name()))
timeout := config.Timeout
if timeout == 0 {
timeout = testcontext.DefaultTimeout
}
ctx := testcontext.NewWithContextAndTimeout(parent, b, timeout)
defer ctx.Cleanup()
planetConfig.applicationName = "testplanet-bench"
planet, err := NewCustom(ctx, log, planetConfig, satelliteDB)
if err != nil {
b.Fatalf("%+v", err)
}
defer ctx.Check(planet.Shutdown)
planet.Start(ctx)
provisionUplinks(ctx, b, planet)
bench(b, ctx, planet)
})
})
}
}
func provisionUplinks(ctx context.Context, t testing.TB, planet *Planet) {
for _, planetUplink := range planet.Uplinks {
for _, satellite := range planet.Satellites {
apiKey := planetUplink.APIKey[satellite.ID()]
// create access grant manually to avoid dialing satellite for
// project id and deriving key with argon2.IDKey method
encAccess := grant.NewEncryptionAccessWithDefaultKey(&storj.Key{})
encAccess.SetDefaultPathCipher(storj.EncAESGCM)
grantAccess := grant.Access{
SatelliteAddress: satellite.URL(),
APIKey: apiKey,
EncAccess: encAccess,
}
serializedAccess, err := grantAccess.Serialize()
if err != nil {
t.Fatalf("%+v", err)
}
access, err := uplink.ParseAccess(serializedAccess)
if err != nil {
t.Fatalf("%+v", err)
}
planetUplink.Access[satellite.ID()] = access
}
}
}