Skip to content

Commit 103d99f

Browse files
stevenhmdelapenya
andauthored
fix: timeout and shutdown (#121)
Fix waitForPruneCondition timeout handling which was not resetting the connection timeout when new connections came in resulting in the reaper shutting down incorrectly. Don't log EOF errors. Add buffer to connection channels so we don't block the accepting goroutine. Fixes testcontainers/testcontainers-go#2348 Co-authored-by: Manuel de la Peña <social.mdelapenya@gmail.com>
1 parent 6e00e6e commit 103d99f

File tree

2 files changed

+33
-44
lines changed

2 files changed

+33
-44
lines changed

main.go

+22-34
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,10 @@ package main
33
import (
44
"bufio"
55
"context"
6+
"errors"
67
"flag"
78
"fmt"
9+
"io"
810
"log"
911
"net"
1012
"net/url"
@@ -135,8 +137,9 @@ func main() {
135137

136138
deathNote := sync.Map{}
137139

138-
connectionAccepted := make(chan net.Addr)
139-
connectionLost := make(chan net.Addr)
140+
// Buffered so we don't block the main process.
141+
connectionAccepted := make(chan net.Addr, 10)
142+
connectionLost := make(chan net.Addr, 10)
140143

141144
go processRequests(&deathNote, connectionAccepted, connectionLost)
142145

@@ -206,8 +209,10 @@ func processRequests(deathNote *sync.Map, connectionAccepted chan<- net.Addr, co
206209
}
207210

208211
if err != nil {
209-
log.Println(err)
210-
break
212+
if !errors.Is(err, io.EOF) {
213+
log.Println(err)
214+
}
215+
return
211216
}
212217
}
213218
}(conn)
@@ -216,45 +221,28 @@ func processRequests(deathNote *sync.Map, connectionAccepted chan<- net.Addr, co
216221

217222
func waitForPruneCondition(ctx context.Context, connectionAccepted <-chan net.Addr, connectionLost <-chan net.Addr) {
218223
connectionCount := 0
219-
never := make(chan time.Time, 1)
220-
defer close(never)
221-
222-
handleConnectionAccepted := func(addr net.Addr) {
223-
log.Printf("New client connected: %s", addr)
224-
connectionCount++
225-
}
226-
227-
select {
228-
case <-time.After(connectionTimeout):
229-
panic("Timed out waiting for the first connection")
230-
case addr := <-connectionAccepted:
231-
handleConnectionAccepted(addr)
232-
case <-ctx.Done():
233-
log.Println("Signal received")
234-
return
235-
}
236-
224+
timer := time.NewTimer(connectionTimeout)
237225
for {
238-
var noConnectionTimeout <-chan time.Time
239-
if connectionCount == 0 {
240-
noConnectionTimeout = time.After(reconnectionTimeout)
241-
} else {
242-
noConnectionTimeout = never
243-
}
244-
245226
select {
246227
case addr := <-connectionAccepted:
247-
handleConnectionAccepted(addr)
248-
break
228+
log.Printf("New client connected: %s", addr)
229+
connectionCount++
230+
if connectionCount == 1 {
231+
if !timer.Stop() {
232+
<-timer.C
233+
}
234+
}
249235
case addr := <-connectionLost:
250236
log.Printf("Client disconnected: %s", addr.String())
251237
connectionCount--
252-
break
238+
if connectionCount == 0 {
239+
timer.Reset(reconnectionTimeout)
240+
}
253241
case <-ctx.Done():
254242
log.Println("Signal received")
255243
return
256-
case <-noConnectionTimeout:
257-
log.Println("Timed out waiting for re-connection")
244+
case <-timer.C:
245+
log.Println("Timeout waiting for connection")
258246
return
259247
}
260248
}

main_test.go

+11-10
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,10 @@ import (
66
"context"
77
"fmt"
88
"io"
9+
"log"
910
"net"
1011
"os"
1112
"path/filepath"
12-
"strings"
1313
"sync"
1414
"testing"
1515
"time"
@@ -64,26 +64,27 @@ func TestInitialTimeout(t *testing.T) {
6464
// reset connectionTimeout
6565
connectionTimeout = testConnectionTimeout
6666

67+
origWriter := log.Default().Writer()
68+
defer func() {
69+
log.SetOutput(origWriter)
70+
}()
71+
var buf bytes.Buffer
72+
log.SetOutput(&buf)
73+
6774
acc := make(chan net.Addr)
6875
lost := make(chan net.Addr)
69-
7076
done := make(chan string)
7177

7278
go func() {
73-
defer func() {
74-
err := recover().(string)
75-
done <- err
76-
}()
7779
waitForPruneCondition(context.Background(), acc, lost)
80+
done <- buf.String()
7881
}()
7982

8083
select {
8184
case p := <-done:
82-
if !strings.Contains(p, "first connection") {
83-
t.Fail()
84-
}
85+
require.Contains(t, p, "Timeout waiting for connection")
8586
case <-time.After(7 * time.Second):
86-
t.Fail()
87+
t.Fatal("Timeout waiting prune condition")
8788
}
8889
}
8990

0 commit comments

Comments
 (0)