Skip to content

Commit

Permalink
gbn: make resend timeout dynamic
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
ViktorTigerstrom committed Nov 21, 2023
1 parent 09c03b8 commit d10b72b
Show file tree
Hide file tree
Showing 6 changed files with 37 additions and 2 deletions.
7 changes: 7 additions & 0 deletions gbn/gbn_client.go
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,9 @@ handshake:
return err
}

// Notify the timeout manager that we sent a SYN.
g.timeoutManager.Sent(msg)

for {
// Wait for SYN
log.Debugf("Client waiting for SYN")
Expand Down Expand Up @@ -162,6 +165,10 @@ handshake:
return io.EOF
}

// Notify the timeout manager we've received the SYN response from the
// counterparty.
g.timeoutManager.Received(resp)

// Send SYNACK
log.Debugf("Client sending SYNACK")
synack, err := new(PacketSYNACK).Serialize()
Expand Down
6 changes: 6 additions & 0 deletions gbn/gbn_conn.go
Original file line number Diff line number Diff line change
Expand Up @@ -366,6 +366,9 @@ func (g *GoBackNConn) sendPacket(ctx context.Context, msg Message) error {
return fmt.Errorf("error calling sendToStream: %s", err)
}

// Notify the timeout manager that a message has been sent.
g.timeoutManager.Sent(msg)

return nil
}

Expand Down Expand Up @@ -499,6 +502,9 @@ func (g *GoBackNConn) receivePacketsForever() error { // nolint:gocyclo
return fmt.Errorf("deserialize error: %s", err)
}

// Notify the timeout manager that a message has been received.
g.timeoutManager.Received(msg)

// Reset the ping & pong timer if any packet is received.
// If ping/pong is disabled, this is a no-op.
g.pingTicker.Reset()
Expand Down
7 changes: 7 additions & 0 deletions gbn/gbn_server.go
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,9 @@ func (g *GoBackNConn) serverHandshake() error { // nolint:gocyclo
return err
}

// Notify the timeout manager that we sent a SYN.
g.timeoutManager.Sent(msg)

// Wait for SYNACK
log.Debugf("Waiting for client SYNACK")
select {
Expand Down Expand Up @@ -155,6 +158,10 @@ func (g *GoBackNConn) serverHandshake() error { // nolint:gocyclo

switch msg.(type) {
case *PacketSYNACK:
// Notify the timeout manager we've received the SYNACK
// response from the counterparty.
g.timeoutManager.Received(msg)

break
case *PacketSYN:
log.Debugf("Received SYN. Resend SYN.")
Expand Down
11 changes: 11 additions & 0 deletions gbn/options.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,17 @@ func WithTimeout(timeout time.Duration) Option {
}
}

// WithResendMultiplier is used to set the resend multiplier. This is the
// multiplier we use when dynamically setting the resend timeout, based on how
// long it took for other party to respond during the handshake.
// Note that when setting the resend timeout manually with the WithTimeout
// option, this option will have no effect.
func WithResendMultiplier(multiplier int) Option {
return func(conn *GoBackNConn) {
conn.timeoutManager.SetResendMultiplier(multiplier)
}
}

// WithHandshakeTimeout is used to set the timeout used during the handshake.
// If the timeout is reached without response from the peer then the handshake
// will be aborted and restarted.
Expand Down
6 changes: 5 additions & 1 deletion mailbox/client_conn.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,10 @@ const (
// to receive ACKS from the peer before resending the queue.
gbnTimeout = 1000 * time.Millisecond

// gbnResendMultiplier is the multiplier that we want the gbn
// connection to use when dynamically setting the resend timeout.
gbnResendMultiplier = 5

// gbnN is the queue size, N, that the gbn server will use. The gbn
// server will send up to N packets before requiring an ACK for the
// first packet in the queue.
Expand Down Expand Up @@ -156,7 +160,7 @@ func NewClientConn(ctx context.Context, sid [64]byte, serverHost string,
c := &ClientConn{
transport: transport,
gbnOptions: []gbn.Option{
gbn.WithTimeout(gbnTimeout),
gbn.WithResendMultiplier(gbnResendMultiplier),
gbn.WithHandshakeTimeout(gbnHandshakeTimeout),
gbn.WithKeepalivePing(
gbnClientPingTimeout, gbnPongTimeout,
Expand Down
2 changes: 1 addition & 1 deletion mailbox/server_conn.go
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ func NewServerConn(ctx context.Context, serverHost string,
cancel: cancel,
quit: make(chan struct{}),
gbnOptions: []gbn.Option{
gbn.WithTimeout(gbnTimeout),
gbn.WithResendMultiplier(gbnResendMultiplier),
gbn.WithHandshakeTimeout(gbnHandshakeTimeout),
gbn.WithKeepalivePing(
gbnServerPingTimeout, gbnPongTimeout,
Expand Down

0 comments on commit d10b72b

Please sign in to comment.