Skip to content

Commit cdbea5c

Browse files
egonelbreolavloite
andauthored
fix: data-race with concurrent scan and close (#304)
* fix: data-race with concurrent scan and close Fixes #299 * test: add test for race condition in close --------- Co-authored-by: Knut Olav Løite <koloite@gmail.com>
1 parent 081fa51 commit cdbea5c

File tree

2 files changed

+40
-5
lines changed

2 files changed

+40
-5
lines changed

driver.go

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -171,7 +171,7 @@ type connector struct {
171171
// propagated to the caller. This option is enabled by default.
172172
retryAbortsInternally bool
173173

174-
initClient sync.Mutex
174+
clientMu sync.Mutex
175175
client *spanner.Client
176176
clientErr error
177177
adminClient *adminapi.DatabaseAdminClient
@@ -317,8 +317,8 @@ func openDriverConn(ctx context.Context, c *connector) (driver.Conn, error) {
317317

318318
// increaseConnCount initializes the client and increases the number of connections that are active.
319319
func (c *connector) increaseConnCount(ctx context.Context, databaseName string, opts []option.ClientOption) error {
320-
c.initClient.Lock()
321-
defer c.initClient.Unlock()
320+
c.clientMu.Lock()
321+
defer c.clientMu.Unlock()
322322

323323
if c.clientErr != nil {
324324
return c.clientErr
@@ -349,8 +349,8 @@ func (c *connector) increaseConnCount(ctx context.Context, databaseName string,
349349
// decreaseConnCount decreases the number of connections that are active and closes the underlying clients if it was the
350350
// last connection.
351351
func (c *connector) decreaseConnCount() error {
352-
c.initClient.Lock()
353-
defer c.initClient.Unlock()
352+
c.clientMu.Lock()
353+
defer c.clientMu.Unlock()
354354

355355
c.connCount--
356356
if c.connCount > 0 {
@@ -373,6 +373,8 @@ func (c *connector) Close() error {
373373
delete(c.driver.connectors, c.dsn)
374374
c.driver.mu.Unlock()
375375

376+
c.clientMu.Lock()
377+
defer c.clientMu.Unlock()
376378
return c.closeClients()
377379
}
378380

driver_with_mockserver_test.go

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,39 @@ func TestSimpleQuery(t *testing.T) {
119119
}
120120
}
121121

122+
func TestConcurrentScanAndClose(t *testing.T) {
123+
t.Parallel()
124+
125+
db, _, teardown := setupTestDBConnection(t)
126+
defer teardown()
127+
rows, err := db.QueryContext(context.Background(), testutil.SelectFooFromBar)
128+
if err != nil {
129+
t.Fatal(err)
130+
}
131+
132+
// Only fetch the first row of the query to make sure that the rows are not auto-closed
133+
// when the end of the stream is reached.
134+
rows.Next()
135+
var got int64
136+
err = rows.Scan(&got)
137+
if err != nil {
138+
t.Fatal(err)
139+
}
140+
141+
// Close both the database and the rows (connection) in parallel.
142+
var wg sync.WaitGroup
143+
wg.Add(2)
144+
go func() {
145+
defer wg.Done()
146+
_ = db.Close()
147+
}()
148+
go func() {
149+
defer wg.Done()
150+
_ = rows.Close()
151+
}()
152+
wg.Wait()
153+
}
154+
122155
func TestSingleQueryWithTimestampBound(t *testing.T) {
123156
t.Parallel()
124157

0 commit comments

Comments
 (0)