@@ -31,7 +31,6 @@ import (
3131
3232 "github.com/docker/docker/api/types"
3333 "github.com/docker/docker/api/types/container"
34- imagetypes "github.com/docker/docker/api/types/image"
3534 "github.com/docker/docker/client"
3635 "github.com/docker/docker/pkg/stdcopy"
3736 "github.com/docker/go-connections/nat"
@@ -546,44 +545,12 @@ func setup(dc *dockerContext, base, backupFile, compactBackupFile string) (mysql
546545 s3server := httptest .NewServer (s3 .Server ())
547546 s3url = s3server .URL
548547
549- // start the mysql container; configure it for lots of debug logging, in case we need it
550- mysqlConf := `
551- [mysqld]
552- log_error =/var/log/mysql/mysql_error.log
553- general_log_file=/var/log/mysql/mysql.log
554- general_log =1
555- slow_query_log =1
556- slow_query_log_file=/var/log/mysql/mysql_slow.log
557- long_query_time =2
558- log_queries_not_using_indexes = 1
559- `
560- confFile := filepath .Join (base , "log.cnf" )
561- if err := os .WriteFile (confFile , []byte (mysqlConf ), 0644 ); err != nil {
562- return mysql , smb , s3url , s3backend , fmt .Errorf ("failed to write mysql config file: %v" , err )
563- }
564- logDir := filepath .Join (base , "mysql_logs" )
565- if err := os .Mkdir (logDir , 0755 ); err != nil {
566- return mysql , smb , s3url , s3backend , fmt .Errorf ("failed to create mysql log directory: %v" , err )
567- }
568- // ensure we have mysql image
569- resp , err := dc .cli .ImagePull (context .Background (), mysqlImage , imagetypes.PullOptions {})
570- if err != nil {
571- return mysql , smb , s3url , s3backend , fmt .Errorf ("failed to pull mysql image: %v" , err )
572- }
573- _ , _ = io .Copy (os .Stdout , resp )
574- _ = resp .Close ()
575- mysqlCID , mysqlPort , err := dc .startContainer (mysqlImage , "mysql" , "3306/tcp" , []string {fmt .Sprintf ("%s:/etc/mysql/conf.d/log.conf:ro" , confFile ), fmt .Sprintf ("%s:/var/log/mysql" , logDir )}, nil , []string {
576- fmt .Sprintf ("MYSQL_ROOT_PASSWORD=%s" , mysqlRootPass ),
577- "MYSQL_DATABASE=tester" ,
578- fmt .Sprintf ("MYSQL_USER=%s" , mysqlUser ),
579- fmt .Sprintf ("MYSQL_PASSWORD=%s" , mysqlPass ),
580- })
548+ mysql , err = startDatabase (dc , base , mysqlImage , "mysql" )
581549 if err != nil {
582- return
550+ return mysql , smb , s3url , s3backend , fmt . Errorf ( "failed to start mysql container: %v" , err )
583551 }
584- mysql = containerPort {name : "mysql" , id : mysqlCID , port : mysqlPort }
585552
586- if err = dc .waitForDBConnectionAndGrantPrivileges (mysqlCID , mysqlRootUser , mysqlRootPass ); err != nil {
553+ if err = dc .waitForDBConnectionAndGrantPrivileges (mysql . id , mysqlRootUser , mysqlRootPass ); err != nil {
587554 return
588555 }
589556
@@ -890,58 +857,119 @@ func populatePrePost(base string, targets []backupTarget) (err error) {
890857 return nil
891858}
892859
893- func startDatabase (dc * dockerContext , baseDir , image , name string ) (containerPort , error ) {
894- resp , err := dc .cli .ImagePull (context .Background (), image , imagetypes.PullOptions {})
895- if err != nil {
896- return containerPort {}, fmt .Errorf ("failed to pull mysql image: %v" , err )
897- }
898- _ , _ = io .Copy (os .Stdout , resp )
899- _ = resp .Close ()
900-
901- // start the mysql container; configure it for lots of debug logging, in case we need it
902- mysqlConf := `
903- [mysqld]
904- log_error =/var/log/mysql/mysql_error.log
905- general_log_file=/var/log/mysql/mysql.log
906- general_log =1
907- slow_query_log =1
908- slow_query_log_file=/var/log/mysql/mysql_slow.log
909- long_query_time =2
910- log_queries_not_using_indexes = 1
911- `
912- if err := os .Mkdir (baseDir , 0o755 ); err != nil {
913- return containerPort {}, fmt .Errorf ("failed to create mysql base directory: %v" , err )
914- }
915- confFile := filepath .Join (baseDir , "log.cnf" )
916- if err := os .WriteFile (confFile , []byte (mysqlConf ), 0644 ); err != nil {
917- return containerPort {}, fmt .Errorf ("failed to write mysql config file: %v" , err )
918- }
919- logDir := filepath .Join (baseDir , "mysql_logs" )
920- if err := os .Mkdir (logDir , 0755 ); err != nil {
921- return containerPort {}, fmt .Errorf ("failed to create mysql log directory: %v" , err )
922- }
923-
924- // start mysql
925- cid , port , err := dc .startContainer (
926- image , name , "3306/tcp" , []string {fmt .Sprintf ("%s:/etc/mysql/conf.d/log.conf:ro" , confFile ), fmt .Sprintf ("%s:/var/log/mysql" , logDir )}, nil , []string {
927- fmt .Sprintf ("MYSQL_ROOT_PASSWORD=%s" , mysqlRootPass ),
928- "MYSQL_DATABASE=tester" ,
929- fmt .Sprintf ("MYSQL_USER=%s" , mysqlUser ),
930- fmt .Sprintf ("MYSQL_PASSWORD=%s" , mysqlPass ),
931- })
932- if err != nil {
933- return containerPort {}, fmt .Errorf ("failed to start mysql container: %v" , err )
934- }
935- return containerPort {name : name , id : cid , port : port }, nil
936- }
937-
938860func TestIntegration (t * testing.T ) {
939861 CheckSkipIntegration (t , "integration" )
940862 syscall .Umask (0 )
941863 dc , err := getDockerContext ()
942864 if err != nil {
943865 t .Fatalf ("failed to get docker client: %v" , err )
944866 }
867+ t .Run ("parallel databases" , func (t * testing.T ) {
868+ base := t .TempDir ()
869+ mysql , err := startDatabase (dc , base , mysqlImage , "mysql-parallel" )
870+ defer func () {
871+ // log the results before tearing down, if requested
872+ if err := logContainers (dc , mysql .id ); err != nil {
873+ log .Errorf ("failed to get logs from service containers: %v" , err )
874+ }
875+
876+ // tear everything down
877+ if err := teardown (dc , mysql .id ); err != nil {
878+ log .Errorf ("failed to teardown test: %v" , err )
879+ }
880+ }()
881+
882+ if err != nil {
883+ t .Fatalf ("failed to start large mysql database: %v" , err )
884+ }
885+ if err = dc .waitForDBConnectionAndGrantPrivileges (mysql .id , mysqlRootUser , mysqlRootPass ); err != nil {
886+ t .Fatalf ("failed to wait for DB connection: %v" , err )
887+ }
888+ dbconn := database.Connection {
889+ User : mysqlRootUser ,
890+ Pass : mysqlRootPass ,
891+ Host : "localhost" ,
892+ Port : mysql .port ,
893+ MultiStatements : true ,
894+ }
895+
896+ db , err := sql .Open ("mysql" , dbconn .MySQL ())
897+ if err != nil {
898+ t .Fatalf ("failed to open connection to database: %v" , err )
899+ }
900+
901+ var dbCount uint = 10
902+ var tableSize uint64 = 1
903+ t .Logf ("Setting up database server with %d databases, with one table of %d MB each" , dbCount , tableSize )
904+ if err := setupLargeDatabase (db , dbCount , tableSize * 1024 * 1024 ); err != nil {
905+ t .Fatalf ("failed to setup large database: %v" , err )
906+ }
907+ // now just run a backup to a temporary directory
908+ t .Logf ("Running backup for large database" )
909+ backupDir := t .TempDir ()
910+ executor := & core.Executor {}
911+ executor .SetLogger (log .New ())
912+
913+ store , err := storage .ParseURL (backupDir , credentials.Creds {})
914+ if err != nil {
915+ t .Fatalf ("invalid target url: %v" , err )
916+ }
917+
918+ dumpOptions := core.DumpOptions {
919+ Compressor : & compression.GzipCompressor {},
920+ DBConn : database.Connection {
921+ User : mysqlRootUser ,
922+ Pass : mysqlRootPass ,
923+ Host : "localhost" ,
924+ Port : mysql .port ,
925+ },
926+ Targets : []storage.Storage {store },
927+ PostDumpDelay : 5 * time .Second , // for testing only, make them delay 10 seconds
928+ }
929+ ctx := context .Background ()
930+ start := time .Now ()
931+ errChan := make (chan error , 1 )
932+ t .Logf ("Starting dump test for large database at %s" , start )
933+ go func () {
934+ _ , err := executor .Dump (ctx , dumpOptions )
935+ errChan <- err
936+ }()
937+ ticker := time .NewTicker (1 * time .Second )
938+ defer ticker .Stop ()
939+
940+ loop:
941+ for {
942+ select {
943+ case err := <- errChan :
944+ if err != nil {
945+ t .Fatalf ("failed to run dump test: %v" , err )
946+ }
947+ ticker .Stop ()
948+ break loop
949+ case <- ticker .C :
950+ // every interval, report how many connections are running to the database
951+ tr , err := getStatus (db , "Threads_running" )
952+ if err != nil {
953+ t .Fatalf ("failed to get Threads_running status: %v" , err )
954+ }
955+
956+ tc , err := getStatus (db , "Threads_connected" )
957+ if err != nil {
958+ t .Fatalf ("failed to get Threads_connected status: %v" , err )
959+ }
960+ uTotal , uActive , err := getProcesslistCounts (db )
961+ if err != nil {
962+ t .Fatalf ("failed to get processlist counts: %v" , err )
963+ }
964+
965+ t .Logf ("[%s]\t threads_running=%d\t threads_connected=%d\t open_user=%d\t active_user=%d\n " ,
966+ time .Now ().Format ("15:04:05" ),
967+ tr , tc , uTotal , uActive )
968+
969+ }
970+ }
971+ t .Logf ("Dump completed at %s in %s" , time .Now (), time .Since (start ))
972+ })
945973 t .Run ("dump" , func (t * testing.T ) {
946974 var (
947975 err error
0 commit comments