]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
MAJOR: stats: Send stats dump over HTTP using zero-copy forwarding
authorChristopher Faulet <cfaulet@haproxy.com>
Thu, 25 Jan 2024 16:45:01 +0000 (17:45 +0100)
committerChristopher Faulet <cfaulet@haproxy.com>
Wed, 7 Feb 2024 14:04:48 +0000 (15:04 +0100)
Just like for the cache applet, it is now possible to send response to the
opposite side using the zero-copy forwarding. Internal functions were
slightly updated but there is nothing special to say. Except the requested
size during the nego stage is not exact.

include/haproxy/stats.h
src/resolvers.c
src/stats.c

index eef800452f609902b1ede4a2d7e2c803c33f88fb..031e9d9a9dfacb10607580d5327a5d280ed671de 100644 (file)
@@ -46,7 +46,7 @@ extern THREAD_LOCAL struct field info[];
 extern THREAD_LOCAL struct field *stat_l[];
 
 struct htx;
-int stats_putchk(struct appctx *appctx, struct htx *htx);
+int stats_putchk(struct appctx *appctx, struct buffer *buf, struct htx *htx);
 
 int stats_dump_one_line(const struct field *stats, size_t stats_count, struct appctx *appctx);
 
index 43cac2f56595bf2c3377f8b1b90c106bb1b8e013..10fc8147d6f08241476ce105dded0557233b1e55 100644 (file)
@@ -2776,7 +2776,7 @@ static int stats_dump_resolv_to_buffer(struct stconn *sc,
        if (!stats_dump_one_line(stats, idx, appctx))
                return 0;
 
-       if (!stats_putchk(appctx, NULL))
+       if (!stats_putchk(appctx, NULL, NULL))
                goto full;
 
        return 1;
index a1e2224d694304c532feb3097f25a82ee2e6a7ce..65a54f6b2e83dd0393aa61e98cb497a76c5304d4 100644 (file)
@@ -300,7 +300,7 @@ static THREAD_LOCAL struct buffer trash_chunk = BUF_NULL;
 
 static void stats_dump_json_schema(struct buffer *out);
 
-int stats_putchk(struct appctx *appctx, struct htx *htx)
+int stats_putchk(struct appctx *appctx, struct buffer *buf, struct htx *htx)
 {
        struct buffer *chk = &trash_chunk;
 
@@ -315,7 +315,13 @@ int stats_putchk(struct appctx *appctx, struct htx *htx)
                }
                chk->data = 0;
        }
-       else  {
+       else if (buf) {
+               if (b_data(chk) > b_room(buf))
+                       return 0;
+               b_putblk(buf, b_head(chk), b_data(chk));
+               chk->data = 0;
+       }
+       else {
                if (applet_putchk(appctx, chk) == -1)
                        return 0;
        }
@@ -3138,7 +3144,7 @@ static void stats_dump_html_px_end(struct stconn *sc, struct proxy *px)
  * both by the CLI and the HTTP entry points, and is able to dump the output
  * in HTML or CSV formats.
  */
-int stats_dump_proxy_to_buffer(struct stconn *sc, struct htx *htx,
+int stats_dump_proxy_to_buffer(struct stconn *sc, struct buffer *buf, struct htx *htx,
                               struct proxy *px)
 {
        struct appctx *appctx = __sc_appctx(sc);
@@ -3203,7 +3209,7 @@ more:
        case STAT_PX_ST_TH:
                if (ctx->flags & STAT_FMT_HTML) {
                        stats_dump_html_px_hdr(sc, px);
-                       if (!stats_putchk(appctx, htx))
+                       if (!stats_putchk(appctx, buf, htx))
                                goto full;
                }
 
@@ -3213,7 +3219,7 @@ more:
        case STAT_PX_ST_FE:
                /* print the frontend */
                if (stats_dump_fe_stats(sc, px)) {
-                       if (!stats_putchk(appctx, htx))
+                       if (!stats_putchk(appctx, buf, htx))
                                goto full;
                        ctx->flags |= STAT_STARTED;
                        if (ctx->field)
@@ -3234,6 +3240,10 @@ more:
                                        goto full;
                                }
                        }
+                       else if (buf) {
+                               if (buffer_almost_full(buf))
+                                       goto full;
+                       }
                        else {
                                struct channel *rep = sc_ic(appctx_sc(appctx));
 
@@ -3257,7 +3267,7 @@ more:
 
                        /* print the frontend */
                        if (stats_dump_li_stats(sc, px, l)) {
-                               if (!stats_putchk(appctx, htx))
+                               if (!stats_putchk(appctx, buf, htx))
                                        goto full;
                                ctx->flags |= STAT_STARTED;
                                if (ctx->field)
@@ -3306,6 +3316,10 @@ more:
                                        goto full;
                                }
                        }
+                       else if (buf) {
+                               if (buffer_almost_full(buf))
+                                       goto full;
+                       }
                        else {
                                struct channel *rep = sc_ic(appctx_sc(appctx));
 
@@ -3346,7 +3360,7 @@ more:
                        }
 
                        if (stats_dump_sv_stats(sc, px, sv)) {
-                               if (!stats_putchk(appctx, htx))
+                               if (!stats_putchk(appctx, buf, htx))
                                        goto full;
                                ctx->flags |= STAT_STARTED;
                                if (ctx->field)
@@ -3361,7 +3375,7 @@ more:
        case STAT_PX_ST_BE:
                /* print the backend */
                if (stats_dump_be_stats(sc, px)) {
-                       if (!stats_putchk(appctx, htx))
+                       if (!stats_putchk(appctx, buf, htx))
                                goto full;
                        ctx->flags |= STAT_STARTED;
                        if (ctx->field)
@@ -3375,7 +3389,7 @@ more:
        case STAT_PX_ST_END:
                if (ctx->flags & STAT_FMT_HTML) {
                        stats_dump_html_px_end(sc, px);
-                       if (!stats_putchk(appctx, htx))
+                       if (!stats_putchk(appctx, buf, htx))
                                goto full;
                }
 
@@ -3862,7 +3876,7 @@ static void stats_dump_json_end()
 /* Uses <appctx.ctx.stats.obj1> as a pointer to the current proxy and <obj2> as
  * a pointer to the current server/listener.
  */
-static int stats_dump_proxies(struct stconn *sc,
+static int stats_dump_proxies(struct stconn *sc, struct buffer *buf,
                               struct htx *htx)
 {
        struct appctx *appctx = __sc_appctx(sc);
@@ -3877,6 +3891,10 @@ static int stats_dump_proxies(struct stconn *sc,
                                goto full;
                        }
                }
+               else if (buf) {
+                       if (buffer_almost_full(buf))
+                               goto full;
+               }
                else {
                        struct channel *rep = sc_ic(appctx_sc(appctx));
 
@@ -3893,7 +3911,7 @@ static int stats_dump_proxies(struct stconn *sc,
                 */
                if (!(px->flags & PR_FL_DISABLED) && px->uuid > 0 &&
                    (px->cap & (PR_CAP_FE | PR_CAP_BE)) && !(px->cap & PR_CAP_INT)) {
-                       if (stats_dump_proxy_to_buffer(sc, htx, px) == 0)
+                       if (stats_dump_proxy_to_buffer(sc, buf, htx, px) == 0)
                                return 0;
                }
 
@@ -3914,7 +3932,7 @@ static int stats_dump_proxies(struct stconn *sc,
  * or -1 in case of any error. This function is used by both the CLI and the
  * HTTP handlers.
  */
-static int stats_dump_stat_to_buffer(struct stconn *sc, struct htx *htx)
+static int stats_dump_stat_to_buffer(struct stconn *sc, struct buffer *buf, struct htx *htx)
 {
        struct appctx *appctx = __sc_appctx(sc);
        struct show_stat_ctx *ctx = appctx->svcctx;
@@ -3937,7 +3955,7 @@ static int stats_dump_stat_to_buffer(struct stconn *sc, struct htx *htx)
                else if (!(ctx->flags & STAT_FMT_TYPED))
                        stats_dump_csv_header(ctx->domain);
 
-               if (!stats_putchk(appctx, htx))
+               if (!stats_putchk(appctx, buf, htx))
                        goto full;
 
                if (ctx->flags & STAT_JSON_SCHM) {
@@ -3950,7 +3968,7 @@ static int stats_dump_stat_to_buffer(struct stconn *sc, struct htx *htx)
        case STAT_STATE_INFO:
                if (ctx->flags & STAT_FMT_HTML) {
                        stats_dump_html_info(sc);
-                       if (!stats_putchk(appctx, htx))
+                       if (!stats_putchk(appctx, buf, htx))
                                goto full;
                }
 
@@ -3975,7 +3993,7 @@ static int stats_dump_stat_to_buffer(struct stconn *sc, struct htx *htx)
                case STATS_DOMAIN_PROXY:
                default:
                        /* dump proxies */
-                       if (!stats_dump_proxies(sc, htx))
+                       if (!stats_dump_proxies(sc, buf, htx))
                                return 0;
                        break;
                }
@@ -3989,7 +4007,7 @@ static int stats_dump_stat_to_buffer(struct stconn *sc, struct htx *htx)
                                stats_dump_html_end();
                        else
                                stats_dump_json_end();
-                       if (!stats_putchk(appctx, htx))
+                       if (!stats_putchk(appctx, buf, htx))
                                goto full;
                }
 
@@ -4466,6 +4484,20 @@ static int stats_send_http_redirect(struct stconn *sc, struct htx *htx)
        return 0;
 }
 
+static size_t http_stats_fastfwd(struct appctx *appctx, struct buffer *buf, size_t count, unsigned int flags)
+{
+       struct stconn *sc = appctx_sc(appctx);
+       size_t ret = 0;
+
+       ret = b_data(buf);
+       if (stats_dump_stat_to_buffer(sc, buf, NULL)) {
+               se_fl_clr(appctx->sedesc, SE_FL_MAY_FASTFWD);
+               appctx->st0 = STAT_HTTP_DONE;
+       }
+
+       ret = b_data(buf) - ret;
+       return ret;
+}
 
 /* This I/O handler runs as an applet embedded in a stream connector. It is
  * used to send HTTP stats over a TCP socket. The mechanism is very simple.
@@ -4484,6 +4516,9 @@ static void http_stats_io_handler(struct appctx *appctx)
        if (applet_fl_test(appctx, APPCTX_FL_OUTBLK_ALLOC|APPCTX_FL_OUTBLK_FULL))
                goto out;
 
+       if (se_fl_test(appctx->sedesc, SE_FL_MAY_FASTFWD))
+               goto out;
+
        if (!appctx_get_buf(appctx, &appctx->outbuf)) {
                appctx->flags |= APPCTX_FL_OUTBLK_ALLOC;
                goto out;
@@ -4503,8 +4538,11 @@ static void http_stats_io_handler(struct appctx *appctx)
 
                        if (find_http_meth(istptr(meth), istlen(meth)) == HTTP_METH_HEAD)
                                appctx->st0 = STAT_HTTP_DONE;
-                       else
+                       else {
+                               if (!(global.tune.no_zero_copy_fwd & NO_ZERO_COPY_FWD))
+                                       se_fl_set(appctx->sedesc, SE_FL_MAY_FASTFWD);
                                appctx->st0 = STAT_HTTP_DUMP;
+                       }
                }
        }
 
@@ -4514,7 +4552,7 @@ static void http_stats_io_handler(struct appctx *appctx)
                 * make sure to perform this call on an empty buffer
                 */
                trash_chunk.size = buf_room_for_htx_data(&trash_chunk);
-               if (stats_dump_stat_to_buffer(sc, res_htx))
+               if (stats_dump_stat_to_buffer(sc, NULL, res_htx))
                        appctx->st0 = STAT_HTTP_DONE;
        }
 
@@ -4542,6 +4580,7 @@ static void http_stats_io_handler(struct appctx *appctx)
                }
                res_htx->flags |= HTX_FL_EOM;
                applet_set_eoi(appctx);
+               se_fl_clr(appctx->sedesc, SE_FL_MAY_FASTFWD);
                appctx->st0 = STAT_HTTP_END;
        }
 
@@ -4565,6 +4604,7 @@ static void http_stats_io_handler(struct appctx *appctx)
                /* eat the whole request */
                b_reset(&appctx->inbuf);
                applet_fl_clr(appctx, APPCTX_FL_INBLK_FULL);
+               appctx->sedesc->iobuf.flags &= ~IOBUF_FL_FF_BLOCKED;
        }
        else if (applet_fl_test(appctx, APPCTX_FL_OUTBLK_FULL))
                applet_wont_consume(appctx);
@@ -5240,7 +5280,7 @@ static int cli_io_handler_dump_info(struct appctx *appctx)
 static int cli_io_handler_dump_stat(struct appctx *appctx)
 {
        trash_chunk = b_make(trash.area, trash.size, 0, 0);
-       return stats_dump_stat_to_buffer(appctx_sc(appctx), NULL);
+       return stats_dump_stat_to_buffer(appctx_sc(appctx), NULL, NULL);
 }
 
 static int cli_io_handler_dump_json_schema(struct appctx *appctx)
@@ -5501,6 +5541,7 @@ struct applet http_stats_applet = {
        .fct = http_stats_io_handler,
        .rcv_buf = appctx_rcv_buf,
        .snd_buf = appctx_snd_buf,
+       .fastfwd = http_stats_fastfwd,
        .release = NULL,
 };