[nginx] QUIC: do not increase underutilized congestion window.
Vladimir Homutov
vl at inspert.ru
Mon Apr 28 09:07:40 UTC 2025
On Fri, Apr 25, 2025 at 10:51:12PM +0400, Sergey Kandaurov wrote:
> On Fri, Apr 25, 2025 at 01:55:11PM +0300, Vladimir Homutov via nginx-devel wrote:
> > missing attachments
> >
>
> Thanks for the provided debug, it is really helpful.
>
> Looking into debug, it becomes clear that once we sent something
> (be it a large response or MTU probe) that temporarily exceeds
> the congestion window (which is permitted for MTU probes),
> we can no longer send acknowledgments.
>
> This is especially visible if we don't receive ACKs for some reason,
> such as in your case,
> to decrease our inflight counter, so the connection becomes stalled.
>
> Below is a fix I have made without looking into your version
> (no offense, it is purely for aesthetic reasons).
>
> Although it appears to be quite similar to yours, it's somewhat less
> intrusive. I tried not to break sending segments for exceeded
> congestion, as well as to send correct frames in the ack_only mode.
indeed, my patch misses the part about queue management, this is
required part.
I'm agree regarding segmentation - sending pure ACK is definitely not
the case for optimizations;
otherwise, looks good for me.
>
>
> diff --git a/src/event/quic/ngx_event_quic_output.c b/src/event/quic/ngx_event_quic_output.c
> index a92a539f3..3fc2091e0 100644
> --- a/src/event/quic/ngx_event_quic_output.c
> +++ b/src/event/quic/ngx_event_quic_output.c
> @@ -55,7 +55,8 @@ static ssize_t ngx_quic_send_segments(ngx_connection_t *c, u_char *buf,
> size_t len, struct sockaddr *sockaddr, socklen_t socklen, size_t segment);
> #endif
> static ssize_t ngx_quic_output_packet(ngx_connection_t *c,
> - ngx_quic_send_ctx_t *ctx, u_char *data, size_t max, size_t min);
> + ngx_quic_send_ctx_t *ctx, u_char *data, size_t max, size_t min,
> + ngx_int_t ack_only);
> static void ngx_quic_init_packet(ngx_connection_t *c, ngx_quic_send_ctx_t *ctx,
> ngx_quic_header_t *pkt, ngx_quic_path_t *path);
> static ngx_uint_t ngx_quic_get_padding_level(ngx_connection_t *c);
> @@ -131,8 +132,7 @@ ngx_quic_create_datagrams(ngx_connection_t *c)
> ngx_memzero(preserved_pnum, sizeof(preserved_pnum));
> #endif
>
> - while (cg->in_flight < cg->window) {
> -
> + do {
> p = dst;
>
> len = ngx_quic_path_limit(c, path, path->mtu);
> @@ -158,7 +158,8 @@ ngx_quic_create_datagrams(ngx_connection_t *c)
> return NGX_OK;
> }
>
> - n = ngx_quic_output_packet(c, ctx, p, len, min);
> + n = ngx_quic_output_packet(c, ctx, p, len, min,
> + cg->in_flight >= cg->window);
> if (n == NGX_ERROR) {
> return NGX_ERROR;
> }
> @@ -187,7 +188,8 @@ ngx_quic_create_datagrams(ngx_connection_t *c)
> ngx_quic_commit_send(c);
>
> path->sent += len;
> - }
> +
> + } while (cg->in_flight < cg->window);
>
> return NGX_OK;
> }
> @@ -315,6 +317,10 @@ ngx_quic_allow_segmentation(ngx_connection_t *c)
>
> bytes += f->len;
>
> + if (qc->congestion->in_flight + bytes >= qc->congestion->window)) {
> + return 0;
> + }
> +
> if (bytes > len * 3) {
> /* require at least ~3 full packets to batch */
> return 1;
> @@ -364,7 +370,7 @@ ngx_quic_create_segments(ngx_connection_t *c)
>
> if (len && cg->in_flight + (p - dst) < cg->window) {
>
> - n = ngx_quic_output_packet(c, ctx, p, len, len);
> + n = ngx_quic_output_packet(c, ctx, p, len, len, 0);
> if (n == NGX_ERROR) {
> return NGX_ERROR;
> }
> @@ -521,7 +527,7 @@ ngx_quic_get_padding_level(ngx_connection_t *c)
>
> static ssize_t
> ngx_quic_output_packet(ngx_connection_t *c, ngx_quic_send_ctx_t *ctx,
> - u_char *data, size_t max, size_t min)
> + u_char *data, size_t max, size_t min, ngx_int_t ack_only)
> {
> size_t len, pad, min_payload, max_payload;
> u_char *p;
> @@ -585,6 +591,10 @@ ngx_quic_output_packet(ngx_connection_t *c, ngx_quic_send_ctx_t *ctx,
> {
> f = ngx_queue_data(q, ngx_quic_frame_t, queue);
>
> + if (ack_only && f->type != NGX_QUIC_FT_ACK) {
> + continue;
> + }
> +
> if (len >= max_payload) {
> break;
> }
> @@ -651,14 +661,20 @@ ngx_quic_output_packet(ngx_connection_t *c, ngx_quic_send_ctx_t *ctx,
> f->plen = res.len;
> }
>
> - while (nframes--) {
> + while (nframes) {
> q = ngx_queue_head(&ctx->frames);
> f = ngx_queue_data(q, ngx_quic_frame_t, queue);
>
> + if (ack_only && f->type != NGX_QUIC_FT_ACK) {
> + continue;
> + }
> +
> f->pkt_need_ack = pkt.need_ack;
>
> ngx_queue_remove(q);
> ngx_queue_insert_tail(&ctx->sending, q);
> +
> + nframes--;
> }
>
> return res.len;
More information about the nginx-devel
mailing list