Skip to content

Commit 8474960

Browse files
committed
fix logger race
1 parent c9f41c0 commit 8474960

File tree

6 files changed

+90
-67
lines changed

6 files changed

+90
-67
lines changed

‎AUTHORS‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ Daniël van Eeden <git at myname.nl>
3535
Dave Protasowski <dprotaso at gmail.com>
3636
Dirkjan Bussink <d.bussink at gmail.com>
3737
DisposaBoy <disposaboy at dby.me>
38+
Dmitry Titov <[email protected]>
3839
Egor Smolyakov <egorsmkv at gmail.com>
3940
Erwan Martin <hello at erwan.io>
4041
Evan Elias <evan at skeema.net>

‎driver_test.go‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2141,7 +2141,7 @@ func TestInsertRetrieveEscapedData(t *testing.T){
21412141
funcTestUnixSocketAuthFail(t*testing.T){
21422142
runTests(t, dsn, func(dbt*DBTest){
21432143
// Save the current logger so we can restore it.
2144-
oldLogger:=defaultLogger
2144+
oldLogger:=getLogger()
21452145

21462146
// Set a new logger so we can capture its output.
21472147
buffer:=bytes.NewBuffer(make([]byte, 0, 64))

‎dsn.go‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@ func NewConfig() *Config{
8989
cfg:=&Config{
9090
Loc: time.UTC,
9191
MaxAllowedPacket: defaultMaxAllowedPacket,
92-
Logger: defaultLogger,
92+
Logger: getLogger(),
9393
AllowNativePasswords: true,
9494
CheckConnLiveness: true,
9595
}
@@ -203,7 +203,7 @@ func (cfg *Config) normalize() error{
203203
}
204204

205205
ifcfg.Logger==nil{
206-
cfg.Logger=defaultLogger
206+
cfg.Logger=getLogger()
207207
}
208208

209209
returnnil

‎dsn_test.go‎

Lines changed: 66 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -17,67 +17,74 @@ import (
1717
"time"
1818
)
1919

20-
vartestDSNs=[]struct{
20+
vartestDSNs []struct{
2121
instring
2222
out*Config
23-
}{{
24-
"username:password@protocol(address)/dbname?param=value",
25-
&Config{User: "username", Passwd: "password", Net: "protocol", Addr: "address", DBName: "dbname", Params: map[string]string{"param": "value"}, Loc: time.UTC, MaxAllowedPacket: defaultMaxAllowedPacket, Logger: defaultLogger, AllowNativePasswords: true, CheckConnLiveness: true},
26-
},{
27-
"username:password@protocol(address)/dbname?param=value&columnsWithAlias=true",
28-
&Config{User: "username", Passwd: "password", Net: "protocol", Addr: "address", DBName: "dbname", Params: map[string]string{"param": "value"}, Loc: time.UTC, MaxAllowedPacket: defaultMaxAllowedPacket, Logger: defaultLogger, AllowNativePasswords: true, CheckConnLiveness: true, ColumnsWithAlias: true},
29-
},{
30-
"username:password@protocol(address)/dbname?param=value&columnsWithAlias=true&multiStatements=true",
31-
&Config{User: "username", Passwd: "password", Net: "protocol", Addr: "address", DBName: "dbname", Params: map[string]string{"param": "value"}, Loc: time.UTC, MaxAllowedPacket: defaultMaxAllowedPacket, Logger: defaultLogger, AllowNativePasswords: true, CheckConnLiveness: true, ColumnsWithAlias: true, MultiStatements: true},
32-
},{
33-
"user@unix(/path/to/socket)/dbname?charset=utf8",
34-
&Config{User: "user", Net: "unix", Addr: "/path/to/socket", DBName: "dbname", charsets: []string{"utf8"}, Loc: time.UTC, MaxAllowedPacket: defaultMaxAllowedPacket, Logger: defaultLogger, AllowNativePasswords: true, CheckConnLiveness: true},
35-
},{
36-
"user:password@tcp(localhost:5555)/dbname?charset=utf8&tls=true",
37-
&Config{User: "user", Passwd: "password", Net: "tcp", Addr: "localhost:5555", DBName: "dbname", charsets: []string{"utf8"}, Loc: time.UTC, MaxAllowedPacket: defaultMaxAllowedPacket, Logger: defaultLogger, AllowNativePasswords: true, CheckConnLiveness: true, TLSConfig: "true"},
38-
},{
39-
"user:password@tcp(localhost:5555)/dbname?charset=utf8mb4,utf8&tls=skip-verify",
40-
&Config{User: "user", Passwd: "password", Net: "tcp", Addr: "localhost:5555", DBName: "dbname", charsets: []string{"utf8mb4", "utf8"}, Loc: time.UTC, MaxAllowedPacket: defaultMaxAllowedPacket, Logger: defaultLogger, AllowNativePasswords: true, CheckConnLiveness: true, TLSConfig: "skip-verify"},
41-
},{
42-
"user:password@/dbname?loc=UTC&timeout=30s&readTimeout=1s&writeTimeout=1s&allowAllFiles=1&clientFoundRows=true&allowOldPasswords=TRUE&collation=utf8mb4_unicode_ci&maxAllowedPacket=16777216&tls=false&allowCleartextPasswords=true&parseTime=true&rejectReadOnly=true",
43-
&Config{User: "user", Passwd: "password", Net: "tcp", Addr: "127.0.0.1:3306", DBName: "dbname", Collation: "utf8mb4_unicode_ci", Loc: time.UTC, TLSConfig: "false", AllowCleartextPasswords: true, AllowNativePasswords: true, Timeout: 30*time.Second, ReadTimeout: time.Second, WriteTimeout: time.Second, Logger: defaultLogger, AllowAllFiles: true, AllowOldPasswords: true, CheckConnLiveness: true, ClientFoundRows: true, MaxAllowedPacket: 16777216, ParseTime: true, RejectReadOnly: true},
44-
},{
45-
"user:password@/dbname?allowNativePasswords=false&checkConnLiveness=false&maxAllowedPacket=0&allowFallbackToPlaintext=true",
46-
&Config{User: "user", Passwd: "password", Net: "tcp", Addr: "127.0.0.1:3306", DBName: "dbname", Loc: time.UTC, MaxAllowedPacket: 0, Logger: defaultLogger, AllowFallbackToPlaintext: true, AllowNativePasswords: false, CheckConnLiveness: false},
47-
},{
48-
"user:p@ss(word)@tcp([de:ad:be:ef::ca:fe]:80)/dbname?loc=Local",
49-
&Config{User: "user", Passwd: "p@ss(word)", Net: "tcp", Addr: "[de:ad:be:ef::ca:fe]:80", DBName: "dbname", Loc: time.Local, MaxAllowedPacket: defaultMaxAllowedPacket, Logger: defaultLogger, AllowNativePasswords: true, CheckConnLiveness: true},
50-
},{
51-
"/dbname",
52-
&Config{Net: "tcp", Addr: "127.0.0.1:3306", DBName: "dbname", Loc: time.UTC, MaxAllowedPacket: defaultMaxAllowedPacket, Logger: defaultLogger, AllowNativePasswords: true, CheckConnLiveness: true},
53-
},{
54-
"/dbname%2Fwithslash",
55-
&Config{Net: "tcp", Addr: "127.0.0.1:3306", DBName: "dbname/withslash", Loc: time.UTC, MaxAllowedPacket: defaultMaxAllowedPacket, Logger: defaultLogger, AllowNativePasswords: true, CheckConnLiveness: true},
56-
},{
57-
"@/",
58-
&Config{Net: "tcp", Addr: "127.0.0.1:3306", Loc: time.UTC, MaxAllowedPacket: defaultMaxAllowedPacket, Logger: defaultLogger, AllowNativePasswords: true, CheckConnLiveness: true},
59-
},{
60-
"/",
61-
&Config{Net: "tcp", Addr: "127.0.0.1:3306", Loc: time.UTC, MaxAllowedPacket: defaultMaxAllowedPacket, Logger: defaultLogger, AllowNativePasswords: true, CheckConnLiveness: true},
62-
},{
63-
"",
64-
&Config{Net: "tcp", Addr: "127.0.0.1:3306", Loc: time.UTC, MaxAllowedPacket: defaultMaxAllowedPacket, Logger: defaultLogger, AllowNativePasswords: true, CheckConnLiveness: true},
65-
},{
66-
"user:p@/ssword@/",
67-
&Config{User: "user", Passwd: "p@/ssword", Net: "tcp", Addr: "127.0.0.1:3306", Loc: time.UTC, MaxAllowedPacket: defaultMaxAllowedPacket, Logger: defaultLogger, AllowNativePasswords: true, CheckConnLiveness: true},
68-
},{
69-
"unix/?arg=%2Fsome%2Fpath.ext",
70-
&Config{Net: "unix", Addr: "/tmp/mysql.sock", Params: map[string]string{"arg": "/some/path.ext"}, Loc: time.UTC, MaxAllowedPacket: defaultMaxAllowedPacket, Logger: defaultLogger, AllowNativePasswords: true, CheckConnLiveness: true},
71-
},{
72-
"tcp(127.0.0.1)/dbname",
73-
&Config{Net: "tcp", Addr: "127.0.0.1:3306", DBName: "dbname", Loc: time.UTC, MaxAllowedPacket: defaultMaxAllowedPacket, Logger: defaultLogger, AllowNativePasswords: true, CheckConnLiveness: true},
74-
},{
75-
"tcp(de:ad:be:ef::ca:fe)/dbname",
76-
&Config{Net: "tcp", Addr: "[de:ad:be:ef::ca:fe]:3306", DBName: "dbname", Loc: time.UTC, MaxAllowedPacket: defaultMaxAllowedPacket, Logger: defaultLogger, AllowNativePasswords: true, CheckConnLiveness: true},
77-
},{
78-
"user:password@/dbname?loc=UTC&timeout=30s&parseTime=true&timeTruncate=1h",
79-
&Config{User: "user", Passwd: "password", Net: "tcp", Addr: "127.0.0.1:3306", DBName: "dbname", Loc: time.UTC, Timeout: 30*time.Second, ParseTime: true, MaxAllowedPacket: defaultMaxAllowedPacket, Logger: defaultLogger, AllowNativePasswords: true, CheckConnLiveness: true, timeTruncate: time.Hour},
80-
},
23+
}
24+
25+
funcinit(){
26+
testDSNs= []struct{
27+
instring
28+
out*Config
29+
}{{
30+
"username:password@protocol(address)/dbname?param=value",
31+
&Config{User: "username", Passwd: "password", Net: "protocol", Addr: "address", DBName: "dbname", Params: map[string]string{"param": "value"}, Loc: time.UTC, MaxAllowedPacket: defaultMaxAllowedPacket, Logger: getLogger(), AllowNativePasswords: true, CheckConnLiveness: true},
32+
},{
33+
"username:password@protocol(address)/dbname?param=value&columnsWithAlias=true",
34+
&Config{User: "username", Passwd: "password", Net: "protocol", Addr: "address", DBName: "dbname", Params: map[string]string{"param": "value"}, Loc: time.UTC, MaxAllowedPacket: defaultMaxAllowedPacket, Logger: getLogger(), AllowNativePasswords: true, CheckConnLiveness: true, ColumnsWithAlias: true},
35+
},{
36+
"username:password@protocol(address)/dbname?param=value&columnsWithAlias=true&multiStatements=true",
37+
&Config{User: "username", Passwd: "password", Net: "protocol", Addr: "address", DBName: "dbname", Params: map[string]string{"param": "value"}, Loc: time.UTC, MaxAllowedPacket: defaultMaxAllowedPacket, Logger: getLogger(), AllowNativePasswords: true, CheckConnLiveness: true, ColumnsWithAlias: true, MultiStatements: true},
38+
},{
39+
"user@unix(/path/to/socket)/dbname?charset=utf8",
40+
&Config{User: "user", Net: "unix", Addr: "/path/to/socket", DBName: "dbname", charsets: []string{"utf8"}, Loc: time.UTC, MaxAllowedPacket: defaultMaxAllowedPacket, Logger: getLogger(), AllowNativePasswords: true, CheckConnLiveness: true},
41+
},{
42+
"user:password@tcp(localhost:5555)/dbname?charset=utf8&tls=true",
43+
&Config{User: "user", Passwd: "password", Net: "tcp", Addr: "localhost:5555", DBName: "dbname", charsets: []string{"utf8"}, Loc: time.UTC, MaxAllowedPacket: defaultMaxAllowedPacket, Logger: getLogger(), AllowNativePasswords: true, CheckConnLiveness: true, TLSConfig: "true"},
44+
},{
45+
"user:password@tcp(localhost:5555)/dbname?charset=utf8mb4,utf8&tls=skip-verify",
46+
&Config{User: "user", Passwd: "password", Net: "tcp", Addr: "localhost:5555", DBName: "dbname", charsets: []string{"utf8mb4", "utf8"}, Loc: time.UTC, MaxAllowedPacket: defaultMaxAllowedPacket, Logger: getLogger(), AllowNativePasswords: true, CheckConnLiveness: true, TLSConfig: "skip-verify"},
47+
},{
48+
"user:password@/dbname?loc=UTC&timeout=30s&readTimeout=1s&writeTimeout=1s&allowAllFiles=1&clientFoundRows=true&allowOldPasswords=TRUE&collation=utf8mb4_unicode_ci&maxAllowedPacket=16777216&tls=false&allowCleartextPasswords=true&parseTime=true&rejectReadOnly=true",
49+
&Config{User: "user", Passwd: "password", Net: "tcp", Addr: "127.0.0.1:3306", DBName: "dbname", Collation: "utf8mb4_unicode_ci", Loc: time.UTC, TLSConfig: "false", AllowCleartextPasswords: true, AllowNativePasswords: true, Timeout: 30*time.Second, ReadTimeout: time.Second, WriteTimeout: time.Second, Logger: getLogger(), AllowAllFiles: true, AllowOldPasswords: true, CheckConnLiveness: true, ClientFoundRows: true, MaxAllowedPacket: 16777216, ParseTime: true, RejectReadOnly: true},
50+
},{
51+
"user:password@/dbname?allowNativePasswords=false&checkConnLiveness=false&maxAllowedPacket=0&allowFallbackToPlaintext=true",
52+
&Config{User: "user", Passwd: "password", Net: "tcp", Addr: "127.0.0.1:3306", DBName: "dbname", Loc: time.UTC, MaxAllowedPacket: 0, Logger: getLogger(), AllowFallbackToPlaintext: true, AllowNativePasswords: false, CheckConnLiveness: false},
53+
},{
54+
"user:p@ss(word)@tcp([de:ad:be:ef::ca:fe]:80)/dbname?loc=Local",
55+
&Config{User: "user", Passwd: "p@ss(word)", Net: "tcp", Addr: "[de:ad:be:ef::ca:fe]:80", DBName: "dbname", Loc: time.Local, MaxAllowedPacket: defaultMaxAllowedPacket, Logger: getLogger(), AllowNativePasswords: true, CheckConnLiveness: true},
56+
},{
57+
"/dbname",
58+
&Config{Net: "tcp", Addr: "127.0.0.1:3306", DBName: "dbname", Loc: time.UTC, MaxAllowedPacket: defaultMaxAllowedPacket, Logger: getLogger(), AllowNativePasswords: true, CheckConnLiveness: true},
59+
},{
60+
"/dbname%2Fwithslash",
61+
&Config{Net: "tcp", Addr: "127.0.0.1:3306", DBName: "dbname/withslash", Loc: time.UTC, MaxAllowedPacket: defaultMaxAllowedPacket, Logger: getLogger(), AllowNativePasswords: true, CheckConnLiveness: true},
62+
},{
63+
"@/",
64+
&Config{Net: "tcp", Addr: "127.0.0.1:3306", Loc: time.UTC, MaxAllowedPacket: defaultMaxAllowedPacket, Logger: getLogger(), AllowNativePasswords: true, CheckConnLiveness: true},
65+
},{
66+
"/",
67+
&Config{Net: "tcp", Addr: "127.0.0.1:3306", Loc: time.UTC, MaxAllowedPacket: defaultMaxAllowedPacket, Logger: getLogger(), AllowNativePasswords: true, CheckConnLiveness: true},
68+
},{
69+
"",
70+
&Config{Net: "tcp", Addr: "127.0.0.1:3306", Loc: time.UTC, MaxAllowedPacket: defaultMaxAllowedPacket, Logger: getLogger(), AllowNativePasswords: true, CheckConnLiveness: true},
71+
},{
72+
"user:p@/ssword@/",
73+
&Config{User: "user", Passwd: "p@/ssword", Net: "tcp", Addr: "127.0.0.1:3306", Loc: time.UTC, MaxAllowedPacket: defaultMaxAllowedPacket, Logger: getLogger(), AllowNativePasswords: true, CheckConnLiveness: true},
74+
},{
75+
"unix/?arg=%2Fsome%2Fpath.ext",
76+
&Config{Net: "unix", Addr: "/tmp/mysql.sock", Params: map[string]string{"arg": "/some/path.ext"}, Loc: time.UTC, MaxAllowedPacket: defaultMaxAllowedPacket, Logger: getLogger(), AllowNativePasswords: true, CheckConnLiveness: true},
77+
},{
78+
"tcp(127.0.0.1)/dbname",
79+
&Config{Net: "tcp", Addr: "127.0.0.1:3306", DBName: "dbname", Loc: time.UTC, MaxAllowedPacket: defaultMaxAllowedPacket, Logger: getLogger(), AllowNativePasswords: true, CheckConnLiveness: true},
80+
},{
81+
"tcp(de:ad:be:ef::ca:fe)/dbname",
82+
&Config{Net: "tcp", Addr: "[de:ad:be:ef::ca:fe]:3306", DBName: "dbname", Loc: time.UTC, MaxAllowedPacket: defaultMaxAllowedPacket, Logger: getLogger(), AllowNativePasswords: true, CheckConnLiveness: true},
83+
},{
84+
"user:password@/dbname?loc=UTC&timeout=30s&parseTime=true&timeTruncate=1h",
85+
&Config{User: "user", Passwd: "password", Net: "tcp", Addr: "127.0.0.1:3306", DBName: "dbname", Loc: time.UTC, Timeout: 30*time.Second, ParseTime: true, MaxAllowedPacket: defaultMaxAllowedPacket, Logger: getLogger(), AllowNativePasswords: true, CheckConnLiveness: true, timeTruncate: time.Hour},
86+
},
87+
}
8188
}
8289

8390
funcTestDSNParser(t*testing.T){

‎errors.go‎

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import (
1313
"fmt"
1414
"log"
1515
"os"
16+
"sync/atomic"
1617
)
1718

1819
// Various errors the driver might return. Can change between driver versions.
@@ -37,7 +38,11 @@ var (
3738
errBadConnNoWrite=errors.New("bad connection")
3839
)
3940

40-
vardefaultLogger=Logger(log.New(os.Stderr, "[mysql] ", log.Ldate|log.Ltime))
41+
vardefaultLogger atomic.Pointer[Logger]
42+
43+
funcinit(){
44+
SetLogger(Logger(log.New(os.Stderr, "[mysql] ", log.Ldate|log.Ltime)))
45+
}
4146

4247
// Logger is used to log critical error messages.
4348
typeLoggerinterface{
@@ -56,10 +61,20 @@ func SetLogger(logger Logger) error{
5661
iflogger==nil{
5762
returnerrors.New("logger is nil")
5863
}
59-
defaultLogger=logger
64+
defaultLogger.Store(&logger)
6065
returnnil
6166
}
6267

68+
funcgetLogger() Logger{
69+
l:=defaultLogger.Load()
70+
ifl==nil{
71+
// Use thread-safe logger initialization
72+
return&NopLogger{}
73+
}
74+
75+
return*l
76+
}
77+
6378
// MySQLError is an error type which represents a single MySQL error
6479
typeMySQLErrorstruct{
6580
Numberuint16

‎errors_test.go‎

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,9 @@ import (
1616
)
1717

1818
funcTestErrorsSetLogger(t*testing.T){
19-
previous:=defaultLogger
19+
previous:=getLogger()
2020
deferfunc(){
21-
defaultLogger=previous
21+
SetLogger(previous)
2222
}()
2323

2424
// set up logger
@@ -28,7 +28,7 @@ func TestErrorsSetLogger(t *testing.T){
2828

2929
// print
3030
SetLogger(logger)
31-
defaultLogger.Print("test")
31+
getLogger().Print("test")
3232

3333
// check result
3434
ifactual:=buffer.String(); actual!=expected{

0 commit comments

Comments
(0)