Skip to content

Commit 73e0809

Browse files
committed
gbn: make resend timeout dynamic
Prior to this commit, the timeout before a client resends the queue of packets was always a fixed value. This fixed timeout isn't suitable for all clients as the latency for different clients varies. With this commit, we instead set the resend timeout based on how long it took for the other party to respond during the handshake process. The timeout is set to the time it took for the server to respond multiplied by the resendMultiplier, unless the duration is shorter than the default resend timeout. If the the resend timeout has been manually set, the resend timeout will always be set to that value, and won't be dynamically set.
1 parent 764fe2f commit 73e0809

File tree

6 files changed

+95
-23
lines changed

6 files changed

+95
-23
lines changed

gbn/gbn_client.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,7 @@ func (g *GoBackNConn) clientHandshake() error {
9595
}()
9696

9797
var resp Message
98+
var sentSYNTime time.Time
9899
handshake:
99100
for {
100101
// start Handshake
@@ -110,6 +111,10 @@ handshake:
110111
return err
111112
}
112113

114+
// Mark the time the client finished sending the SYN, and
115+
// started awaiting the server's response.
116+
sentSYNTime = time.Now()
117+
113118
for {
114119
// Wait for SYN
115120
log.Debugf("Client waiting for SYN")
@@ -159,6 +164,11 @@ handshake:
159164
return io.EOF
160165
}
161166

167+
// Increased the resend timeout to the time it took to receive the SYN
168+
// from the server multiplied by the resend multiplier.
169+
g.increaseResendTimeout(
170+
time.Since(sentSYNTime) * time.Duration(g.resendMultiplier))
171+
162172
// Send SYNACK
163173
log.Debugf("Client sending SYNACK")
164174
synack, err := new(PacketSYNACK).Serialize()

gbn/gbn_conn.go

Lines changed: 56 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ const (
2323
defaultHandshakeTimeout = 100 * time.Millisecond
2424
defaultResendTimeout = 100 * time.Millisecond
2525
finSendTimeout = 1000 * time.Millisecond
26+
defaultResendMultiplier = 5
2627
DefaultSendTimeout = math.MaxInt64
2728
DefaultRecvTimeout = math.MaxInt64
2829
)
@@ -60,10 +61,21 @@ type GoBackNConn struct {
6061
recvSeq uint8
6162

6263
// resendTimeout is the duration that will be waited before resending
63-
// the packets in the current queue.
64+
// the packets in the current queue. The timeout is dynamically set
65+
// during the handshake process, and is set to the time it took for the
66+
// other party to respond, multiplied by the resendMultiplier.
6467
resendTimeout time.Duration
6568
resendTicker *time.Ticker
6669

70+
// resendMultiplier defines the multiplier used when multiplying the
71+
// duration it took for the other party to respond when setting the
72+
// resendTimeout dynamically during the handshake.
73+
resendMultiplier int
74+
75+
// hasSetResendTimeout is used to determine if a resend timeout
76+
// has been manually set when creating a new GoBackNConn.
77+
hasSetResendTimeout bool
78+
6779
recvFromStream recvBytesFunc
6880
sendToStream sendBytesFunc
6981

@@ -164,26 +176,28 @@ func newGoBackNConn(ctx context.Context, sendFunc sendBytesFunc,
164176
ctxc, cancel := context.WithCancel(ctx)
165177

166178
return &GoBackNConn{
167-
n: n,
168-
s: n + 1,
169-
resendTimeout: defaultResendTimeout,
170-
recvFromStream: recvFunc,
171-
sendToStream: sendFunc,
172-
recvDataChan: make(chan *PacketData, n),
173-
sendDataChan: make(chan *PacketData),
174-
isServer: isServer,
175-
sendQueue: newQueue(n+1, defaultHandshakeTimeout),
176-
handshakeTimeout: defaultHandshakeTimeout,
177-
recvTimeout: DefaultRecvTimeout,
178-
sendTimeout: DefaultSendTimeout,
179-
receivedACKSignal: make(chan struct{}),
180-
resendSignal: make(chan struct{}, 1),
181-
remoteClosed: make(chan struct{}),
182-
awaitedACKSignal: make(chan struct{}, 1),
183-
awaitedNACKSignal: make(chan struct{}, 1),
184-
ctx: ctxc,
185-
cancel: cancel,
186-
quit: make(chan struct{}),
179+
n: n,
180+
s: n + 1,
181+
resendTimeout: defaultResendTimeout,
182+
resendMultiplier: defaultResendMultiplier,
183+
hasSetResendTimeout: false,
184+
recvFromStream: recvFunc,
185+
sendToStream: sendFunc,
186+
recvDataChan: make(chan *PacketData, n),
187+
sendDataChan: make(chan *PacketData),
188+
isServer: isServer,
189+
sendQueue: newQueue(n+1, defaultHandshakeTimeout),
190+
handshakeTimeout: defaultHandshakeTimeout,
191+
recvTimeout: DefaultRecvTimeout,
192+
sendTimeout: DefaultSendTimeout,
193+
receivedACKSignal: make(chan struct{}),
194+
resendSignal: make(chan struct{}, 1),
195+
remoteClosed: make(chan struct{}),
196+
awaitedACKSignal: make(chan struct{}, 1),
197+
awaitedNACKSignal: make(chan struct{}, 1),
198+
ctx: ctxc,
199+
cancel: cancel,
200+
quit: make(chan struct{}),
187201
}
188202
}
189203

@@ -920,3 +934,24 @@ func (g *GoBackNConn) proceedAfterTime(catchUpId int64) {
920934
// in the queue would lead to a NACK.
921935
time.AfterFunc(g.resendTimeout, cb)
922936
}
937+
938+
// increaseResendTimeout sets the resendTimeout to the passed duration, if the
939+
// passed duration is > than the current set resendTimeout.
940+
// If the resendTimeout has been set manually, this function will be a no op.
941+
func (g *GoBackNConn) increaseResendTimeout(resendTimeout time.Duration) {
942+
if g.hasSetResendTimeout {
943+
log.Tracef("Not increasing resendTimeout as it has been set " +
944+
"manually")
945+
946+
return
947+
}
948+
949+
if resendTimeout > g.resendTimeout {
950+
log.Tracef("Increasing resendTimeout to %v", resendTimeout)
951+
g.resendTimeout = resendTimeout
952+
} else {
953+
log.Tracef("Not increasing resendTimeout to %v as it is not "+
954+
"greater than the current resendTimeout %v",
955+
resendTimeout, g.resendTimeout)
956+
}
957+
}

gbn/gbn_server.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,7 @@ func (g *GoBackNConn) serverHandshake() error { // nolint:gocyclo
7979
}()
8080

8181
var n uint8
82+
var sentSYNTime time.Time
8283

8384
for {
8485
log.Debugf("Waiting for client SYN")
@@ -128,6 +129,10 @@ func (g *GoBackNConn) serverHandshake() error { // nolint:gocyclo
128129
return err
129130
}
130131

132+
// Mark the time the server finished sending the SYN, and
133+
// started awaiting the client's response.
134+
sentSYNTime = time.Now()
135+
131136
// Wait for SYNACK
132137
log.Debugf("Waiting for client SYNACK")
133138
select {
@@ -172,6 +177,11 @@ func (g *GoBackNConn) serverHandshake() error { // nolint:gocyclo
172177

173178
log.Debugf("Received SYNACK")
174179

180+
// Increased the resend timeout to the time it took to receive the
181+
// SYNACK from the client multiplied by the resend multiplier.
182+
g.increaseResendTimeout(
183+
time.Since(sentSYNTime) * time.Duration(g.resendMultiplier))
184+
175185
// Set all variables that are dependent on the value of N that we get
176186
// from the client
177187
g.setN(n)

gbn/options.go

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,18 @@ func WithMaxSendSize(size int) Option {
1919
func WithTimeout(timeout time.Duration) Option {
2020
return func(conn *GoBackNConn) {
2121
conn.resendTimeout = timeout
22+
conn.hasSetResendTimeout = true
23+
}
24+
}
25+
26+
// WithResendMultiplier is used to set the resend multiplier. This is the
27+
// multiplier we use when dynamically setting the resend timeout, based on how
28+
// long it took for other party to respond during the handshake.
29+
// Note that when setting the resend timeout manually with the WithTimeout
30+
// option, this option will have no effect.
31+
func WithResendMultiplier(multiplier int) Option {
32+
return func(conn *GoBackNConn) {
33+
conn.resendMultiplier = multiplier
2234
}
2335
}
2436

mailbox/client_conn.go

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,11 @@ const (
5656
// set up the clients send stream cipher box.
5757
gbnHandshakeTimeout = 2000 * time.Millisecond
5858

59+
// gbnResendMultiplier is the multiplier that gbn connection will use
60+
// when dynamically setting the resend timeout, based on how long it
61+
// took for other party to respond during the handshake.
62+
gbnResendMultiplier = 5
63+
5964
// gbnClientPingTimeout is the time after with the client will send the
6065
// server a ping message if it has not received any packets from the
6166
// server. The client will close the connection if it then does not
@@ -156,7 +161,7 @@ func NewClientConn(ctx context.Context, sid [64]byte, serverHost string,
156161
c := &ClientConn{
157162
transport: transport,
158163
gbnOptions: []gbn.Option{
159-
gbn.WithTimeout(gbnTimeout),
164+
gbn.WithResendMultiplier(gbnResendMultiplier),
160165
gbn.WithHandshakeTimeout(gbnHandshakeTimeout),
161166
gbn.WithKeepalivePing(
162167
gbnClientPingTimeout, gbnPongTimeout,

mailbox/server_conn.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ func NewServerConn(ctx context.Context, serverHost string,
7777
cancel: cancel,
7878
quit: make(chan struct{}),
7979
gbnOptions: []gbn.Option{
80-
gbn.WithTimeout(gbnTimeout),
80+
gbn.WithResendMultiplier(gbnResendMultiplier),
8181
gbn.WithHandshakeTimeout(gbnHandshakeTimeout),
8282
gbn.WithKeepalivePing(
8383
gbnServerPingTimeout, gbnPongTimeout,

0 commit comments

Comments
 (0)