Skip to content

Commit 69b2827

Browse files
Fixing cancel race condition (#92)
Because we were spanning a new coroutine to cancel the request, the connection was closing and in parallel the sentinel was trying to cancel In http_client the coroutines were racing to closeResponse This PR fixes the issue and adds a new action to run race condition tests. Signed-off-by: Andre Furlan <[email protected]>
1 parent 7499fbe commit 69b2827

File tree

6 files changed

+23
-19
lines changed

6 files changed

+23
-19
lines changed

.github/workflows/go.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,5 +65,8 @@ jobs:
6565
env:
6666
CGO_ENABLED: 0
6767

68+
- name: Test-Race
69+
run: make test-race
70+
6871
- name: Build
6972
run: make linux

Makefile

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,12 @@ test: bin/gotestsum ## Run the go unit tests.
5757
@echo "INFO: Running all go unit tests."
5858
CGO_ENABLED=0 ./bin/gotestsum --format pkgname-and-test-fails --junitfile $(TEST_RESULTS_DIR)/unit-tests.xml ./...
5959

60+
.PHONY: test-race
61+
test-race:
62+
@echo "INFO: Running all go unit tests checking for race conditions."
63+
go test -race
64+
65+
6066
.PHONY: coverage
6167
coverage: bin/gotestsum ## Report the unit test code coverage.
6268
@echo "INFO: Generating unit test coverage report."

connection_test.go

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -544,7 +544,6 @@ func TestConn_pollOperation(t *testing.T) {
544544
Secret: []byte("b"),
545545
},
546546
})
547-
time.Sleep(50 * time.Millisecond)
548547
assert.Error(t, err)
549548
assert.Equal(t, 0, getOperationStatusCount)
550549
assert.Equal(t, 1, cancelOperationCount)
@@ -590,7 +589,7 @@ func TestConn_pollOperation(t *testing.T) {
590589
Secret: []byte("b"),
591590
},
592591
})
593-
time.Sleep(50 * time.Millisecond)
592+
594593
assert.Error(t, err)
595594
assert.GreaterOrEqual(t, getOperationStatusCount, 1)
596595
assert.Equal(t, 1, cancelOperationCount)
@@ -1183,7 +1182,6 @@ func TestConn_ExecContext(t *testing.T) {
11831182
ctx, cancel = context.WithCancel(ctx)
11841183
defer cancel()
11851184
res, err := testConn.ExecContext(ctx, "insert 10", []driver.NamedValue{})
1186-
time.Sleep(10 * time.Millisecond)
11871185
assert.Error(t, err)
11881186
assert.Nil(t, res)
11891187
assert.Equal(t, 1, executeStatementCount)

driver_e2e_test.go

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -291,7 +291,6 @@ func TestContextTimeoutExample(t *testing.T) {
291291
assert.True(t, ok)
292292
assert.Equal(t, 1, state.executeStatementCalls)
293293
assert.GreaterOrEqual(t, state.getOperationStatusCalls, 1)
294-
time.Sleep(time.Second)
295294
assert.Equal(t, 1, state.cancelOperationCalls)
296295

297296
}

internal/sentinel/sentinel.go

Lines changed: 13 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -83,15 +83,6 @@ func (s Sentinel) Watch(ctx context.Context, interval, timeout time.Duration) (W
8383
resCh <- ret
8484
}
8585
}
86-
canceler := func(ctx context.Context, reason string) {
87-
_, err := s.OnCancelFn()
88-
if err != nil {
89-
log.Err(err).Msgf("cancel failed after %s", reason)
90-
return
91-
}
92-
log.Debug().Msgf("cancel success")
93-
}
94-
9586
for {
9687
select {
9788
case <-intervalTimer.C:
@@ -117,7 +108,12 @@ func (s Sentinel) Watch(ctx context.Context, interval, timeout time.Duration) (W
117108
_ = intervalTimer.Stop()
118109
if s.OnCancelFn != nil && !s.onCancelFnCalled {
119110
s.onCancelFnCalled = true
120-
go canceler(ctx, ctx.Err().Error())
111+
_, err := s.OnCancelFn()
112+
if err != nil {
113+
log.Err(err).Msg("cancel failed")
114+
return WatchCanceled, nil, errors.Wrap(err, ctx.Err().Error())
115+
}
116+
log.Debug().Msgf("cancel success")
121117
}
122118
return WatchCanceled, nil, errors.Wrap(ctx.Err(), "sentinel context done")
123119
case <-timeoutTimerCh:
@@ -126,8 +122,14 @@ func (s Sentinel) Watch(ctx context.Context, interval, timeout time.Duration) (W
126122
_ = intervalTimer.Stop()
127123
if s.OnCancelFn != nil && !s.onCancelFnCalled {
128124
s.onCancelFnCalled = true
129-
go canceler(ctx, err.Error())
125+
_, err := s.OnCancelFn()
126+
if err != nil {
127+
log.Err(err).Msg("cancel failed")
128+
return WatchCanceled, nil, errors.Wrap(err, ctx.Err().Error())
129+
}
130+
log.Debug().Msgf("cancel success")
130131
}
132+
131133
return WatchTimeout, nil, err
132134
}
133135
}

internal/sentinel/sentinel_test.go

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,6 @@ func TestWatch(t *testing.T) {
9898
},
9999
}
100100
status, res, err := s.Watch(context.Background(), 0, 100*time.Millisecond)
101-
time.Sleep(10 * time.Millisecond)
102101
assert.Equal(t, WatchTimeout, status)
103102
assert.Equal(t, 1, cancelFnCalls)
104103
assert.Nil(t, res)
@@ -167,7 +166,6 @@ func TestWatch(t *testing.T) {
167166
}
168167
status, res, err := s.Watch(ctx, 0, 15*time.Second)
169168
assert.Equal(t, WatchCanceled, status)
170-
time.Sleep(10 * time.Millisecond)
171169
assert.Equal(t, cancelFnCalls, 1)
172170
assert.Nil(t, res)
173171
assert.Error(t, err)
@@ -194,7 +192,6 @@ func TestWatch(t *testing.T) {
194192
}
195193
status, res, err := s.Watch(ctx, 0, 200*time.Millisecond)
196194
assert.Equal(t, WatchCanceled, status)
197-
time.Sleep(10 * time.Millisecond)
198195
assert.Equal(t, cancelFnCalls, 1)
199196
assert.Nil(t, res)
200197
assert.Error(t, err)
@@ -273,7 +270,6 @@ func TestWatch(t *testing.T) {
273270
},
274271
}
275272
status, res, err := s.Watch(context.Background(), 0, 0)
276-
time.Sleep(10 * time.Millisecond)
277273
assert.Equal(t, WatchErr, status)
278274
assert.Equal(t, 1, statusFnCalls)
279275
assert.Nil(t, res)

0 commit comments

Comments
 (0)