nquic_cc_cubic (nquic v1.0.0)

View Source

CUBIC congestion control per RFC 8312.

CUBIC uses a cubic function for window growth during congestion avoidance, achieving better bandwidth utilization on high-BDP paths than NewReno while remaining TCP-friendly. Includes fast convergence (Section 4.6) and a TCP-friendly region (Section 4.2) where W_est tracks standard TCP growth.

Summary

Functions

Get the current congestion window size in bytes.

Get the current maximum datagram size in bytes.

Get the current slow start threshold.

Return the HyStart++ phase. standard means HyStart++ is disabled, slow_start / css / done track the ladder. Used by tests and diagnostic accessors; production code does not need to inspect it.

Initialize CUBIC state with default 1200-byte MSS.

Initialize CUBIC state with options. Recognises

Reduce the congestion window on a loss event.

Reset CUBIC state to the initial window after an idle period (RFC 9002 Section 7.8). All recovery/epoch bookkeeping is cleared so the next acked packet starts a fresh slow-start phase.

Grow the congestion window when a packet is acknowledged.

Handle a sent packet (no-op for CUBIC).

Collapse the congestion window to the minimum (2 * max_datagram_size) on persistent congestion (RFC 9002 Section 7.6.2). The CUBIC epoch is also reset (epoch_start, origin_point, tcp_cwnd, w_max) so the next ACK after recovery starts a fresh growth phase from the collapsed window. recovery_start_time is reset so subsequent ACKs for newly sent packets are not filtered by the previous recovery period. Any pending spurious-loss snapshot is discarded.

Roll back the most recent congestion-event reduction (RFC 9002 Appendix A.10). No-op when no rollback snapshot is available.

Set the maximum datagram size, recalculating cwnd if still at initial window.

Functions

cbrt/1

-spec cbrt(float()) -> float().

cubic_window(T_us, WMax, MSS)

-spec cubic_window(non_neg_integer(), non_neg_integer(), pos_integer()) -> non_neg_integer().

get_cwnd/1

-spec get_cwnd(#state{cwnd :: non_neg_integer(),
                      ssthresh :: non_neg_integer(),
                      max_datagram_size :: pos_integer(),
                      recovery_start_time :: integer(),
                      w_max :: non_neg_integer(),
                      w_last_max :: non_neg_integer(),
                      epoch_start :: non_neg_integer(),
                      origin_point :: non_neg_integer(),
                      tcp_cwnd :: non_neg_integer(),
                      cubic_k :: undefined | float(),
                      congestion_occurred :: boolean(),
                      prev_state ::
                          undefined |
                          {non_neg_integer(),
                           non_neg_integer(),
                           integer(),
                           non_neg_integer(),
                           non_neg_integer(),
                           non_neg_integer(),
                           non_neg_integer(),
                           non_neg_integer(),
                           undefined | float(),
                           boolean()},
                      hystart_phase :: standard | slow_start | css | done,
                      last_round_min_rtt :: non_neg_integer(),
                      current_round_min_rtt :: non_neg_integer(),
                      rtt_sample_count :: non_neg_integer(),
                      last_round_largest_pn :: non_neg_integer(),
                      css_baseline_min_rtt :: non_neg_integer(),
                      css_round_count :: non_neg_integer()}) ->
                  non_neg_integer().

Get the current congestion window size in bytes.

get_max_datagram_size/1

-spec get_max_datagram_size(#state{cwnd :: non_neg_integer(),
                                   ssthresh :: non_neg_integer(),
                                   max_datagram_size :: pos_integer(),
                                   recovery_start_time :: integer(),
                                   w_max :: non_neg_integer(),
                                   w_last_max :: non_neg_integer(),
                                   epoch_start :: non_neg_integer(),
                                   origin_point :: non_neg_integer(),
                                   tcp_cwnd :: non_neg_integer(),
                                   cubic_k :: undefined | float(),
                                   congestion_occurred :: boolean(),
                                   prev_state ::
                                       undefined |
                                       {non_neg_integer(),
                                        non_neg_integer(),
                                        integer(),
                                        non_neg_integer(),
                                        non_neg_integer(),
                                        non_neg_integer(),
                                        non_neg_integer(),
                                        non_neg_integer(),
                                        undefined | float(),
                                        boolean()},
                                   hystart_phase :: standard | slow_start | css | done,
                                   last_round_min_rtt :: non_neg_integer(),
                                   current_round_min_rtt :: non_neg_integer(),
                                   rtt_sample_count :: non_neg_integer(),
                                   last_round_largest_pn :: non_neg_integer(),
                                   css_baseline_min_rtt :: non_neg_integer(),
                                   css_round_count :: non_neg_integer()}) ->
                               pos_integer().

Get the current maximum datagram size in bytes.

get_ssthresh/1

-spec get_ssthresh(#state{cwnd :: non_neg_integer(),
                          ssthresh :: non_neg_integer(),
                          max_datagram_size :: pos_integer(),
                          recovery_start_time :: integer(),
                          w_max :: non_neg_integer(),
                          w_last_max :: non_neg_integer(),
                          epoch_start :: non_neg_integer(),
                          origin_point :: non_neg_integer(),
                          tcp_cwnd :: non_neg_integer(),
                          cubic_k :: undefined | float(),
                          congestion_occurred :: boolean(),
                          prev_state ::
                              undefined |
                              {non_neg_integer(),
                               non_neg_integer(),
                               integer(),
                               non_neg_integer(),
                               non_neg_integer(),
                               non_neg_integer(),
                               non_neg_integer(),
                               non_neg_integer(),
                               undefined | float(),
                               boolean()},
                          hystart_phase :: standard | slow_start | css | done,
                          last_round_min_rtt :: non_neg_integer(),
                          current_round_min_rtt :: non_neg_integer(),
                          rtt_sample_count :: non_neg_integer(),
                          last_round_largest_pn :: non_neg_integer(),
                          css_baseline_min_rtt :: non_neg_integer(),
                          css_round_count :: non_neg_integer()}) ->
                      non_neg_integer().

Get the current slow start threshold.

hystart_phase/1

-spec hystart_phase(#state{cwnd :: non_neg_integer(),
                           ssthresh :: non_neg_integer(),
                           max_datagram_size :: pos_integer(),
                           recovery_start_time :: integer(),
                           w_max :: non_neg_integer(),
                           w_last_max :: non_neg_integer(),
                           epoch_start :: non_neg_integer(),
                           origin_point :: non_neg_integer(),
                           tcp_cwnd :: non_neg_integer(),
                           cubic_k :: undefined | float(),
                           congestion_occurred :: boolean(),
                           prev_state ::
                               undefined |
                               {non_neg_integer(),
                                non_neg_integer(),
                                integer(),
                                non_neg_integer(),
                                non_neg_integer(),
                                non_neg_integer(),
                                non_neg_integer(),
                                non_neg_integer(),
                                undefined | float(),
                                boolean()},
                           hystart_phase :: standard | slow_start | css | done,
                           last_round_min_rtt :: non_neg_integer(),
                           current_round_min_rtt :: non_neg_integer(),
                           rtt_sample_count :: non_neg_integer(),
                           last_round_largest_pn :: non_neg_integer(),
                           css_baseline_min_rtt :: non_neg_integer(),
                           css_round_count :: non_neg_integer()}) ->
                       standard | slow_start | css | done.

Return the HyStart++ phase. standard means HyStart++ is disabled, slow_start / css / done track the ladder. Used by tests and diagnostic accessors; production code does not need to inspect it.

init()

-spec init() ->
              #state{cwnd :: non_neg_integer(),
                     ssthresh :: non_neg_integer(),
                     max_datagram_size :: pos_integer(),
                     recovery_start_time :: integer(),
                     w_max :: non_neg_integer(),
                     w_last_max :: non_neg_integer(),
                     epoch_start :: non_neg_integer(),
                     origin_point :: non_neg_integer(),
                     tcp_cwnd :: non_neg_integer(),
                     cubic_k :: undefined | float(),
                     congestion_occurred :: boolean(),
                     prev_state ::
                         undefined |
                         {non_neg_integer(),
                          non_neg_integer(),
                          integer(),
                          non_neg_integer(),
                          non_neg_integer(),
                          non_neg_integer(),
                          non_neg_integer(),
                          non_neg_integer(),
                          undefined | float(),
                          boolean()},
                     hystart_phase :: standard | slow_start | css | done,
                     last_round_min_rtt :: non_neg_integer(),
                     current_round_min_rtt :: non_neg_integer(),
                     rtt_sample_count :: non_neg_integer(),
                     last_round_largest_pn :: non_neg_integer(),
                     css_baseline_min_rtt :: non_neg_integer(),
                     css_round_count :: non_neg_integer()}.

Initialize CUBIC state with default 1200-byte MSS.

init(Opts)

-spec init(map()) ->
              #state{cwnd :: non_neg_integer(),
                     ssthresh :: non_neg_integer(),
                     max_datagram_size :: pos_integer(),
                     recovery_start_time :: integer(),
                     w_max :: non_neg_integer(),
                     w_last_max :: non_neg_integer(),
                     epoch_start :: non_neg_integer(),
                     origin_point :: non_neg_integer(),
                     tcp_cwnd :: non_neg_integer(),
                     cubic_k :: undefined | float(),
                     congestion_occurred :: boolean(),
                     prev_state ::
                         undefined |
                         {non_neg_integer(),
                          non_neg_integer(),
                          integer(),
                          non_neg_integer(),
                          non_neg_integer(),
                          non_neg_integer(),
                          non_neg_integer(),
                          non_neg_integer(),
                          undefined | float(),
                          boolean()},
                     hystart_phase :: standard | slow_start | css | done,
                     last_round_min_rtt :: non_neg_integer(),
                     current_round_min_rtt :: non_neg_integer(),
                     rtt_sample_count :: non_neg_integer(),
                     last_round_largest_pn :: non_neg_integer(),
                     css_baseline_min_rtt :: non_neg_integer(),
                     css_round_count :: non_neg_integer()}.

Initialize CUBIC state with options. Recognises:

  • mss: pos_integer override of the 1200-byte default.
  • slow_start: standard (classic Reno-style slow start, the default) or hystart_plus_plus (RFC 9406, exits slow start on RTT inflation rather than waiting for loss).

on_congestion_event(State, LostBytes, BytesInFlight, SentTime)

-spec on_congestion_event(#state{cwnd :: non_neg_integer(),
                                 ssthresh :: non_neg_integer(),
                                 max_datagram_size :: pos_integer(),
                                 recovery_start_time :: integer(),
                                 w_max :: non_neg_integer(),
                                 w_last_max :: non_neg_integer(),
                                 epoch_start :: non_neg_integer(),
                                 origin_point :: non_neg_integer(),
                                 tcp_cwnd :: non_neg_integer(),
                                 cubic_k :: undefined | float(),
                                 congestion_occurred :: boolean(),
                                 prev_state ::
                                     undefined |
                                     {non_neg_integer(),
                                      non_neg_integer(),
                                      integer(),
                                      non_neg_integer(),
                                      non_neg_integer(),
                                      non_neg_integer(),
                                      non_neg_integer(),
                                      non_neg_integer(),
                                      undefined | float(),
                                      boolean()},
                                 hystart_phase :: standard | slow_start | css | done,
                                 last_round_min_rtt :: non_neg_integer(),
                                 current_round_min_rtt :: non_neg_integer(),
                                 rtt_sample_count :: non_neg_integer(),
                                 last_round_largest_pn :: non_neg_integer(),
                                 css_baseline_min_rtt :: non_neg_integer(),
                                 css_round_count :: non_neg_integer()},
                          non_neg_integer(),
                          non_neg_integer(),
                          non_neg_integer()) ->
                             #state{cwnd :: non_neg_integer(),
                                    ssthresh :: non_neg_integer(),
                                    max_datagram_size :: pos_integer(),
                                    recovery_start_time :: integer(),
                                    w_max :: non_neg_integer(),
                                    w_last_max :: non_neg_integer(),
                                    epoch_start :: non_neg_integer(),
                                    origin_point :: non_neg_integer(),
                                    tcp_cwnd :: non_neg_integer(),
                                    cubic_k :: undefined | float(),
                                    congestion_occurred :: boolean(),
                                    prev_state ::
                                        undefined |
                                        {non_neg_integer(),
                                         non_neg_integer(),
                                         integer(),
                                         non_neg_integer(),
                                         non_neg_integer(),
                                         non_neg_integer(),
                                         non_neg_integer(),
                                         non_neg_integer(),
                                         undefined | float(),
                                         boolean()},
                                    hystart_phase :: standard | slow_start | css | done,
                                    last_round_min_rtt :: non_neg_integer(),
                                    current_round_min_rtt :: non_neg_integer(),
                                    rtt_sample_count :: non_neg_integer(),
                                    last_round_largest_pn :: non_neg_integer(),
                                    css_baseline_min_rtt :: non_neg_integer(),
                                    css_round_count :: non_neg_integer()}.

Reduce the congestion window on a loss event.

on_idle_reset/1

-spec on_idle_reset(#state{cwnd :: non_neg_integer(),
                           ssthresh :: non_neg_integer(),
                           max_datagram_size :: pos_integer(),
                           recovery_start_time :: integer(),
                           w_max :: non_neg_integer(),
                           w_last_max :: non_neg_integer(),
                           epoch_start :: non_neg_integer(),
                           origin_point :: non_neg_integer(),
                           tcp_cwnd :: non_neg_integer(),
                           cubic_k :: undefined | float(),
                           congestion_occurred :: boolean(),
                           prev_state ::
                               undefined |
                               {non_neg_integer(),
                                non_neg_integer(),
                                integer(),
                                non_neg_integer(),
                                non_neg_integer(),
                                non_neg_integer(),
                                non_neg_integer(),
                                non_neg_integer(),
                                undefined | float(),
                                boolean()},
                           hystart_phase :: standard | slow_start | css | done,
                           last_round_min_rtt :: non_neg_integer(),
                           current_round_min_rtt :: non_neg_integer(),
                           rtt_sample_count :: non_neg_integer(),
                           last_round_largest_pn :: non_neg_integer(),
                           css_baseline_min_rtt :: non_neg_integer(),
                           css_round_count :: non_neg_integer()}) ->
                       #state{cwnd :: non_neg_integer(),
                              ssthresh :: non_neg_integer(),
                              max_datagram_size :: pos_integer(),
                              recovery_start_time :: integer(),
                              w_max :: non_neg_integer(),
                              w_last_max :: non_neg_integer(),
                              epoch_start :: non_neg_integer(),
                              origin_point :: non_neg_integer(),
                              tcp_cwnd :: non_neg_integer(),
                              cubic_k :: undefined | float(),
                              congestion_occurred :: boolean(),
                              prev_state ::
                                  undefined |
                                  {non_neg_integer(),
                                   non_neg_integer(),
                                   integer(),
                                   non_neg_integer(),
                                   non_neg_integer(),
                                   non_neg_integer(),
                                   non_neg_integer(),
                                   non_neg_integer(),
                                   undefined | float(),
                                   boolean()},
                              hystart_phase :: standard | slow_start | css | done,
                              last_round_min_rtt :: non_neg_integer(),
                              current_round_min_rtt :: non_neg_integer(),
                              rtt_sample_count :: non_neg_integer(),
                              last_round_largest_pn :: non_neg_integer(),
                              css_baseline_min_rtt :: non_neg_integer(),
                              css_round_count :: non_neg_integer()}.

Reset CUBIC state to the initial window after an idle period (RFC 9002 Section 7.8). All recovery/epoch bookkeeping is cleared so the next acked packet starts a fresh slow-start phase.

on_packet_acked/4

-spec on_packet_acked(#state{cwnd :: non_neg_integer(),
                             ssthresh :: non_neg_integer(),
                             max_datagram_size :: pos_integer(),
                             recovery_start_time :: integer(),
                             w_max :: non_neg_integer(),
                             w_last_max :: non_neg_integer(),
                             epoch_start :: non_neg_integer(),
                             origin_point :: non_neg_integer(),
                             tcp_cwnd :: non_neg_integer(),
                             cubic_k :: undefined | float(),
                             congestion_occurred :: boolean(),
                             prev_state ::
                                 undefined |
                                 {non_neg_integer(),
                                  non_neg_integer(),
                                  integer(),
                                  non_neg_integer(),
                                  non_neg_integer(),
                                  non_neg_integer(),
                                  non_neg_integer(),
                                  non_neg_integer(),
                                  undefined | float(),
                                  boolean()},
                             hystart_phase :: standard | slow_start | css | done,
                             last_round_min_rtt :: non_neg_integer(),
                             current_round_min_rtt :: non_neg_integer(),
                             rtt_sample_count :: non_neg_integer(),
                             last_round_largest_pn :: non_neg_integer(),
                             css_baseline_min_rtt :: non_neg_integer(),
                             css_round_count :: non_neg_integer()},
                      #sent_packet{packet_number :: nquic_packet_number:t(),
                                   time_sent :: non_neg_integer(),
                                   size :: non_neg_integer(),
                                   ack_eliciting :: boolean(),
                                   in_flight :: boolean(),
                                   frames :: [nquic_frame:t()]},
                      non_neg_integer(),
                      map()) ->
                         #state{cwnd :: non_neg_integer(),
                                ssthresh :: non_neg_integer(),
                                max_datagram_size :: pos_integer(),
                                recovery_start_time :: integer(),
                                w_max :: non_neg_integer(),
                                w_last_max :: non_neg_integer(),
                                epoch_start :: non_neg_integer(),
                                origin_point :: non_neg_integer(),
                                tcp_cwnd :: non_neg_integer(),
                                cubic_k :: undefined | float(),
                                congestion_occurred :: boolean(),
                                prev_state ::
                                    undefined |
                                    {non_neg_integer(),
                                     non_neg_integer(),
                                     integer(),
                                     non_neg_integer(),
                                     non_neg_integer(),
                                     non_neg_integer(),
                                     non_neg_integer(),
                                     non_neg_integer(),
                                     undefined | float(),
                                     boolean()},
                                hystart_phase :: standard | slow_start | css | done,
                                last_round_min_rtt :: non_neg_integer(),
                                current_round_min_rtt :: non_neg_integer(),
                                rtt_sample_count :: non_neg_integer(),
                                last_round_largest_pn :: non_neg_integer(),
                                css_baseline_min_rtt :: non_neg_integer(),
                                css_round_count :: non_neg_integer()}.

Grow the congestion window when a packet is acknowledged.

on_packet_sent(State, BytesSent, BytesInFlight)

-spec on_packet_sent(#state{cwnd :: non_neg_integer(),
                            ssthresh :: non_neg_integer(),
                            max_datagram_size :: pos_integer(),
                            recovery_start_time :: integer(),
                            w_max :: non_neg_integer(),
                            w_last_max :: non_neg_integer(),
                            epoch_start :: non_neg_integer(),
                            origin_point :: non_neg_integer(),
                            tcp_cwnd :: non_neg_integer(),
                            cubic_k :: undefined | float(),
                            congestion_occurred :: boolean(),
                            prev_state ::
                                undefined |
                                {non_neg_integer(),
                                 non_neg_integer(),
                                 integer(),
                                 non_neg_integer(),
                                 non_neg_integer(),
                                 non_neg_integer(),
                                 non_neg_integer(),
                                 non_neg_integer(),
                                 undefined | float(),
                                 boolean()},
                            hystart_phase :: standard | slow_start | css | done,
                            last_round_min_rtt :: non_neg_integer(),
                            current_round_min_rtt :: non_neg_integer(),
                            rtt_sample_count :: non_neg_integer(),
                            last_round_largest_pn :: non_neg_integer(),
                            css_baseline_min_rtt :: non_neg_integer(),
                            css_round_count :: non_neg_integer()},
                     non_neg_integer(),
                     non_neg_integer()) ->
                        #state{cwnd :: non_neg_integer(),
                               ssthresh :: non_neg_integer(),
                               max_datagram_size :: pos_integer(),
                               recovery_start_time :: integer(),
                               w_max :: non_neg_integer(),
                               w_last_max :: non_neg_integer(),
                               epoch_start :: non_neg_integer(),
                               origin_point :: non_neg_integer(),
                               tcp_cwnd :: non_neg_integer(),
                               cubic_k :: undefined | float(),
                               congestion_occurred :: boolean(),
                               prev_state ::
                                   undefined |
                                   {non_neg_integer(),
                                    non_neg_integer(),
                                    integer(),
                                    non_neg_integer(),
                                    non_neg_integer(),
                                    non_neg_integer(),
                                    non_neg_integer(),
                                    non_neg_integer(),
                                    undefined | float(),
                                    boolean()},
                               hystart_phase :: standard | slow_start | css | done,
                               last_round_min_rtt :: non_neg_integer(),
                               current_round_min_rtt :: non_neg_integer(),
                               rtt_sample_count :: non_neg_integer(),
                               last_round_largest_pn :: non_neg_integer(),
                               css_baseline_min_rtt :: non_neg_integer(),
                               css_round_count :: non_neg_integer()}.

Handle a sent packet (no-op for CUBIC).

on_persistent_congestion(State)

-spec on_persistent_congestion(#state{cwnd :: non_neg_integer(),
                                      ssthresh :: non_neg_integer(),
                                      max_datagram_size :: pos_integer(),
                                      recovery_start_time :: integer(),
                                      w_max :: non_neg_integer(),
                                      w_last_max :: non_neg_integer(),
                                      epoch_start :: non_neg_integer(),
                                      origin_point :: non_neg_integer(),
                                      tcp_cwnd :: non_neg_integer(),
                                      cubic_k :: undefined | float(),
                                      congestion_occurred :: boolean(),
                                      prev_state ::
                                          undefined |
                                          {non_neg_integer(),
                                           non_neg_integer(),
                                           integer(),
                                           non_neg_integer(),
                                           non_neg_integer(),
                                           non_neg_integer(),
                                           non_neg_integer(),
                                           non_neg_integer(),
                                           undefined | float(),
                                           boolean()},
                                      hystart_phase :: standard | slow_start | css | done,
                                      last_round_min_rtt :: non_neg_integer(),
                                      current_round_min_rtt :: non_neg_integer(),
                                      rtt_sample_count :: non_neg_integer(),
                                      last_round_largest_pn :: non_neg_integer(),
                                      css_baseline_min_rtt :: non_neg_integer(),
                                      css_round_count :: non_neg_integer()}) ->
                                  #state{cwnd :: non_neg_integer(),
                                         ssthresh :: non_neg_integer(),
                                         max_datagram_size :: pos_integer(),
                                         recovery_start_time :: integer(),
                                         w_max :: non_neg_integer(),
                                         w_last_max :: non_neg_integer(),
                                         epoch_start :: non_neg_integer(),
                                         origin_point :: non_neg_integer(),
                                         tcp_cwnd :: non_neg_integer(),
                                         cubic_k :: undefined | float(),
                                         congestion_occurred :: boolean(),
                                         prev_state ::
                                             undefined |
                                             {non_neg_integer(),
                                              non_neg_integer(),
                                              integer(),
                                              non_neg_integer(),
                                              non_neg_integer(),
                                              non_neg_integer(),
                                              non_neg_integer(),
                                              non_neg_integer(),
                                              undefined | float(),
                                              boolean()},
                                         hystart_phase :: standard | slow_start | css | done,
                                         last_round_min_rtt :: non_neg_integer(),
                                         current_round_min_rtt :: non_neg_integer(),
                                         rtt_sample_count :: non_neg_integer(),
                                         last_round_largest_pn :: non_neg_integer(),
                                         css_baseline_min_rtt :: non_neg_integer(),
                                         css_round_count :: non_neg_integer()}.

Collapse the congestion window to the minimum (2 * max_datagram_size) on persistent congestion (RFC 9002 Section 7.6.2). The CUBIC epoch is also reset (epoch_start, origin_point, tcp_cwnd, w_max) so the next ACK after recovery starts a fresh growth phase from the collapsed window. recovery_start_time is reset so subsequent ACKs for newly sent packets are not filtered by the previous recovery period. Any pending spurious-loss snapshot is discarded.

on_spurious_congestion/1

-spec on_spurious_congestion(#state{cwnd :: non_neg_integer(),
                                    ssthresh :: non_neg_integer(),
                                    max_datagram_size :: pos_integer(),
                                    recovery_start_time :: integer(),
                                    w_max :: non_neg_integer(),
                                    w_last_max :: non_neg_integer(),
                                    epoch_start :: non_neg_integer(),
                                    origin_point :: non_neg_integer(),
                                    tcp_cwnd :: non_neg_integer(),
                                    cubic_k :: undefined | float(),
                                    congestion_occurred :: boolean(),
                                    prev_state ::
                                        undefined |
                                        {non_neg_integer(),
                                         non_neg_integer(),
                                         integer(),
                                         non_neg_integer(),
                                         non_neg_integer(),
                                         non_neg_integer(),
                                         non_neg_integer(),
                                         non_neg_integer(),
                                         undefined | float(),
                                         boolean()},
                                    hystart_phase :: standard | slow_start | css | done,
                                    last_round_min_rtt :: non_neg_integer(),
                                    current_round_min_rtt :: non_neg_integer(),
                                    rtt_sample_count :: non_neg_integer(),
                                    last_round_largest_pn :: non_neg_integer(),
                                    css_baseline_min_rtt :: non_neg_integer(),
                                    css_round_count :: non_neg_integer()}) ->
                                #state{cwnd :: non_neg_integer(),
                                       ssthresh :: non_neg_integer(),
                                       max_datagram_size :: pos_integer(),
                                       recovery_start_time :: integer(),
                                       w_max :: non_neg_integer(),
                                       w_last_max :: non_neg_integer(),
                                       epoch_start :: non_neg_integer(),
                                       origin_point :: non_neg_integer(),
                                       tcp_cwnd :: non_neg_integer(),
                                       cubic_k :: undefined | float(),
                                       congestion_occurred :: boolean(),
                                       prev_state ::
                                           undefined |
                                           {non_neg_integer(),
                                            non_neg_integer(),
                                            integer(),
                                            non_neg_integer(),
                                            non_neg_integer(),
                                            non_neg_integer(),
                                            non_neg_integer(),
                                            non_neg_integer(),
                                            undefined | float(),
                                            boolean()},
                                       hystart_phase :: standard | slow_start | css | done,
                                       last_round_min_rtt :: non_neg_integer(),
                                       current_round_min_rtt :: non_neg_integer(),
                                       rtt_sample_count :: non_neg_integer(),
                                       last_round_largest_pn :: non_neg_integer(),
                                       css_baseline_min_rtt :: non_neg_integer(),
                                       css_round_count :: non_neg_integer()}.

Roll back the most recent congestion-event reduction (RFC 9002 Appendix A.10). No-op when no rollback snapshot is available.

set_max_datagram_size(State, Size)

-spec set_max_datagram_size(#state{cwnd :: non_neg_integer(),
                                   ssthresh :: non_neg_integer(),
                                   max_datagram_size :: pos_integer(),
                                   recovery_start_time :: integer(),
                                   w_max :: non_neg_integer(),
                                   w_last_max :: non_neg_integer(),
                                   epoch_start :: non_neg_integer(),
                                   origin_point :: non_neg_integer(),
                                   tcp_cwnd :: non_neg_integer(),
                                   cubic_k :: undefined | float(),
                                   congestion_occurred :: boolean(),
                                   prev_state ::
                                       undefined |
                                       {non_neg_integer(),
                                        non_neg_integer(),
                                        integer(),
                                        non_neg_integer(),
                                        non_neg_integer(),
                                        non_neg_integer(),
                                        non_neg_integer(),
                                        non_neg_integer(),
                                        undefined | float(),
                                        boolean()},
                                   hystart_phase :: standard | slow_start | css | done,
                                   last_round_min_rtt :: non_neg_integer(),
                                   current_round_min_rtt :: non_neg_integer(),
                                   rtt_sample_count :: non_neg_integer(),
                                   last_round_largest_pn :: non_neg_integer(),
                                   css_baseline_min_rtt :: non_neg_integer(),
                                   css_round_count :: non_neg_integer()},
                            pos_integer()) ->
                               #state{cwnd :: non_neg_integer(),
                                      ssthresh :: non_neg_integer(),
                                      max_datagram_size :: pos_integer(),
                                      recovery_start_time :: integer(),
                                      w_max :: non_neg_integer(),
                                      w_last_max :: non_neg_integer(),
                                      epoch_start :: non_neg_integer(),
                                      origin_point :: non_neg_integer(),
                                      tcp_cwnd :: non_neg_integer(),
                                      cubic_k :: undefined | float(),
                                      congestion_occurred :: boolean(),
                                      prev_state ::
                                          undefined |
                                          {non_neg_integer(),
                                           non_neg_integer(),
                                           integer(),
                                           non_neg_integer(),
                                           non_neg_integer(),
                                           non_neg_integer(),
                                           non_neg_integer(),
                                           non_neg_integer(),
                                           undefined | float(),
                                           boolean()},
                                      hystart_phase :: standard | slow_start | css | done,
                                      last_round_min_rtt :: non_neg_integer(),
                                      current_round_min_rtt :: non_neg_integer(),
                                      rtt_sample_count :: non_neg_integer(),
                                      last_round_largest_pn :: non_neg_integer(),
                                      css_baseline_min_rtt :: non_neg_integer(),
                                      css_round_count :: non_neg_integer()}.

Set the maximum datagram size, recalculating cwnd if still at initial window.