Re: [quicwg/base-drafts] Include ack_delay when deciding with PN space to arm PTO for (#3666)

Martin Thomson <> Tue, 19 May 2020 00:45 UTC

Return-Path: <>
Received: from localhost (localhost []) by (Postfix) with ESMTP id 2A7923A0DB1 for <>; Mon, 18 May 2020 17:45:03 -0700 (PDT)
X-Virus-Scanned: amavisd-new at
X-Spam-Flag: NO
X-Spam-Score: -1.201
X-Spam-Status: No, score=-1.201 tagged_above=-999 required=5 tests=[DKIMWL_WL_HIGH=-0.001, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, HTML_MESSAGE=0.001, MAILING_LIST_MULTI=-1, RCVD_IN_MSPIKE_H2=-0.001, SPF_HELO_NONE=0.001, SPF_PASS=-0.001] autolearn=ham autolearn_force=no
Authentication-Results: (amavisd-new); dkim=pass (1024-bit key)
Received: from ([]) by localhost ( []) (amavisd-new, port 10024) with ESMTP id xXTslXfEyKuW for <>; Mon, 18 May 2020 17:45:01 -0700 (PDT)
Received: from ( []) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by (Postfix) with ESMTPS id 063E13A0D94 for <>; Mon, 18 May 2020 17:45:00 -0700 (PDT)
Received: from ( []) by (Postfix) with ESMTP id 315CC6A0A58 for <>; Mon, 18 May 2020 17:45:00 -0700 (PDT)
DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed;; s=pf2014; t=1589849100; bh=gbJ+jRhTD5g6YIX0G4nFNDSdJataAWd8EwFf3k8ZjWs=; h=Date:From:Reply-To:To:Cc:In-Reply-To:References:Subject:List-ID: List-Archive:List-Post:List-Unsubscribe:From; b=TR6xBJ3EPq8qj1l5pNJquNZHfDqjmr2sOXRx4/AAgWim3oG/3DM7xDmDyHJXW6CBt lHtw4ZEnVCJGmpNVcysi4SZqFjRdwh/Zui0Sm7Zuew+XTk38O/vZT8+lFm/EiE6j0s moEy7DcUYFfMBdcLpSSKXB8SaZFIWlzfaXNP0CX4=
Date: Mon, 18 May 2020 17:45:00 -0700
From: Martin Thomson <>
Reply-To: quicwg/base-drafts <>
To: quicwg/base-drafts <>
Cc: Subscribed <>
Message-ID: <quicwg/base-drafts/pull/3666/review/>
In-Reply-To: <quicwg/base-drafts/pull/>
References: <quicwg/base-drafts/pull/>
Subject: Re: [quicwg/base-drafts] Include ack_delay when deciding with PN space to arm PTO for (#3666)
Mime-Version: 1.0
Content-Type: multipart/alternative; boundary="--==_mimepart_5ec32c0c210e8_75f43f95eb0cd968112124"; charset="UTF-8"
Content-Transfer-Encoding: 7bit
Precedence: list
X-GitHub-Sender: martinthomson
X-GitHub-Recipient: quic-issues
X-GitHub-Reason: subscribed
X-Auto-Response-Suppress: All
Archived-At: <>
X-Mailman-Version: 2.1.29
List-Id: Notification list for GitHub issues related to the QUIC WG <>
List-Unsubscribe: <>, <>
List-Archive: <>
List-Post: <>
List-Help: <>
List-Subscribe: <>, <>
X-List-Received-Date: Tue, 19 May 2020 00:45:03 -0000

@martinthomson commented on this pull request.

One large comment.  Bottom line is that I don't think that this pseudocode is doing a great job of communicating here.  Also, this is a functional change to the outcome in more ways that the subject of the PR implies.

> +  // Calculate PTO duration, sent_time includes max_ack_delay.
+  timeout = smoothed_rtt + max(4 * rttvar, kGranularity)

Not that it was great before, but I think that this change obfuscates things.

I think that the inclusion of the max_ack_delay argument overloads GetEarliestTimeAndSpace in a way that just highlights how the dual purpose to which the function is applied is not appropriate.

We use this function for working out two things:
* when to declare something lost
* when the PTO timer should start 

The former is not affected by max_ack_delay.   The PTO calculation is, but this change adds max_ack_delay to the baseline rather than the PTO calculation itself.

For one, that's a fundamental change in logic for one (because PTO number 2 used to include 2*max_ack_delay and now it doesn't. Maybe that was a bug that this fixes, but that needs to be clear.

However, the main problem is the way that this code now communicates its intent.  If the intent is to start with a baseline (t_sent) and to add a PTO (RTT + 4*rttvar) that is then doubled each time, and only then compensate for ACK delay, you want to show your work, not hide it:

pto_timer = smoothed_rtt + max(4 * rttvar, kGranularity)
pto_timer *= 1 << pto_count
loss_detection_timer.update(sent_time + pto_timer + ?max_ack_delay)
Or is it, with maybe a fix for the packet number space issue...
pto_timer = smoothed_rtt + max(4 * rttvar, kGranularity) + ?max_ack_delay
pto_timer *= 1 << pto_count
loss_detection_timer.update(sent_time + pto_timer)

The trick is that the code that triggers SendOneOrTwoAckElicitingPackets() needs to follow the same logic.  But that too is too clever.  Basing that on the reference time (the time you sent the last packet) rather than the time that the timer is expected to pop is obtuse (even if it is correct as you currently formulate it).

Why not provide a CalculatePtoTime() function and use that?  This code doesn't need to be optimized, so you can calculate everything always.

pto_time = { Initial: 0, Handshake: 0, ApplicationData: 0 };
for space in time_of_last_sent_ack_eliciting_packet:
    if time_of_last_sent_ack_eliciting_packet[space] == 0:
    t = smoothed_rtt + max(4 * rttvar, kGranularity)
    t = t * (2 ^ pto_count)
    if space == ApplicationData: # or move this up as you need
        t += max_ack_delay
    pto_time[space] = time_of_last_sent_ack_eliciting_packet[space] + t
pto_time, pn_space = GetEarliestTimeAndSpace(pto_time)

This needs tweaking for the case where sent_time == 0, but that is just a case of factoring out the PTO interval calculation.  Note that you aren't adding max_ack_delay in that case, which is another difference.

You are receiving this because you are subscribed to this thread.
Reply to this email directly or view it on GitHub: