Skip to content

Commit 23c0b6c

Browse files
committed
Fix Data Race with TraceLog
- a `DATA RACE` was introduced with pgx 5.7.0 when using a `pgxpool.Pool` when `minConns > 0`. Changed from using lazy initialization to return the `defaultTimeKey` when the `tl.Config` is `nil`. Below are the stack traces emitted by the race detector: ``` ================== WARNING: DATA RACE Read at 0x00c00094ca58 by goroutine 4079: github.com/jackc/pgx/v5/tracelog.(*TraceLog).ensureConfig() /home/dave/go/pkg/mod/github.com/jackc/pgx/[email protected]/tracelog/tracelog.go:142 +0x87 github.com/jackc/pgx/v5/tracelog.(*TraceLog).TraceConnectEnd() /home/dave/go/pkg/mod/github.com/jackc/pgx/[email protected]/tracelog/tracelog.go:279 +0x7f github.com/xxxx//internal/pkg/pg.compositeTracer.TraceConnectEnd() /home/dave/go/pkg/mod/github.com/xxxx/@v1.80.1-0.20240906184531-58ce8cae9811/internal/pkg/pg/composite.go:66 +0xdd github.com/xxxx//internal/pkg/pg.(*compositeTracer).TraceConnectEnd() <autogenerated>:1 +0x37 github.com/jackc/pgx/v5.connect.func1() /home/dave/go/pkg/mod/github.com/jackc/pgx/[email protected]/conn.go:244 +0xa1 runtime.deferreturn() /home/dave/go/go1.22.2/src/runtime/panic.go:602 +0x5d github.com/jackc/pgx/v5.ConnectConfig() /home/dave/go/pkg/mod/github.com/jackc/pgx/[email protected]/conn.go:159 +0x144 github.com/jackc/pgx/v5/pgxpool.NewWithConfig.func1() /home/dave/go/pkg/mod/github.com/jackc/pgx/[email protected]/pgxpool/pool.go:227 +0x266 github.com/jackc/puddle/v2.(*Pool[go.shape.*uint8]).CreateResource() /home/dave/go/pkg/mod/github.com/jackc/puddle/[email protected]/pool.go:614 +0x1a2 github.com/jackc/pgx/v5/pgxpool.(*Pool).createIdleResources.func1() /home/dave/go/pkg/mod/github.com/jackc/pgx/[email protected]/pgxpool/pool.go:490 +0x76 Previous write at 0x00c00094ca58 by goroutine 4080: github.com/jackc/pgx/v5/tracelog.(*TraceLog).ensureConfig() /home/dave/go/pkg/mod/github.com/jackc/pgx/[email protected]/tracelog/tracelog.go:143 +0xe5 github.com/jackc/pgx/v5/tracelog.(*TraceLog).TraceConnectEnd() /home/dave/go/pkg/mod/github.com/jackc/pgx/[email protected]/tracelog/tracelog.go:279 +0x7f github.com/xxxx//internal/pkg/pg.compositeTracer.TraceConnectEnd() /home/dave/go/pkg/mod/github.com/xxxx/@v1.80.1-0.20240906184531-58ce8cae9811/internal/pkg/pg/composite.go:66 +0xdd github.com/xxxx//internal/pkg/pg.(*compositeTracer).TraceConnectEnd() <autogenerated>:1 +0x37 github.com/jackc/pgx/v5.connect.func1() /home/dave/go/pkg/mod/github.com/jackc/pgx/[email protected]/conn.go:244 +0xa1 runtime.deferreturn() /home/dave/go/go1.22.2/src/runtime/panic.go:602 +0x5d github.com/jackc/pgx/v5.ConnectConfig() /home/dave/go/pkg/mod/github.com/jackc/pgx/[email protected]/conn.go:159 +0x144 github.com/jackc/pgx/v5/pgxpool.NewWithConfig.func1() /home/dave/go/pkg/mod/github.com/jackc/pgx/[email protected]/pgxpool/pool.go:227 +0x266 github.com/jackc/puddle/v2.(*Pool[go.shape.*uint8]).CreateResource() /home/dave/go/pkg/mod/github.com/jackc/puddle/[email protected]/pool.go:614 +0x1a2 github.com/jackc/pgx/v5/pgxpool.(*Pool).createIdleResources.func1() /home/dave/go/pkg/mod/github.com/jackc/pgx/[email protected]/pgxpool/pool.go:490 +0x76 Goroutine 4079 (running) created at: github.com/jackc/pgx/v5/pgxpool.(*Pool).createIdleResources() /home/dave/go/pkg/mod/github.com/jackc/pgx/[email protected]/pgxpool/pool.go:489 +0xf6 github.com/jackc/pgx/v5/pgxpool.NewWithConfig.func3() /home/dave/go/pkg/mod/github.com/jackc/pgx/[email protected]/pgxpool/pool.go:274 +0x5d Goroutine 4080 (finished) created at: github.com/jackc/pgx/v5/pgxpool.(*Pool).createIdleResources() /home/dave/go/pkg/mod/github.com/jackc/pgx/[email protected]/pgxpool/pool.go:489 +0xf6 github.com/jackc/pgx/v5/pgxpool.NewWithConfig.func3() /home/dave/go/pkg/mod/github.com/jackc/pgx/[email protected]/pgxpool/pool.go:274 +0x5d ================== ================== WARNING: DATA RACE Read at 0x00c001433700 by goroutine 4079: github.com/jackc/pgx/v5/tracelog.(*TraceLog).TraceConnectEnd() /home/dave/go/pkg/mod/github.com/jackc/pgx/[email protected]/tracelog/tracelog.go:304 +0x472 github.com/xxxx//internal/pkg/pg.compositeTracer.TraceConnectEnd() /home/dave/go/pkg/mod/github.com/xxxx/@v1.80.1-0.20240906184531-58ce8cae9811/internal/pkg/pg/composite.go:66 +0xdd github.com/xxxx//internal/pkg/pg.(*compositeTracer).TraceConnectEnd() <autogenerated>:1 +0x37 github.com/jackc/pgx/v5.connect.func1() /home/dave/go/pkg/mod/github.com/jackc/pgx/[email protected]/conn.go:244 +0xa1 runtime.deferreturn() /home/dave/go/go1.22.2/src/runtime/panic.go:602 +0x5d github.com/jackc/pgx/v5.ConnectConfig() /home/dave/go/pkg/mod/github.com/jackc/pgx/[email protected]/conn.go:159 +0x144 github.com/jackc/pgx/v5/pgxpool.NewWithConfig.func1() /home/dave/go/pkg/mod/github.com/jackc/pgx/[email protected]/pgxpool/pool.go:227 +0x266 github.com/jackc/puddle/v2.(*Pool[go.shape.*uint8]).CreateResource() /home/dave/go/pkg/mod/github.com/jackc/puddle/[email protected]/pool.go:614 +0x1a2 github.com/jackc/pgx/v5/pgxpool.(*Pool).createIdleResources.func1() /home/dave/go/pkg/mod/github.com/jackc/pgx/[email protected]/pgxpool/pool.go:490 +0x76 Previous write at 0x00c001433700 by goroutine 4080: github.com/jackc/pgx/v5/tracelog.DefaultTraceLogConfig() /home/dave/go/pkg/mod/github.com/jackc/pgx/[email protected]/tracelog/tracelog.go:128 +0xb0 github.com/jackc/pgx/v5/tracelog.(*TraceLog).ensureConfig() /home/dave/go/pkg/mod/github.com/jackc/pgx/[email protected]/tracelog/tracelog.go:143 +0xdc github.com/jackc/pgx/v5/tracelog.(*TraceLog).TraceConnectEnd() /home/dave/go/pkg/mod/github.com/jackc/pgx/[email protected]/tracelog/tracelog.go:279 +0x7f github.com/xxxx//internal/pkg/pg.compositeTracer.TraceConnectEnd() /home/dave/go/pkg/mod/github.com/xxxx/@v1.80.1-0.20240906184531-58ce8cae9811/internal/pkg/pg/composite.go:66 +0xdd github.com/xxxx//internal/pkg/pg.(*compositeTracer).TraceConnectEnd() <autogenerated>:1 +0x37 github.com/jackc/pgx/v5.connect.func1() /home/dave/go/pkg/mod/github.com/jackc/pgx/[email protected]/conn.go:244 +0xa1 runtime.deferreturn() /home/dave/go/go1.22.2/src/runtime/panic.go:602 +0x5d github.com/jackc/pgx/v5.ConnectConfig() /home/dave/go/pkg/mod/github.com/jackc/pgx/[email protected]/conn.go:159 +0x144 github.com/jackc/pgx/v5/pgxpool.NewWithConfig.func1() /home/dave/go/pkg/mod/github.com/jackc/pgx/[email protected]/pgxpool/pool.go:227 +0x266 github.com/jackc/puddle/v2.(*Pool[go.shape.*uint8]).CreateResource() /home/dave/go/pkg/mod/github.com/jackc/puddle/[email protected]/pool.go:614 +0x1a2 github.com/jackc/pgx/v5/pgxpool.(*Pool).createIdleResources.func1() /home/dave/go/pkg/mod/github.com/jackc/pgx/[email protected]/pgxpool/pool.go:490 +0x76 Goroutine 4079 (running) created at: github.com/jackc/pgx/v5/pgxpool.(*Pool).createIdleResources() /home/dave/go/pkg/mod/github.com/jackc/pgx/[email protected]/pgxpool/pool.go:489 +0xf6 github.com/jackc/pgx/v5/pgxpool.NewWithConfig.func3() /home/dave/go/pkg/mod/github.com/jackc/pgx/[email protected]/pgxpool/pool.go:274 +0x5d Goroutine 4080 (finished) created at: github.com/jackc/pgx/v5/pgxpool.(*Pool).createIdleResources() /home/dave/go/pkg/mod/github.com/jackc/pgx/[email protected]/pgxpool/pool.go:489 +0xf6 github.com/jackc/pgx/v5/pgxpool.NewWithConfig.func3() /home/dave/go/pkg/mod/github.com/jackc/pgx/[email protected]/pgxpool/pool.go:274 +0x5d
1 parent d1205a6 commit 23c0b6c

File tree

1 file changed

+29
-26
lines changed

1 file changed

+29
-26
lines changed

tracelog/tracelog.go

+29-26
Original file line numberDiff line numberDiff line change
@@ -122,10 +122,13 @@ type TraceLogConfig struct {
122122
TimeKey string
123123
}
124124

125+
// defaultTimeKey is the name used in log statements for the timing, i.e. end time - start time, of an operation.
126+
const defaultTimeKey = "time"
127+
125128
// DefaultTraceLogConfig returns the default configuration for TraceLog
126129
func DefaultTraceLogConfig() *TraceLogConfig {
127130
return &TraceLogConfig{
128-
TimeKey: "time",
131+
TimeKey: defaultTimeKey,
129132
}
130133
}
131134

@@ -137,11 +140,11 @@ type TraceLog struct {
137140
Config *TraceLogConfig
138141
}
139142

140-
// ensureConfig initializes the Config field with default values if it is nil.
141-
func (tl *TraceLog) ensureConfig() {
143+
func (tl *TraceLog) determineTimeKey() string {
142144
if tl.Config == nil {
143-
tl.Config = DefaultTraceLogConfig()
145+
return defaultTimeKey
144146
}
147+
return tl.Config.TimeKey
145148
}
146149

147150
type ctxKey int
@@ -170,21 +173,21 @@ func (tl *TraceLog) TraceQueryStart(ctx context.Context, conn *pgx.Conn, data pg
170173
}
171174

172175
func (tl *TraceLog) TraceQueryEnd(ctx context.Context, conn *pgx.Conn, data pgx.TraceQueryEndData) {
173-
tl.ensureConfig()
174176
queryData := ctx.Value(tracelogQueryCtxKey).(*traceQueryData)
175177

176178
endTime := time.Now()
177179
interval := endTime.Sub(queryData.startTime)
180+
timeKey := tl.determineTimeKey()
178181

179182
if data.Err != nil {
180183
if tl.shouldLog(LogLevelError) {
181-
tl.log(ctx, conn, LogLevelError, "Query", map[string]any{"sql": queryData.sql, "args": logQueryArgs(queryData.args), "err": data.Err, tl.Config.TimeKey: interval})
184+
tl.log(ctx, conn, LogLevelError, "Query", map[string]any{"sql": queryData.sql, "args": logQueryArgs(queryData.args), "err": data.Err, timeKey: interval})
182185
}
183186
return
184187
}
185188

186189
if tl.shouldLog(LogLevelInfo) {
187-
tl.log(ctx, conn, LogLevelInfo, "Query", map[string]any{"sql": queryData.sql, "args": logQueryArgs(queryData.args), tl.Config.TimeKey: interval, "commandTag": data.CommandTag.String()})
190+
tl.log(ctx, conn, LogLevelInfo, "Query", map[string]any{"sql": queryData.sql, "args": logQueryArgs(queryData.args), timeKey: interval, "commandTag": data.CommandTag.String()})
188191
}
189192
}
190193

@@ -212,21 +215,21 @@ func (tl *TraceLog) TraceBatchQuery(ctx context.Context, conn *pgx.Conn, data pg
212215
}
213216

214217
func (tl *TraceLog) TraceBatchEnd(ctx context.Context, conn *pgx.Conn, data pgx.TraceBatchEndData) {
215-
tl.ensureConfig()
216218
queryData := ctx.Value(tracelogBatchCtxKey).(*traceBatchData)
217219

218220
endTime := time.Now()
219221
interval := endTime.Sub(queryData.startTime)
222+
timeKey := tl.determineTimeKey()
220223

221224
if data.Err != nil {
222225
if tl.shouldLog(LogLevelError) {
223-
tl.log(ctx, conn, LogLevelError, "BatchClose", map[string]any{"err": data.Err, tl.Config.TimeKey: interval})
226+
tl.log(ctx, conn, LogLevelError, "BatchClose", map[string]any{"err": data.Err, timeKey: interval})
224227
}
225228
return
226229
}
227230

228231
if tl.shouldLog(LogLevelInfo) {
229-
tl.log(ctx, conn, LogLevelInfo, "BatchClose", map[string]any{tl.Config.TimeKey: interval})
232+
tl.log(ctx, conn, LogLevelInfo, "BatchClose", map[string]any{timeKey: interval})
230233
}
231234
}
232235

@@ -245,21 +248,21 @@ func (tl *TraceLog) TraceCopyFromStart(ctx context.Context, conn *pgx.Conn, data
245248
}
246249

247250
func (tl *TraceLog) TraceCopyFromEnd(ctx context.Context, conn *pgx.Conn, data pgx.TraceCopyFromEndData) {
248-
tl.ensureConfig()
249251
copyFromData := ctx.Value(tracelogCopyFromCtxKey).(*traceCopyFromData)
250252

251253
endTime := time.Now()
252254
interval := endTime.Sub(copyFromData.startTime)
255+
timeKey := tl.determineTimeKey()
253256

254257
if data.Err != nil {
255258
if tl.shouldLog(LogLevelError) {
256-
tl.log(ctx, conn, LogLevelError, "CopyFrom", map[string]any{"tableName": copyFromData.TableName, "columnNames": copyFromData.ColumnNames, "err": data.Err, tl.Config.TimeKey: interval})
259+
tl.log(ctx, conn, LogLevelError, "CopyFrom", map[string]any{"tableName": copyFromData.TableName, "columnNames": copyFromData.ColumnNames, "err": data.Err, timeKey: interval})
257260
}
258261
return
259262
}
260263

261264
if tl.shouldLog(LogLevelInfo) {
262-
tl.log(ctx, conn, LogLevelInfo, "CopyFrom", map[string]any{"tableName": copyFromData.TableName, "columnNames": copyFromData.ColumnNames, "err": data.Err, tl.Config.TimeKey: interval, "rowCount": data.CommandTag.RowsAffected()})
265+
tl.log(ctx, conn, LogLevelInfo, "CopyFrom", map[string]any{"tableName": copyFromData.TableName, "columnNames": copyFromData.ColumnNames, "err": data.Err, timeKey: interval, "rowCount": data.CommandTag.RowsAffected()})
263266
}
264267
}
265268

@@ -276,20 +279,20 @@ func (tl *TraceLog) TraceConnectStart(ctx context.Context, data pgx.TraceConnect
276279
}
277280

278281
func (tl *TraceLog) TraceConnectEnd(ctx context.Context, data pgx.TraceConnectEndData) {
279-
tl.ensureConfig()
280282
connectData := ctx.Value(tracelogConnectCtxKey).(*traceConnectData)
281283

282284
endTime := time.Now()
283285
interval := endTime.Sub(connectData.startTime)
286+
timeKey := tl.determineTimeKey()
284287

285288
if data.Err != nil {
286289
if tl.shouldLog(LogLevelError) {
287290
tl.Logger.Log(ctx, LogLevelError, "Connect", map[string]any{
288-
"host": connectData.connConfig.Host,
289-
"port": connectData.connConfig.Port,
290-
"database": connectData.connConfig.Database,
291-
tl.Config.TimeKey: interval,
292-
"err": data.Err,
291+
"host": connectData.connConfig.Host,
292+
"port": connectData.connConfig.Port,
293+
"database": connectData.connConfig.Database,
294+
timeKey: interval,
295+
"err": data.Err,
293296
})
294297
}
295298
return
@@ -298,10 +301,10 @@ func (tl *TraceLog) TraceConnectEnd(ctx context.Context, data pgx.TraceConnectEn
298301
if data.Conn != nil {
299302
if tl.shouldLog(LogLevelInfo) {
300303
tl.log(ctx, data.Conn, LogLevelInfo, "Connect", map[string]any{
301-
"host": connectData.connConfig.Host,
302-
"port": connectData.connConfig.Port,
303-
"database": connectData.connConfig.Database,
304-
tl.Config.TimeKey: interval,
304+
"host": connectData.connConfig.Host,
305+
"port": connectData.connConfig.Port,
306+
"database": connectData.connConfig.Database,
307+
timeKey: interval,
305308
})
306309
}
307310
}
@@ -322,21 +325,21 @@ func (tl *TraceLog) TracePrepareStart(ctx context.Context, _ *pgx.Conn, data pgx
322325
}
323326

324327
func (tl *TraceLog) TracePrepareEnd(ctx context.Context, conn *pgx.Conn, data pgx.TracePrepareEndData) {
325-
tl.ensureConfig()
326328
prepareData := ctx.Value(tracelogPrepareCtxKey).(*tracePrepareData)
327329

328330
endTime := time.Now()
329331
interval := endTime.Sub(prepareData.startTime)
332+
timeKey := tl.determineTimeKey()
330333

331334
if data.Err != nil {
332335
if tl.shouldLog(LogLevelError) {
333-
tl.log(ctx, conn, LogLevelError, "Prepare", map[string]any{"name": prepareData.name, "sql": prepareData.sql, "err": data.Err, tl.Config.TimeKey: interval})
336+
tl.log(ctx, conn, LogLevelError, "Prepare", map[string]any{"name": prepareData.name, "sql": prepareData.sql, "err": data.Err, timeKey: interval})
334337
}
335338
return
336339
}
337340

338341
if tl.shouldLog(LogLevelInfo) {
339-
tl.log(ctx, conn, LogLevelInfo, "Prepare", map[string]any{"name": prepareData.name, "sql": prepareData.sql, tl.Config.TimeKey: interval, "alreadyPrepared": data.AlreadyPrepared})
342+
tl.log(ctx, conn, LogLevelInfo, "Prepare", map[string]any{"name": prepareData.name, "sql": prepareData.sql, timeKey: interval, "alreadyPrepared": data.AlreadyPrepared})
340343
}
341344
}
342345

0 commit comments

Comments
 (0)