Skip to content

Commit be9081e

Browse files
authored
Merge pull request #456 from databacker/parallel-database-test
add support for dumping large database test
2 parents 777a1b0 + 830eabf commit be9081e

File tree

16 files changed

+435
-138
lines changed

16 files changed

+435
-138
lines changed

cmd/dump_test.go

Lines changed: 18 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -30,35 +30,35 @@ func TestDumpCmd(t *testing.T) {
3030
}{
3131
// invalid ones
3232
{"missing server and target options", []string{""}, "", true, core.DumpOptions{}, core.TimerOptions{}, nil},
33-
{"invalid target URL", []string{"--server", "abc", "--target", "def"}, "", true, core.DumpOptions{DBConn: database.Connection{Host: "abc"}}, core.TimerOptions{}, nil},
33+
{"invalid target URL", []string{"--server", "abc", "--target", "def"}, "", true, core.DumpOptions{DBConn: &database.Connection{Host: "abc"}}, core.TimerOptions{}, nil},
3434

3535
// file URL
3636
{"file URL", []string{"--server", "abc", "--target", "file:///foo/bar"}, "", false, core.DumpOptions{
3737
Targets: []storage.Storage{file.New(*fileTargetURL)},
3838
MaxAllowedPacket: defaultMaxAllowedPacket,
3939
Compressor: &compression.GzipCompressor{},
40-
DBConn: database.Connection{Host: "abc", Port: defaultPort},
40+
DBConn: &database.Connection{Host: "abc", Port: defaultPort},
4141
FilenamePattern: "db_backup_{{ .now }}.{{ .compression }}",
4242
}, core.TimerOptions{Frequency: defaultFrequency, Begin: defaultBegin}, nil},
4343
{"file URL with pass-file", []string{"--server", "abc", "--target", "file:///foo/bar", "--pass-file", "testdata/password.txt"}, "", false, core.DumpOptions{
4444
Targets: []storage.Storage{file.New(*fileTargetURL)},
4545
MaxAllowedPacket: defaultMaxAllowedPacket,
4646
Compressor: &compression.GzipCompressor{},
47-
DBConn: database.Connection{Host: "abc", Port: defaultPort, Pass: "testpassword"},
47+
DBConn: &database.Connection{Host: "abc", Port: defaultPort, Pass: "testpassword"},
4848
FilenamePattern: "db_backup_{{ .now }}.{{ .compression }}",
4949
}, core.TimerOptions{Frequency: defaultFrequency, Begin: defaultBegin}, nil},
5050
{"file URL with pass and pass-file (pass takes precedence)", []string{"--server", "abc", "--target", "file:///foo/bar", "--pass", "explicitpass", "--pass-file", "testdata/password.txt"}, "", false, core.DumpOptions{
5151
Targets: []storage.Storage{file.New(*fileTargetURL)},
5252
MaxAllowedPacket: defaultMaxAllowedPacket,
5353
Compressor: &compression.GzipCompressor{},
54-
DBConn: database.Connection{Host: "abc", Port: defaultPort, Pass: "explicitpass"},
54+
DBConn: &database.Connection{Host: "abc", Port: defaultPort, Pass: "explicitpass"},
5555
FilenamePattern: "db_backup_{{ .now }}.{{ .compression }}",
5656
}, core.TimerOptions{Frequency: defaultFrequency, Begin: defaultBegin}, nil},
5757
{"file URL with prune", []string{"--server", "abc", "--target", "file:///foo/bar", "--retention", "1h"}, "", false, core.DumpOptions{
5858
Targets: []storage.Storage{file.New(*fileTargetURL)},
5959
MaxAllowedPacket: defaultMaxAllowedPacket,
6060
Compressor: &compression.GzipCompressor{},
61-
DBConn: database.Connection{Host: "abc", Port: defaultPort},
61+
DBConn: &database.Connection{Host: "abc", Port: defaultPort},
6262
FilenamePattern: "db_backup_{{ .now }}.{{ .compression }}",
6363
}, core.TimerOptions{Frequency: defaultFrequency, Begin: defaultBegin}, &core.PruneOptions{Targets: []storage.Storage{file.New(*fileTargetURL)}, Retention: "1h"}},
6464

@@ -67,14 +67,14 @@ func TestDumpCmd(t *testing.T) {
6767
Targets: []storage.Storage{file.New(*fileTargetURL)},
6868
MaxAllowedPacket: defaultMaxAllowedPacket,
6969
Compressor: &compression.GzipCompressor{},
70-
DBConn: database.Connection{Host: "abc", Port: defaultPort},
70+
DBConn: &database.Connection{Host: "abc", Port: defaultPort},
7171
FilenamePattern: "db_backup_{{ .now }}.{{ .compression }}",
7272
}, core.TimerOptions{Frequency: defaultFrequency, Begin: defaultBegin}, nil},
7373
{"database explicit name with explicit port", []string{"--server", "abc", "--port", "3307", "--target", "file:///foo/bar"}, "", false, core.DumpOptions{
7474
Targets: []storage.Storage{file.New(*fileTargetURL)},
7575
MaxAllowedPacket: defaultMaxAllowedPacket,
7676
Compressor: &compression.GzipCompressor{},
77-
DBConn: database.Connection{Host: "abc", Port: 3307},
77+
DBConn: &database.Connection{Host: "abc", Port: 3307},
7878
FilenamePattern: "db_backup_{{ .now }}.{{ .compression }}",
7979
}, core.TimerOptions{Frequency: defaultFrequency, Begin: defaultBegin}, nil},
8080

@@ -83,21 +83,21 @@ func TestDumpCmd(t *testing.T) {
8383
Targets: []storage.Storage{file.New(*fileTargetURL)},
8484
MaxAllowedPacket: defaultMaxAllowedPacket,
8585
Compressor: &compression.GzipCompressor{},
86-
DBConn: database.Connection{Host: "abcd", Port: 3306, User: "user2", Pass: "xxxx2"},
86+
DBConn: &database.Connection{Host: "abcd", Port: 3306, User: "user2", Pass: "xxxx2"},
8787
FilenamePattern: "db_backup_{{ .now }}.{{ .compression }}",
8888
}, core.TimerOptions{Frequency: defaultFrequency, Begin: defaultBegin}, &core.PruneOptions{Targets: []storage.Storage{file.New(*fileTargetURL)}, Retention: "1h"}},
8989
{"config file with port override", []string{"--config-file", "testdata/config.yml", "--port", "3307"}, "", false, core.DumpOptions{
9090
Targets: []storage.Storage{file.New(*fileTargetURL)},
9191
MaxAllowedPacket: defaultMaxAllowedPacket,
9292
Compressor: &compression.GzipCompressor{},
93-
DBConn: database.Connection{Host: "abcd", Port: 3307, User: "user2", Pass: "xxxx2"},
93+
DBConn: &database.Connection{Host: "abcd", Port: 3307, User: "user2", Pass: "xxxx2"},
9494
FilenamePattern: "db_backup_{{ .now }}.{{ .compression }}",
9595
}, core.TimerOptions{Frequency: defaultFrequency, Begin: defaultBegin}, &core.PruneOptions{Targets: []storage.Storage{file.New(*fileTargetURL)}, Retention: "1h"}},
9696
{"config file with filename pattern override", []string{"--config-file", "testdata/pattern.yml", "--port", "3307"}, "", false, core.DumpOptions{
9797
Targets: []storage.Storage{file.New(*fileTargetURL)},
9898
MaxAllowedPacket: defaultMaxAllowedPacket,
9999
Compressor: &compression.GzipCompressor{},
100-
DBConn: database.Connection{Host: "abcd", Port: 3307, User: "user2", Pass: "xxxx2"},
100+
DBConn: &database.Connection{Host: "abcd", Port: 3307, User: "user2", Pass: "xxxx2"},
101101
FilenamePattern: "foo_{{ .now }}.{{ .compression }}",
102102
}, core.TimerOptions{Frequency: defaultFrequency, Begin: defaultBegin}, &core.PruneOptions{Targets: []storage.Storage{file.New(*fileTargetURL)}, Retention: "1h"}},
103103

@@ -106,60 +106,60 @@ func TestDumpCmd(t *testing.T) {
106106
Targets: []storage.Storage{file.New(*fileTargetURL)},
107107
MaxAllowedPacket: defaultMaxAllowedPacket,
108108
Compressor: &compression.GzipCompressor{},
109-
DBConn: database.Connection{Host: "abc", Port: defaultPort},
109+
DBConn: &database.Connection{Host: "abc", Port: defaultPort},
110110
FilenamePattern: "db_backup_{{ .now }}.{{ .compression }}",
111111
}, core.TimerOptions{Once: true, Frequency: defaultFrequency, Begin: defaultBegin}, nil},
112112
{"cron flag", []string{"--server", "abc", "--target", "file:///foo/bar", "--cron", "0 0 * * *"}, "", false, core.DumpOptions{
113113
Targets: []storage.Storage{file.New(*fileTargetURL)},
114114
MaxAllowedPacket: defaultMaxAllowedPacket,
115115
Compressor: &compression.GzipCompressor{},
116-
DBConn: database.Connection{Host: "abc", Port: defaultPort},
116+
DBConn: &database.Connection{Host: "abc", Port: defaultPort},
117117
FilenamePattern: "db_backup_{{ .now }}.{{ .compression }}",
118118
}, core.TimerOptions{Frequency: defaultFrequency, Begin: defaultBegin, Cron: "0 0 * * *"}, nil},
119119
{"begin flag", []string{"--server", "abc", "--target", "file:///foo/bar", "--begin", "1234"}, "", false, core.DumpOptions{
120120
Targets: []storage.Storage{file.New(*fileTargetURL)},
121121
MaxAllowedPacket: defaultMaxAllowedPacket,
122122
Compressor: &compression.GzipCompressor{},
123-
DBConn: database.Connection{Host: "abc", Port: defaultPort},
123+
DBConn: &database.Connection{Host: "abc", Port: defaultPort},
124124
FilenamePattern: "db_backup_{{ .now }}.{{ .compression }}",
125125
}, core.TimerOptions{Frequency: defaultFrequency, Begin: "1234"}, nil},
126126
{"frequency flag", []string{"--server", "abc", "--target", "file:///foo/bar", "--frequency", "10"}, "", false, core.DumpOptions{
127127
Targets: []storage.Storage{file.New(*fileTargetURL)},
128128
MaxAllowedPacket: defaultMaxAllowedPacket,
129129
Compressor: &compression.GzipCompressor{},
130-
DBConn: database.Connection{Host: "abc", Port: defaultPort},
130+
DBConn: &database.Connection{Host: "abc", Port: defaultPort},
131131
FilenamePattern: "db_backup_{{ .now }}.{{ .compression }}",
132132
}, core.TimerOptions{Frequency: 10, Begin: defaultBegin}, nil},
133133
{"incompatible flags: once/cron", []string{"--server", "abc", "--target", "file:///foo/bar", "--once", "--cron", "0 0 * * *"}, "", true, core.DumpOptions{}, core.TimerOptions{}, nil},
134134
{"incompatible flags: once/begin", []string{"--server", "abc", "--target", "file:///foo/bar", "--once", "--begin", "1234"}, "", true, core.DumpOptions{}, core.TimerOptions{}, nil},
135135
{"incompatible flags: once/frequency", []string{"--server", "abc", "--target", "file:///foo/bar", "--once", "--frequency", "10"}, "", true, core.DumpOptions{}, core.TimerOptions{}, nil},
136136
{"incompatible flags: cron/begin", []string{"--server", "abc", "--target", "file:///foo/bar", "--cron", "0 0 * * *", "--begin", "1234"}, "", true, core.DumpOptions{}, core.TimerOptions{}, nil},
137137
{"incompatible flags: cron/frequency", []string{"--server", "abc", "--target", "file:///foo/bar", "--cron", "0 0 * * *", "--frequency", "10"}, "", true, core.DumpOptions{
138-
DBConn: database.Connection{Host: "abcd", Port: 3306, User: "user2", Pass: "xxxx2"},
138+
DBConn: &database.Connection{Host: "abcd", Port: 3306, User: "user2", Pass: "xxxx2"},
139139
}, core.TimerOptions{Frequency: defaultFrequency, Begin: defaultBegin}, &core.PruneOptions{Targets: []storage.Storage{file.New(*fileTargetURL)}, Retention: "1h"}},
140140

141141
// pre- and post-backup scripts
142142
{"prebackup scripts", []string{"--server", "abc", "--target", "file:///foo/bar", "--pre-backup-scripts", "/prebackup"}, "", false, core.DumpOptions{
143143
Targets: []storage.Storage{file.New(*fileTargetURL)},
144144
MaxAllowedPacket: defaultMaxAllowedPacket,
145145
Compressor: &compression.GzipCompressor{},
146-
DBConn: database.Connection{Host: "abc", Port: defaultPort},
146+
DBConn: &database.Connection{Host: "abc", Port: defaultPort},
147147
PreBackupScripts: "/prebackup",
148148
FilenamePattern: "db_backup_{{ .now }}.{{ .compression }}",
149149
}, core.TimerOptions{Frequency: defaultFrequency, Begin: defaultBegin}, nil},
150150
{"postbackup scripts", []string{"--server", "abc", "--target", "file:///foo/bar", "--post-backup-scripts", "/postbackup"}, "", false, core.DumpOptions{
151151
Targets: []storage.Storage{file.New(*fileTargetURL)},
152152
MaxAllowedPacket: defaultMaxAllowedPacket,
153153
Compressor: &compression.GzipCompressor{},
154-
DBConn: database.Connection{Host: "abc", Port: defaultPort},
154+
DBConn: &database.Connection{Host: "abc", Port: defaultPort},
155155
PostBackupScripts: "/postbackup",
156156
FilenamePattern: "db_backup_{{ .now }}.{{ .compression }}",
157157
}, core.TimerOptions{Frequency: defaultFrequency, Begin: defaultBegin}, nil},
158158
{"prebackup and postbackup scripts", []string{"--server", "abc", "--target", "file:///foo/bar", "--post-backup-scripts", "/postbackup", "--pre-backup-scripts", "/prebackup"}, "", false, core.DumpOptions{
159159
Targets: []storage.Storage{file.New(*fileTargetURL)},
160160
MaxAllowedPacket: defaultMaxAllowedPacket,
161161
Compressor: &compression.GzipCompressor{},
162-
DBConn: database.Connection{Host: "abc", Port: defaultPort},
162+
DBConn: &database.Connection{Host: "abc", Port: defaultPort},
163163
PreBackupScripts: "/prebackup",
164164
PostBackupScripts: "/postbackup",
165165
FilenamePattern: "db_backup_{{ .now }}.{{ .compression }}",

cmd/restore_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ func TestRestoreCmd(t *testing.T) {
3333
{"missing server and target options", []string{""}, "", true, core.RestoreOptions{}},
3434
{"invalid target URL", []string{"--server", "abc", "--target", "def"}, "", true, core.RestoreOptions{}},
3535
{"valid URL missing dump filename", []string{"--server", "abc", "--target", "file:///foo/bar"}, "", true, core.RestoreOptions{}},
36-
{"valid file URL", []string{"--server", "abc", "--target", fileTarget, "filename.tgz", "--verbose", "2"}, "", false, core.RestoreOptions{Target: file.New(*fileTargetURL), TargetFile: "filename.tgz", DBConn: database.Connection{Host: "abc", Port: defaultPort}, DatabasesMap: map[string]string{}, Compressor: &compression.GzipCompressor{}}},
36+
{"valid file URL", []string{"--server", "abc", "--target", fileTarget, "filename.tgz", "--verbose", "2"}, "", false, core.RestoreOptions{Target: file.New(*fileTargetURL), TargetFile: "filename.tgz", DBConn: &database.Connection{Host: "abc", Port: defaultPort}, DatabasesMap: map[string]string{}, Compressor: &compression.GzipCompressor{}}},
3737
}
3838

3939
for _, tt := range tests {

cmd/root.go

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ type subCommand func(execs, *cmdConfiguration) (*cobra.Command, error)
3838
var subCommands = []subCommand{dumpCmd, restoreCmd, pruneCmd}
3939

4040
type cmdConfiguration struct {
41-
dbconn database.Connection
41+
dbconn *database.Connection
4242
creds credentials.Creds
4343
configuration *api.ConfigSpec
4444
logger *log.Logger
@@ -111,6 +111,11 @@ func rootCmd(execs execs) (*cobra.Command, error) {
111111
// the structure of our config file is more complex and with relationships than our config/env var
112112
// so we cannot use a single viper structure, as described above.
113113

114+
// avoid nil pointer dereference
115+
if cmdConfig.dbconn == nil {
116+
cmdConfig.dbconn = &database.Connection{}
117+
}
118+
114119
// set up database connection
115120
if actualConfig != nil {
116121
if actualConfig.Database != nil {

pkg/core/dump.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,7 @@ func (e *Executor) Dump(ctx context.Context, opts DumpOptions) (DumpResults, err
111111
Routines: routines,
112112
SuppressUseDatabase: suppressUseDatabase,
113113
MaxAllowedPacket: maxAllowedPacket,
114+
PostDumpDelay: opts.PostDumpDelay,
114115
}, dw); err != nil {
115116
dbDumpSpan.SetStatus(codes.Error, err.Error())
116117
dbDumpSpan.End()

pkg/core/dumpoptions.go

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
package core
22

33
import (
4+
"time"
5+
46
"github.com/databacker/mysql-backup/pkg/compression"
57
"github.com/databacker/mysql-backup/pkg/database"
68
"github.com/databacker/mysql-backup/pkg/encrypt"
@@ -12,7 +14,7 @@ type DumpOptions struct {
1214
Targets []storage.Storage
1315
Safechars bool
1416
DBNames []string
15-
DBConn database.Connection
17+
DBConn *database.Connection
1618
Compressor compression.Compressor
1719
Encryptor encrypt.Encryptor
1820
Exclude []string
@@ -25,4 +27,6 @@ type DumpOptions struct {
2527
MaxAllowedPacket int
2628
Run uuid.UUID
2729
FilenamePattern string
30+
// PostDumpDelay inafter each dump is complete, while holding connection open. Do not use outside of tests.
31+
PostDumpDelay time.Duration
2832
}

pkg/core/restoreoptions.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import (
1010
type RestoreOptions struct {
1111
Target storage.Storage
1212
TargetFile string
13-
DBConn database.Connection
13+
DBConn *database.Connection
1414
DatabasesMap map[string]string
1515
Compressor compression.Compressor
1616
Run uuid.UUID

pkg/database/connection.go

Lines changed: 34 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,48 @@
11
package database
22

33
import (
4+
"database/sql"
45
"fmt"
56
"strings"
67

78
mysql "github.com/go-sql-driver/mysql"
89
)
910

1011
type Connection struct {
11-
User string
12-
Pass string
13-
Host string
14-
Port int
12+
User string
13+
Pass string
14+
Host string
15+
Port int
16+
MultiStatements bool
17+
18+
// holds a connection to the database
19+
sql *sql.DB
1520
}
1621

17-
func (c Connection) MySQL() string {
18-
config := mysql.NewConfig()
19-
config.User = c.User
20-
config.Passwd = c.Pass
21-
if strings.HasPrefix(c.Host, "/") {
22-
config.Net = "unix"
23-
config.Addr = c.Host
24-
} else {
25-
config.Net = "tcp"
26-
config.Addr = fmt.Sprintf("%s:%d", c.Host, c.Port)
22+
// MySQL returns a MySQL connection for the Connection.
23+
func (c *Connection) MySQL() (*sql.DB, error) {
24+
if c.sql == nil {
25+
26+
config := mysql.NewConfig()
27+
config.User = c.User
28+
config.Passwd = c.Pass
29+
if strings.HasPrefix(c.Host, "/") {
30+
config.Net = "unix"
31+
config.Addr = c.Host
32+
} else {
33+
config.Net = "tcp"
34+
config.Addr = fmt.Sprintf("%s:%d", c.Host, c.Port)
35+
}
36+
config.ParseTime = true
37+
config.TLSConfig = "preferred"
38+
config.MultiStatements = c.MultiStatements
39+
dsn := config.FormatDSN()
40+
handle, err := sql.Open("mysql", dsn)
41+
if err != nil {
42+
return nil, fmt.Errorf("failed to open connection to database: %v", err)
43+
}
44+
c.sql = handle
2745
}
28-
config.ParseTime = true
29-
config.TLSConfig = "preferred"
30-
return config.FormatDSN()
46+
return c.sql, nil
47+
3148
}

pkg/database/dump.go

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@ package database
22

33
import (
44
"context"
5-
"database/sql"
65
"fmt"
6+
"time"
77

88
"github.com/databacker/mysql-backup/pkg/database/mysql"
99
)
@@ -14,9 +14,11 @@ type DumpOpts struct {
1414
Routines bool
1515
SuppressUseDatabase bool
1616
MaxAllowedPacket int
17+
// PostDumpDelay after each dump is complete, while holding connection open. Do not use outside of tests.
18+
PostDumpDelay time.Duration
1719
}
1820

19-
func Dump(ctx context.Context, dbconn Connection, opts DumpOpts, writers []DumpWriter) error {
21+
func Dump(ctx context.Context, dbconn *Connection, opts DumpOpts, writers []DumpWriter) error {
2022

2123
// TODO: dump data for each writer:
2224
// per schema
@@ -25,12 +27,11 @@ func Dump(ctx context.Context, dbconn Connection, opts DumpOpts, writers []DumpW
2527
// mysqldump -A $MYSQLDUMP_OPTS
2628
// all at once limited to some databases
2729
// mysqldump --databases $DB_DUMP_INCLUDE $MYSQLDUMP_OPTS
30+
db, err := dbconn.MySQL()
31+
if err != nil {
32+
return fmt.Errorf("failed to open connection to database: %v", err)
33+
}
2834
for _, writer := range writers {
29-
db, err := sql.Open("mysql", dbconn.MySQL())
30-
if err != nil {
31-
return fmt.Errorf("failed to open connection to database: %v", err)
32-
}
33-
defer func() { _ = db.Close() }()
3435
for _, schema := range writer.Schemas {
3536
dumper := &mysql.Data{
3637
Out: writer.Writer,
@@ -42,6 +43,7 @@ func Dump(ctx context.Context, dbconn Connection, opts DumpOpts, writers []DumpW
4243
Routines: opts.Routines,
4344
SuppressUseDatabase: opts.SuppressUseDatabase,
4445
MaxAllowedPacket: opts.MaxAllowedPacket,
46+
PostDumpDelay: opts.PostDumpDelay,
4547
}
4648
if err := dumper.Dump(); err != nil {
4749
return fmt.Errorf("failed to dump database %s: %v", schema, err)

pkg/database/mysql/dump.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ type Data struct {
5252
SuppressUseDatabase bool
5353
Charset string
5454
Collation string
55+
PostDumpDelay time.Duration
5556

5657
tx *sql.Tx
5758
headerTmpl *template.Template
@@ -254,6 +255,9 @@ func (data *Data) Dump() error {
254255
return data.err
255256
}
256257

258+
if data.PostDumpDelay > 0 {
259+
time.Sleep(data.PostDumpDelay)
260+
}
257261
meta.CompleteTime = time.Now().UTC().Format("2006-01-02 15:04:05")
258262
return data.footerTmpl.Execute(data.Out, meta)
259263
}

pkg/database/restore.go

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,12 +19,11 @@ var (
1919
createRegex = regexp.MustCompile(`(?i)^(CREATE\s+DATABASE\s*(\/\*.*\*\/\s*)?` + "`" + `)([^\s]+)(` + "`" + `\s*(\s*\/\*.*\*\/\s*)?\s*;$)`)
2020
)
2121

22-
func Restore(ctx context.Context, dbconn Connection, databasesMap map[string]string, readers []io.ReadSeeker) error {
23-
db, err := sql.Open("mysql", dbconn.MySQL())
22+
func Restore(ctx context.Context, dbconn *Connection, databasesMap map[string]string, readers []io.ReadSeeker) error {
23+
db, err := dbconn.MySQL()
2424
if err != nil {
2525
return fmt.Errorf("failed to open connection to database: %v", err)
2626
}
27-
defer func() { _ = db.Close() }()
2827

2928
// load data into database by reading from each reader
3029
for _, r := range readers {

0 commit comments

Comments
 (0)