+------------------------------------+ |1| version | 9 | +------------------------------------+ | Flags (8) | Length (24 bits) | +------------------------------------+ |X| Number of stream/rate pairs (31) | +------------------------------------+ |X| Stream ID (31 bits) | +------------------------------------+ | Consumption rate (32 bits) | +------------------------------------+ | (repeats) |
Consumption Rate: The current average rate that the receiver can drain the specified stream's buffer, in bytes/sec.
Sending endpoints MUST associate a consumption rate with each outgoing stream. Senders MUST NOT send data on a particular stream at an average rate greater than the stream's consumption rate, unless this would prevent any stream from sending data.
Receiving endpoints MAY send this frame at any time to update the streams' associated consumption rates on the sender.
3) Change setting ID 7 in the SETTINGS frame to SETTINGS_INITIAL_CONSUMPTION_RATE: The consumption rate for the remote endpoint to associate with new streams.
Rationale: These changes allow senders to implement a wide variety of stream scheduling algorithms, so long as they do not cause HoL blocking on the receiver, unless the only data available to send is on a stream that would cause HoL blocking (senders are not required to continue at this point; they may back off). This enables data to flow continuously without being stalled by window updates, a penalty that increases with latency. If a receiving endpoint suspects that its counterpart is not respecting its declared consumption rates, it may either accept the HoL blocking, or kill the offending stream with RST_STREAM(CANCEL).
Advantages:- Speed. Data is *always* being sent. Round trip delays are not periodically incurred, unlike per-stream window solutions.- Simplicity. Nearly as simple as eliminating flow control altogether, but without the HoL blocking. Does not require complex algorithms to resize windows or estimate BDP, unlike per-stream window solutions.- Cheap on resources. No additional buffering commitments beyond the TCP connection window.What do you guys think?Alek
On Fri, 8 Jun 2012, Alek Storm wrote:I see very little gain with this, and lots of problems.
Consumption Rate: The current average rate that the receiver can drain the specified stream's buffer, in bytes/sec.
For this to even begin to work sensibly you would need to explain with enough details exactly how the speed is to be calculated - in both ends.
For a situation where the consumption rate varies, it would basically result in the same amount of control traffic having to be sent.
For a receiver, it is much easier to know how much data it can receive next rather than how fast it can consume/handle such data. Writing software that can blindly assume a certain network related efficiency into the future is not always (if ever) possible. Especially when spread out over N concurrent streams.
If a receiver gets data at X bytes/second for a particular stream and can consume it all, how can it know how fast it can go? Won't it then basically always have to (slowly?) increase the rate as long as can consume it? Until it no longer can consume it and then it has to lower the rate again...
--
/ daniel.haxx.se
Alek
I initially proposed this in the massive flow control thread, and Mike asked for a separate thread and some elaboration, so here goes.I propose to:1) Remove the WINDOW_UPDATE frame2) Add a new RATE_UPDATE frame, with the following format:+------------------------------------+ |1| version | 9 | +------------------------------------+ | Flags (8) | Length (24 bits) | +------------------------------------+ |X| Number of stream/rate pairs (31) | +------------------------------------+ |X| Stream ID (31 bits) | +------------------------------------+ | Consumption rate (32 bits) | +------------------------------------+ | (repeats) |Consumption Rate: The current average rate that the receiver can drain the specified stream's buffer, in bytes/sec.
Sending endpoints MUST associate a consumption rate with each outgoing stream. Senders MUST NOT send data on a particular stream at an average rate greater than the stream's consumption rate, unless this would prevent any stream from sending data.Receiving endpoints MAY send this frame at any time to update the streams' associated consumption rates on the sender.3) Change setting ID 7 in the SETTINGS frame to SETTINGS_INITIAL_CONSUMPTION_RATE: The consumption rate for the remote endpoint to associate with new streams.Rationale: These changes allow senders to implement a wide variety of stream scheduling algorithms, so long as they do not cause HoL blocking on the receiver, unless the only data available to send is on a stream that would cause HoL blocking (senders are not required to continue at this point; they may back off). This enables data to flow continuously without being stalled by window updates, a penalty that increases with latency. If a receiving endpoint suspects that its counterpart is not respecting its declared consumption rates, it may either accept the HoL blocking, or kill the offending stream with RST_STREAM(CANCEL).Advantages:- Speed. Data is *always* being sent. Round trip delays are not periodically incurred, unlike per-stream window solutions.
- Simplicity. Nearly as simple as eliminating flow control altogether, but without the HoL blocking. Does not require complex algorithms to resize windows or estimate BDP, unlike per-stream window solutions.
- Cheap on resources. No additional buffering commitments beyond the TCP connection window.
- Simplicity. Nearly as simple as eliminating flow control altogether, but without the HoL blocking. Does not require complex algorithms to resize windows or estimate BDP, unlike per-stream window solutions.I see this as substantially more complex. With size based limits, its a pretty easy check. With rate based limits, you're now requiring all endpoints to clock IOs to certain rates, which is far more subjective than a window size. The hardest problem, which is just figuring out what the limits should be (regardless of whether specified as a rate or number of bytes) is the same in both cases.
- Cheap on resources. No additional buffering commitments beyond the TCP connection window.This is just not true nor is it an advantage.The proxy has to read from the socket buffer. The problem comes up when the proxy can't pass that data to the next hop (because the next hop is running slow/down/something bad) - it now has to buffer. You simply cannot leave it in the socket buffer, as that would mean that any single stream having a slow backend would block *all* streams.
I disagree on 'simplicity' - figuring out the 'data rate' is quite complicated, it's not really a constant.I would suggest at least replacing 'consumption rate' with 'buffer stats', which is far more deterministic and easy.Receiver will send 2 ints: available buffer and used buffer ( it can be packed in 32 bits - for example 16 bit each, in kB).Available buffer will be based on total RAM and number of active streams - can be used as a window, sendershould not send more than that.'Used buffer' is a proxy for data rate, sender can calculate consumption rate from 2 successive 'stats' packages.
I initially proposed this in the massive flow control thread, and Mike asked for a separate thread and some elaboration, so here goes.I propose to:1) Remove the WINDOW_UPDATE frame2) Add a new RATE_UPDATE frame, with the following format:+------------------------------------+ |1| version | 9 | +------------------------------------+ | Flags (8) | Length (24 bits) | +------------------------------------+ |X| Number of stream/rate pairs (31) | +------------------------------------+ |X| Stream ID (31 bits) | +------------------------------------+ | Consumption rate (32 bits) | +------------------------------------+ | (repeats) |Consumption Rate: The current average rate that the receiver can drain the specified stream's buffer, in bytes/sec.Sending endpoints MUST associate a consumption rate with each outgoing stream. Senders MUST NOT send data on a particular stream at an average rate greater than the stream's consumption rate, unless this would prevent any stream from sending data.Receiving endpoints MAY send this frame at any time to update the streams' associated consumption rates on the sender.3) Change setting ID 7 in the SETTINGS frame to SETTINGS_INITIAL_CONSUMPTION_RATE: The consumption rate for the remote endpoint to associate with new streams.Rationale: These changes allow senders to implement a wide variety of stream scheduling algorithms, so long as they do not cause HoL blocking on the receiver, unless the only data available to send is on a stream that would cause HoL blocking (senders are not required to continue at this point; they may back off). This enables data to flow continuously without being stalled by window updates, a penalty that increases with latency. If a receiving endpoint suspects that its counterpart is not respecting its declared consumption rates, it may either accept the HoL blocking, or kill the offending stream with RST_STREAM(CANCEL).Advantages:- Speed. Data is *always* being sent. Round trip delays are not periodically incurred, unlike per-stream window solutions.
- Simplicity. Nearly as simple as eliminating flow control altogether, but without the HoL blocking. Does not require complex algorithms to resize windows or estimate BDP, unlike per-stream window solutions.
- Cheap on resources. No additional buffering commitments beyond the TCP connection window.
What do you guys think?
- Simplicity. Nearly as simple as eliminating flow control altogether, but without the HoL blocking. Does not require complex algorithms to resize windows or estimate BDP, unlike per-stream window solutions.I see this as substantially more complex. With size based limits, its a pretty easy check. With rate based limits, you're now requiring all endpoints to clock IOs to certain rates, which is far more subjective than a window size. The hardest problem, which is just figuring out what the limits should be (regardless of whether specified as a rate or number of bytes) is the same in both cases.They're not clocking IO, they're clocking whatever process consumes the data, which I remain convinced will not be difficult.
If figuring out the limits is equally complex in both schemes (which I now realize is more subjective than I'd thought), then my scheme is still superior in that it does not incur periodic RTTs while window updates are sent.- Cheap on resources. No additional buffering commitments beyond the TCP connection window.This is just not true nor is it an advantage.The proxy has to read from the socket buffer. The problem comes up when the proxy can't pass that data to the next hop (because the next hop is running slow/down/something bad) - it now has to buffer. You simply cannot leave it in the socket buffer, as that would mean that any single stream having a slow backend would block *all* streams.Proxies have the easiest job of all - they just forward consumption rates from downstream receivers to upstream senders. The only time they would ever have to substantially buffer incoming data is when a consumption rate suddenly, drastically changes - and they're still protected by the TCP window. Or have I misunderstood you?