Improve Continuous integration and fix concurrency bugs (#66)
- improvements to the continuous GH actions - fix edge case concurrency bugs with Process.start() and state transitions discovered setting up CI.
This commit is contained in:
13
.github/workflows/go-ci.yml
vendored
13
.github/workflows/go-ci.yml
vendored
@@ -1,10 +1,11 @@
|
||||
# This workflow will build a golang project
|
||||
|
||||
name: Go
|
||||
name: CI
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ "main" ]
|
||||
|
||||
pull_request:
|
||||
branches: [ "main" ]
|
||||
|
||||
@@ -13,7 +14,7 @@ on:
|
||||
|
||||
jobs:
|
||||
|
||||
build:
|
||||
run-tests:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
@@ -23,5 +24,9 @@ jobs:
|
||||
with:
|
||||
go-version: '1.23'
|
||||
|
||||
- name: Test
|
||||
run: make test-all
|
||||
# necessary for testing proxy/Process swapping
|
||||
- name: Create simple-responder
|
||||
run: make simple-responder
|
||||
|
||||
- name: Test all
|
||||
run: make test-all
|
||||
@@ -133,9 +133,24 @@ func (p *Process) start() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// There is the possibility of a hard to replicate race condition where
|
||||
// curState *WAS* StateStopped but by the time we get to the p.stateMutex.Lock()
|
||||
// below, it's value has changed!
|
||||
|
||||
p.stateMutex.Lock()
|
||||
defer p.stateMutex.Unlock()
|
||||
|
||||
// with the exclusive lock, check if p.state is StateStopped, which is the only valid state
|
||||
// to transition from to StateReady
|
||||
|
||||
if p.state != StateStopped {
|
||||
if p.state == StateReady {
|
||||
return nil
|
||||
} else {
|
||||
return fmt.Errorf("start() can not proceed expected StateReady but process is in %v", p.state)
|
||||
}
|
||||
}
|
||||
|
||||
if err := p.setState(StateStarting); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -169,6 +169,8 @@ func TestProcess_LowTTLValue(t *testing.T) {
|
||||
}
|
||||
|
||||
// issue #19
|
||||
// This test makes sure using Process.Stop() does not affect pending HTTP
|
||||
// requests. All HTTP requests in this test should complete successfully.
|
||||
func TestProcess_HTTPRequestsHaveTimeToFinish(t *testing.T) {
|
||||
if testing.Short() {
|
||||
t.Skip("skipping slow test")
|
||||
@@ -192,8 +194,9 @@ func TestProcess_HTTPRequestsHaveTimeToFinish(t *testing.T) {
|
||||
wg.Add(1)
|
||||
go func(key string) {
|
||||
defer wg.Done()
|
||||
// send a request that should take 5 * 200ms (1 second) to complete
|
||||
req := httptest.NewRequest("GET", fmt.Sprintf("/slow-respond?echo=%s&delay=200ms", key), nil)
|
||||
// send a request where simple-responder is will wait 300ms before responding
|
||||
// this will simulate an in-progress request.
|
||||
req := httptest.NewRequest("GET", fmt.Sprintf("/slow-respond?echo=%s&delay=300ms", key), nil)
|
||||
w := httptest.NewRecorder()
|
||||
|
||||
process.ProxyRequest(w, req)
|
||||
@@ -209,9 +212,9 @@ func TestProcess_HTTPRequestsHaveTimeToFinish(t *testing.T) {
|
||||
}(key)
|
||||
}
|
||||
|
||||
// stop the requests in the middle
|
||||
// Stop the process while requests are still being processed
|
||||
go func() {
|
||||
<-time.After(500 * time.Millisecond)
|
||||
<-time.After(150 * time.Millisecond)
|
||||
process.Stop()
|
||||
}()
|
||||
|
||||
|
||||
Reference in New Issue
Block a user