2019-01-24 16:26:36 +00:00
// Copyright (C) 2019 Storj Labs, Inc.
2018-12-10 14:50:12 +00:00
// See LICENSE for copying information.
package satellitedbtest
// This package should be referenced only in test files!
import (
2020-01-13 13:18:48 +00:00
"context"
2020-03-27 16:18:19 +00:00
"fmt"
2019-10-18 20:03:10 +01:00
"strconv"
2019-02-04 20:37:46 +00:00
"strings"
2018-12-10 14:50:12 +00:00
"testing"
2019-12-04 03:36:21 +00:00
"github.com/zeebo/errs"
"go.uber.org/zap"
2019-02-14 21:55:21 +00:00
"go.uber.org/zap/zaptest"
2020-01-19 16:29:15 +00:00
2020-01-13 13:18:48 +00:00
"storj.io/common/testcontext"
2019-12-04 03:36:21 +00:00
"storj.io/storj/private/dbutil"
2020-04-27 20:34:42 +01:00
"storj.io/storj/private/dbutil/pgtest"
2019-11-14 19:46:15 +00:00
"storj.io/storj/private/dbutil/pgutil"
2019-12-04 03:36:21 +00:00
"storj.io/storj/private/dbutil/tempdb"
2018-12-27 09:56:25 +00:00
"storj.io/storj/satellite"
2019-12-04 03:36:21 +00:00
"storj.io/storj/satellite/metainfo"
2018-12-10 14:50:12 +00:00
"storj.io/storj/satellite/satellitedb"
2020-01-15 02:29:51 +00:00
"storj.io/storj/satellite/satellitedb/dbx"
2018-12-10 14:50:12 +00:00
)
2019-10-04 15:12:21 +01:00
// SatelliteDatabases maybe name can be better
type SatelliteDatabases struct {
2020-04-27 17:28:40 +01:00
Name string
2019-10-04 15:12:21 +01:00
MasterDB Database
PointerDB Database
}
2019-02-04 20:37:46 +00:00
// Database describes a test database
type Database struct {
Name string
URL string
Message string
}
2020-04-27 20:34:42 +01:00
type ignoreSkip struct { }
func ( ignoreSkip ) Skip ( ... interface { } ) { }
2019-02-04 20:37:46 +00:00
// Databases returns default databases.
2019-10-04 15:12:21 +01:00
func Databases ( ) [ ] SatelliteDatabases {
2020-04-27 20:34:42 +01:00
cockroachConnStr := pgtest . PickCockroach ( ignoreSkip { } )
postgresConnStr := pgtest . PickPostgres ( ignoreSkip { } )
2019-10-04 15:12:21 +01:00
return [ ] SatelliteDatabases {
{
2020-04-27 17:28:40 +01:00
Name : "Postgres" ,
2020-04-27 20:34:42 +01:00
MasterDB : Database { "Postgres" , postgresConnStr , "Postgres flag missing, example: -postgres-test-db=" + pgtest . DefaultPostgres + " or use STORJ_POSTGRES_TEST environment variable." } ,
PointerDB : Database { "Postgres" , postgresConnStr , "" } ,
2019-10-04 15:12:21 +01:00
} ,
2019-11-22 19:59:46 +00:00
{
2020-04-27 17:28:40 +01:00
Name : "Cockroach" ,
2020-04-27 20:34:42 +01:00
MasterDB : Database { "Cockroach" , cockroachConnStr , "Cockroach flag missing, example: -cockroach-test-db=" + pgtest . DefaultCockroach + " or use STORJ_COCKROACH_TEST environment variable." } ,
PointerDB : Database { "Cockroach" , cockroachConnStr , "" } ,
2019-11-22 19:59:46 +00:00
} ,
2019-02-04 20:37:46 +00:00
}
}
2019-10-18 20:03:10 +01:00
// SchemaSuffix returns a suffix for schemas.
func SchemaSuffix ( ) string {
return pgutil . CreateRandomTestingSchemaName ( 6 )
}
// SchemaName returns a properly formatted schema string.
func SchemaName ( testname , category string , index int , schemaSuffix string ) string {
// postgres has a maximum schema length of 64
// we need additional 6 bytes for the random suffix
// and 4 bytes for the satellite index "/S0/""
indexStr := strconv . Itoa ( index )
var maxTestNameLen = 64 - len ( category ) - len ( indexStr ) - len ( schemaSuffix ) - 2
if len ( testname ) > maxTestNameLen {
testname = testname [ : maxTestNameLen ]
}
if schemaSuffix == "" {
return strings . ToLower ( testname + "/" + category + indexStr )
}
return strings . ToLower ( testname + "/" + schemaSuffix + "/" + category + indexStr )
}
2019-12-04 03:36:21 +00:00
// tempMasterDB is a satellite.DB-implementing type that cleans up after itself when closed.
type tempMasterDB struct {
satellite . DB
tempDB * dbutil . TempDatabase
}
// Close closes a tempMasterDB and cleans it up afterward.
func ( db * tempMasterDB ) Close ( ) error {
return errs . Combine ( db . DB . Close ( ) , db . tempDB . Close ( ) )
}
// TestDBAccess provides a somewhat regularized access to the underlying DB
func ( db * tempMasterDB ) TestDBAccess ( ) * dbx . DB {
return db . DB . ( interface { TestDBAccess ( ) * dbx . DB } ) . TestDBAccess ( )
}
2019-11-26 16:39:57 +00:00
// CreateMasterDB creates a new satellite database for testing
2020-03-27 16:18:19 +00:00
func CreateMasterDB ( ctx context . Context , log * zap . Logger , name string , category string , index int , dbInfo Database ) ( db satellite . DB , err error ) {
2019-11-26 16:39:57 +00:00
if dbInfo . URL == "" {
2020-03-27 16:18:19 +00:00
return nil , fmt . Errorf ( "Database %s connection string not provided. %s" , dbInfo . Name , dbInfo . Message )
2019-11-26 16:39:57 +00:00
}
schemaSuffix := SchemaSuffix ( )
2020-03-27 16:18:19 +00:00
log . Debug ( "creating" , zap . String ( "suffix" , schemaSuffix ) )
schema := SchemaName ( name , category , index , schemaSuffix )
2019-11-26 16:39:57 +00:00
2020-01-13 13:18:48 +00:00
tempDB , err := tempdb . OpenUnique ( ctx , dbInfo . URL , schema )
2019-12-04 03:36:21 +00:00
if err != nil {
return nil , err
2019-11-26 16:39:57 +00:00
}
2019-12-04 03:36:21 +00:00
2020-03-27 16:18:19 +00:00
return CreateMasterDBOnTopOf ( log , tempDB )
2019-12-04 03:36:21 +00:00
}
// CreateMasterDBOnTopOf creates a new satellite database on top of an already existing
// temporary database.
func CreateMasterDBOnTopOf ( log * zap . Logger , tempDB * dbutil . TempDatabase ) ( db satellite . DB , err error ) {
2020-01-10 01:12:27 +00:00
masterDB , err := satellitedb . New ( log . Named ( "db" ) , tempDB . ConnStr , satellitedb . Options { } )
2019-12-04 03:36:21 +00:00
return & tempMasterDB { DB : masterDB , tempDB : tempDB } , err
}
// tempPointerDB is a satellite.DB-implementing type that cleans up after itself when closed.
type tempPointerDB struct {
metainfo . PointerDB
tempDB * dbutil . TempDatabase
}
// Close closes a tempPointerDB and cleans it up afterward.
func ( db * tempPointerDB ) Close ( ) error {
return errs . Combine ( db . PointerDB . Close ( ) , db . tempDB . Close ( ) )
}
// CreatePointerDB creates a new satellite pointer database for testing
2020-03-27 16:18:19 +00:00
func CreatePointerDB ( ctx context . Context , log * zap . Logger , name string , category string , index int , dbInfo Database ) ( db metainfo . PointerDB , err error ) {
2019-12-04 03:36:21 +00:00
if dbInfo . URL == "" {
2020-03-27 16:18:19 +00:00
return nil , fmt . Errorf ( "Database %s connection string not provided. %s" , dbInfo . Name , dbInfo . Message )
2019-12-04 03:36:21 +00:00
}
schemaSuffix := SchemaSuffix ( )
2020-03-27 16:18:19 +00:00
log . Debug ( "creating" , zap . String ( "suffix" , schemaSuffix ) )
2019-12-04 03:36:21 +00:00
2020-03-27 16:18:19 +00:00
schema := SchemaName ( name , category , index , schemaSuffix )
2019-12-04 03:36:21 +00:00
2020-01-13 13:18:48 +00:00
tempDB , err := tempdb . OpenUnique ( ctx , dbInfo . URL , schema )
2019-12-04 03:36:21 +00:00
if err != nil {
return nil , err
}
2020-03-27 16:18:19 +00:00
return CreatePointerDBOnTopOf ( ctx , log , tempDB )
2019-12-04 03:36:21 +00:00
}
// CreatePointerDBOnTopOf creates a new satellite database on top of an already existing
// temporary database.
2020-01-13 13:18:48 +00:00
func CreatePointerDBOnTopOf ( ctx context . Context , log * zap . Logger , tempDB * dbutil . TempDatabase ) ( db metainfo . PointerDB , err error ) {
2019-12-04 03:36:21 +00:00
pointerDB , err := metainfo . NewStore ( log . Named ( "pointerdb" ) , tempDB . ConnStr )
2020-04-24 20:15:27 +01:00
if err != nil {
return nil , err
}
err = pointerDB . MigrateToLatest ( ctx )
2019-12-04 03:36:21 +00:00
return & tempPointerDB { PointerDB : pointerDB , tempDB : tempDB } , err
2019-11-26 16:39:57 +00:00
}
2018-12-10 14:50:12 +00:00
// Run method will iterate over all supported databases. Will establish
// connection and will create tables for each DB.
2020-01-19 16:29:15 +00:00
func Run ( t * testing . T , test func ( ctx * testcontext . Context , t * testing . T , db satellite . DB ) ) {
2019-02-04 20:37:46 +00:00
for _ , dbInfo := range Databases ( ) {
2019-02-06 09:16:05 +00:00
dbInfo := dbInfo
2020-06-11 10:37:13 +01:00
if strings . EqualFold ( dbInfo . MasterDB . URL , "omit" ) {
continue
}
2020-04-27 17:28:40 +01:00
t . Run ( dbInfo . Name , func ( t * testing . T ) {
2019-02-05 19:44:00 +00:00
t . Parallel ( )
2020-01-13 13:18:48 +00:00
ctx := testcontext . New ( t )
defer ctx . Cleanup ( )
2019-12-30 12:17:41 +00:00
if dbInfo . MasterDB . URL == "" {
t . Skipf ( "Database %s connection string not provided. %s" , dbInfo . MasterDB . Name , dbInfo . MasterDB . Message )
}
2020-03-27 16:18:19 +00:00
db , err := CreateMasterDB ( ctx , zaptest . NewLogger ( t ) , t . Name ( ) , "T" , 0 , dbInfo . MasterDB )
2018-12-10 14:50:12 +00:00
if err != nil {
t . Fatal ( err )
}
defer func ( ) {
2019-10-18 20:03:10 +01:00
err := db . Close ( )
2018-12-10 14:50:12 +00:00
if err != nil {
t . Fatal ( err )
}
} ( )
2020-04-30 07:36:59 +01:00
err = db . TestingMigrateToLatest ( ctx )
2018-12-10 14:50:12 +00:00
if err != nil {
t . Fatal ( err )
}
2020-01-19 16:29:15 +00:00
test ( ctx , t , db )
2018-12-10 14:50:12 +00:00
} )
}
}
2019-05-19 16:10:46 +01:00
// Bench method will iterate over all supported databases. Will establish
// connection and will create tables for each DB.
func Bench ( b * testing . B , bench func ( b * testing . B , db satellite . DB ) ) {
for _ , dbInfo := range Databases ( ) {
dbInfo := dbInfo
2020-06-11 10:37:13 +01:00
if strings . EqualFold ( dbInfo . MasterDB . URL , "omit" ) {
continue
}
2020-04-27 17:28:40 +01:00
b . Run ( dbInfo . Name , func ( b * testing . B ) {
2019-10-04 15:12:21 +01:00
if dbInfo . MasterDB . URL == "" {
b . Skipf ( "Database %s connection string not provided. %s" , dbInfo . MasterDB . Name , dbInfo . MasterDB . Message )
2019-05-19 16:10:46 +01:00
}
2020-01-13 13:18:48 +00:00
ctx := testcontext . New ( b )
defer ctx . Cleanup ( )
2020-03-27 16:18:19 +00:00
db , err := CreateMasterDB ( ctx , zap . NewNop ( ) , b . Name ( ) , "X" , 0 , dbInfo . MasterDB )
2019-05-19 16:10:46 +01:00
if err != nil {
b . Fatal ( err )
}
defer func ( ) {
2019-10-18 20:03:10 +01:00
err := db . Close ( )
2019-05-19 16:10:46 +01:00
if err != nil {
b . Fatal ( err )
}
} ( )
2020-04-30 07:36:59 +01:00
err = db . MigrateToLatest ( ctx )
2019-05-19 16:10:46 +01:00
if err != nil {
b . Fatal ( err )
}
2020-01-13 13:18:48 +00:00
// TODO: pass the ctx down
2019-05-19 16:10:46 +01:00
bench ( b , db )
} )
}
}