MEDIUM: stream-int: automatically disable CF_STREAMER flags after idle
Disabling the streamer flags after an idle period will help TCP proxies
to better adapt to the streams they're forwarding, especially with SSL
where this will allow the SSL sender to use smaller records. This is
typically used to optimally relay HTTP and derivatives such as SPDY or
HTTP/2 in pure TCP mode when haproxy is used as an SSL offloader.
This idea was first proposed by Ilya Grigorik on the haproxy mailing
list, and his tests seem to confirm the improvement :
https://www.mail-archive.com/haproxy@formilux.org/msg12576.html
diff --git a/src/stream_interface.c b/src/stream_interface.c
index b3364a3..83e814c 100644
--- a/src/stream_interface.c
+++ b/src/stream_interface.c
@@ -1096,6 +1096,17 @@
cur_read = 0;
+ if ((chn->flags & (CF_STREAMER | CF_STREAMER_FAST)) && !chn->buf->o &&
+ (unsigned short)(now_ms - chn->last_read) >= 1000) {
+ /* The buffer was empty and nothing was transferred for more
+ * than one second. This was caused by a pause and not by
+ * congestion. Reset any streaming mode to reduce latency.
+ */
+ chn->xfer_small = 0;
+ chn->xfer_large = 0;
+ chn->flags &= ~(CF_STREAMER | CF_STREAMER_FAST);
+ }
+
/* First, let's see if we may splice data across the channel without
* using a buffer.
*/
@@ -1190,38 +1201,6 @@
chn->total += ret;
if (channel_full(chn)) {
- /* The buffer is now full, there's no point in going through
- * the loop again.
- */
- if (!(chn->flags & CF_STREAMER_FAST) && (cur_read == buffer_len(chn->buf))) {
- chn->xfer_small = 0;
- chn->xfer_large++;
- if (chn->xfer_large >= 3) {
- /* we call this buffer a fast streamer if it manages
- * to be filled in one call 3 consecutive times.
- */
- chn->flags |= (CF_STREAMER | CF_STREAMER_FAST);
- //fputc('+', stderr);
- }
- }
- else if ((chn->flags & (CF_STREAMER | CF_STREAMER_FAST)) &&
- (cur_read <= chn->buf->size / 2)) {
- chn->xfer_large = 0;
- chn->xfer_small++;
- if (chn->xfer_small >= 2) {
- /* if the buffer has been at least half full twice,
- * we receive faster than we send, so at least it
- * is not a "fast streamer".
- */
- chn->flags &= ~CF_STREAMER_FAST;
- //fputc('-', stderr);
- }
- }
- else {
- chn->xfer_small = 0;
- chn->xfer_large = 0;
- }
-
si->flags |= SI_FL_WAIT_ROOM;
break;
}
@@ -1237,20 +1216,6 @@
* not have them in buffers.
*/
if (ret < max) {
- if ((chn->flags & (CF_STREAMER | CF_STREAMER_FAST)) &&
- (cur_read <= chn->buf->size / 2)) {
- chn->xfer_large = 0;
- chn->xfer_small++;
- if (chn->xfer_small >= 3) {
- /* we have read less than half of the buffer in
- * one pass, and this happened at least 3 times.
- * This is definitely not a streamer.
- */
- chn->flags &= ~(CF_STREAMER | CF_STREAMER_FAST);
- //fputc('!', stderr);
- }
- }
-
/* if a streamer has read few data, it may be because we
* have exhausted system buffers. It's not worth trying
* again.
@@ -1269,6 +1234,45 @@
if (conn->flags & CO_FL_ERROR)
return;
+ if (cur_read) {
+ if ((chn->flags & (CF_STREAMER | CF_STREAMER_FAST)) &&
+ (cur_read <= chn->buf->size / 2)) {
+ chn->xfer_large = 0;
+ chn->xfer_small++;
+ if (chn->xfer_small >= 3) {
+ /* we have read less than half of the buffer in
+ * one pass, and this happened at least 3 times.
+ * This is definitely not a streamer.
+ */
+ chn->flags &= ~(CF_STREAMER | CF_STREAMER_FAST);
+ }
+ else if (chn->xfer_small >= 2) {
+ /* if the buffer has been at least half full twice,
+ * we receive faster than we send, so at least it
+ * is not a "fast streamer".
+ */
+ chn->flags &= ~CF_STREAMER_FAST;
+ }
+ }
+ else if (!(chn->flags & CF_STREAMER_FAST) &&
+ (cur_read >= chn->buf->size - global.tune.maxrewrite)) {
+ /* we read a full buffer at once */
+ chn->xfer_small = 0;
+ chn->xfer_large++;
+ if (chn->xfer_large >= 3) {
+ /* we call this buffer a fast streamer if it manages
+ * to be filled in one call 3 consecutive times.
+ */
+ chn->flags |= (CF_STREAMER | CF_STREAMER_FAST);
+ }
+ }
+ else {
+ chn->xfer_small = 0;
+ chn->xfer_large = 0;
+ }
+ chn->last_read = now_ms;
+ }
+
if (conn_data_read0_pending(conn))
/* connection closed */
goto out_shutdown_r;