]> git.kaiwu.me - haproxy.git/commitdiff
MINOR: cache: add a counter for cache hits serving early hints
authorMaxime Henrion <mhenrion@haproxy.com>
Tue, 7 Jul 2026 13:10:59 +0000 (09:10 -0400)
committerWilly Tarreau <w@1wt.eu>
Fri, 10 Jul 2026 14:42:35 +0000 (16:42 +0200)
Add a cache_hint_hits counter to the per-proxy HTTP stats, incremented
each time the cache emits a 103 Early Hints response. It is reported
in the CSV/typed outputs, on the stats page next to the cache hits,
and exported to prometheus as http_cache_hint_hits_total. Since the
new field changes the shm stats file mapping, its minor version is
bumped along with the object size assertion.

addons/promex/README
addons/promex/service-prometheus.c
doc/management.txt
include/haproxy/counters-t.h
include/haproxy/stats-file-t.h
include/haproxy/stats-t.h
src/cache.c
src/stats-file.c
src/stats-html.c
src/stats-proxy.c

index 4d52c1d1a4f07d2ae7f51e1bed331199e9901c08..9ed37debeb10df880e73e8be4d14105f1166d41a 100644 (file)
@@ -259,6 +259,7 @@ listed below. Metrics from extra counters are not listed.
 | haproxy_frontend_failed_header_rewriting_total  |
 | haproxy_frontend_http_cache_lookups_total       |
 | haproxy_frontend_http_cache_hits_total          |
+| haproxy_frontend_http_cache_hint_hits_total     |
 | haproxy_frontend_internal_errors_total          |
 +-------------------------------------------------+
 
@@ -329,6 +330,7 @@ listed below. Metrics from extra counters are not listed.
 | haproxy_backend_connection_reuses_total             |
 | haproxy_backend_http_cache_lookups_total            |
 | haproxy_backend_http_cache_hits_total               |
+| haproxy_backend_http_cache_hint_hits_total          |
 | haproxy_backend_max_queue_time_seconds              |
 | haproxy_backend_max_connect_time_seconds            |
 | haproxy_backend_max_response_time_seconds           |
index a792fed998ff5074e3febdfeb96da151bad8888c..a0ebdff6300bc48b7b565f82c43860d338ecbff1 100644 (file)
@@ -587,6 +587,7 @@ static int promex_dump_front_metrics(struct appctx *appctx, struct htx *htx)
                                case ST_I_PX_INTERCEPTED:
                                case ST_I_PX_CACHE_LOOKUPS:
                                case ST_I_PX_CACHE_HITS:
+                               case ST_I_PX_CACHE_HINT_HITS:
                                case ST_I_PX_COMP_IN:
                                case ST_I_PX_COMP_OUT:
                                case ST_I_PX_COMP_BYP:
@@ -1065,6 +1066,7 @@ static int promex_dump_back_metrics(struct appctx *appctx, struct htx *htx)
                                case ST_I_PX_REQ_TOT:
                                case ST_I_PX_CACHE_LOOKUPS:
                                case ST_I_PX_CACHE_HITS:
+                               case ST_I_PX_CACHE_HINT_HITS:
                                case ST_I_PX_COMP_IN:
                                case ST_I_PX_COMP_OUT:
                                case ST_I_PX_COMP_BYP:
index d7128847358826bae7c3d24d3df7f1af6796cca4..19bc71ff3df99abb205f24fc15c3421d42e1b743 100644 (file)
@@ -1375,6 +1375,8 @@ Here is the list of static fields using the proxy statistics domain:
       process started
  117. resbout [LFBS]: total number of response bytes sent since the worker
       process started
+ 118. cache_hint_hits [.FB.]: cumulative number of cache hits having served
+      early hints
 
 For all other statistics domains, the presence or the order of the fields are
 not guaranteed. In this case, the header line should always be used to parse
index 301490d29cedc029518c422e4d8495c47eeef6c0..b606428a820c947271e15237e8a002f00eb389a0 100644 (file)
@@ -91,6 +91,7 @@ struct fe_counters_shared_tg {
                        long long cum_req[4];   /* cumulated number of processed other/h1/h2/h3 requests */
                        long long cache_hits;   /* cache hits */
                        long long cache_lookups;/* cache lookups */
+                       long long cache_hint_hits; /* cache hits serving early hints */
                        long long comp_rsp;     /* number of compressed responses */
                        long long rsp[6];       /* http response codes */
                } http;
@@ -144,6 +145,7 @@ struct be_counters_shared_tg {
 
                        long long cache_hits;   /* cache hits */
                        long long cache_lookups;/* cache lookups */
+                       long long cache_hint_hits; /* cache hits serving early hints */
                        long long comp_rsp;     /* number of compressed responses */
                        long long rsp[6];       /* http response codes */
 
index e730681b434692a29ba372141829f247a7a62b47..0343ca9fe0d55c3f0b1486abf584deff1bd4092d 100644 (file)
@@ -15,7 +15,7 @@ enum stfile_domain {
 };
 
 #define SHM_STATS_FILE_VER_MAJOR         1
-#define SHM_STATS_FILE_VER_MINOR         2
+#define SHM_STATS_FILE_VER_MINOR         3
 
 #define SHM_STATS_FILE_HEARTBEAT_TIMEOUT 60 /* passed this delay (seconds) process which has not
                                              * sent heartbeat will be considered down
index 1078f90f9ec86d48baac51f608803b32415c6bd6..57a38c311d7d5f74d2f44daa91e9aa24fb64c494 100644 (file)
@@ -495,6 +495,7 @@ enum stat_idx_px {
        ST_I_PX_REQ_OUT,
        ST_I_PX_RES_IN,
        ST_I_PX_RES_OUT,
+       ST_I_PX_CACHE_HINT_HITS,
        /* must always be the last one */
        ST_I_PX_MAX
 };
index bc3d3d43a7ad0d6f470049fb894a0a2182978555..d5dad45d61807f1353b9332b72954f831c6032c0 100644 (file)
@@ -2442,16 +2442,17 @@ static int should_send_notmodified_response(struct cache *cache, struct htx *htx
  * length <hint_data_len>. The format matches what http_action_store_cache
  * writes: concatenated records of {uint16_t length, char value[length]}, each
  * value being a Link header value. Skipped for HTTP/1.0 clients.
+ * Returns 1 if a 103 response was emitted, 0 otherwise.
  */
-static void cache_emit_early_hints(struct stream *s, const char *hint_data,
-                                   unsigned int hint_data_len)
+static int cache_emit_early_hints(struct stream *s, const char *hint_data,
+                                  unsigned int hint_data_len)
 {
        struct htx *htx;
        const char *p = hint_data;
        const char *end = hint_data + hint_data_len;
 
        if (!(s->txn.http->req.flags & HTTP_MSGF_VER_11))
-               return;
+               return 0;
 
        htx = http_early_hint_start(s);
        if (!htx)
@@ -2470,11 +2471,12 @@ static void cache_emit_early_hints(struct stream *s, const char *hint_data,
 
        if (!http_early_hint_end(s))
                goto error;
-       return;
+       return 1;
 
   error:
        channel_htx_truncate(&s->res, htxbuf(&s->res.buf));
        s->txn.http->status = 0;
+       return 0;
 }
 
 enum act_return http_action_req_cache_use(struct act_rule *rule, struct proxy *px,
@@ -2586,8 +2588,20 @@ enum act_return http_action_req_cache_use(struct act_rule *rule, struct proxy *p
                shctx_wrunlock(shctx);
                cache_rdunlock(cache_tree);
 
-               if (hint_buf && b_data(hint_buf) > 0)
-                       cache_emit_early_hints(s, b_orig(hint_buf), b_data(hint_buf));
+               if (hint_buf && b_data(hint_buf) > 0 &&
+                   cache_emit_early_hints(s, b_orig(hint_buf), b_data(hint_buf))) {
+                       long long *ctr = NULL;
+
+                       if (px == strm_fe(s)) {
+                               if (px->fe_counters.shared.tg)
+                                       ctr = &px->fe_counters.shared.tg[tgid - 1]->p.http.cache_hint_hits;
+                       }
+                       else if (px->be_counters.shared.tg)
+                               ctr = &px->be_counters.shared.tg[tgid - 1]->p.http.cache_hint_hits;
+
+                       if (ctr)
+                               _HA_ATOMIC_INC(ctr);
+               }
 
                /* In case of Vary, we could have multiple entries with the same
                 * primary hash. We need to calculate the secondary hash in order
index 205b886b1849cb4c0541f44968e4854813e59748..7988f3bb90304c513f1b5861fb6a240dcc0ef10d 100644 (file)
@@ -849,7 +849,7 @@ int shm_stats_file_prepare(void)
        BUG_ON(sizeof(struct shm_stats_file_hdr) != 672, "shm_stats_file_hdr struct size changed, "
               "it is part of the exported API: ensure all precautions were taken (ie: shm_stats_file "
               "version change) before adjusting this");
-       BUG_ON(sizeof(struct shm_stats_file_object) != 552, "shm_stats_file_object struct size changed, "
+       BUG_ON(sizeof(struct shm_stats_file_object) != 560, "shm_stats_file_object struct size changed, "
               "it is part of the exported API: ensure all precautions were taken (ie: shm_stats_file "
               "version change) before adjusting this");
 
index 469ca6ac1c838111e9b0a66d2f3ba608c619cc94..4deeb695ab71c0f2ae3c53cc8482fad56849cf5c 100644 (file)
@@ -611,6 +611,7 @@ int stats_dump_fields_html(struct buffer *out,
                                      "<tr><th>Intercepted requests:</th><td>%s</td></tr>"
                                      "<tr><th>Cache lookups:</th><td>%s</td></tr>"
                                      "<tr><th>Cache hits:</th><td>%s</td><td>(%d%%)</td></tr>"
+                                     "<tr><th>Cache hint hits:</th><td>%s</td></tr>"
                                      "<tr><th>Failed hdr rewrites:</th><td>%s</td></tr>"
                                      "<tr><th>Internal errors:</th><td>%s</td></tr>"
                                      "",
@@ -619,6 +620,7 @@ int stats_dump_fields_html(struct buffer *out,
                                      U2H(stats[ST_I_PX_CACHE_HITS].u.u64),
                                      stats[ST_I_PX_CACHE_LOOKUPS].u.u64 ?
                                      (int)(100 * stats[ST_I_PX_CACHE_HITS].u.u64 / stats[ST_I_PX_CACHE_LOOKUPS].u.u64) : 0,
+                                     U2H(stats[ST_I_PX_CACHE_HINT_HITS].u.u64),
                                      U2H(stats[ST_I_PX_WREW].u.u64),
                                      U2H(stats[ST_I_PX_EINT].u.u64));
                }
@@ -1216,6 +1218,7 @@ int stats_dump_fields_html(struct buffer *out,
                                      "<tr><th>- other responses:</th><td>%s</td></tr>"
                                      "<tr><th>Cache lookups:</th><td>%s</td></tr>"
                                      "<tr><th>Cache hits:</th><td>%s</td><td>(%d%%)</td></tr>"
+                                     "<tr><th>Cache hint hits:</th><td>%s</td></tr>"
                                      "<tr><th>Failed hdr rewrites:</th><td>%s</td></tr>"
                                      "<tr><th>Internal errors:</th><td>%s</td></tr>"
                                      "",
@@ -1237,6 +1240,7 @@ int stats_dump_fields_html(struct buffer *out,
                                      U2H(stats[ST_I_PX_CACHE_HITS].u.u64),
                                      stats[ST_I_PX_CACHE_LOOKUPS].u.u64 ?
                                      (int)(100 * stats[ST_I_PX_CACHE_HITS].u.u64 / stats[ST_I_PX_CACHE_LOOKUPS].u.u64) : 0,
+                                     U2H(stats[ST_I_PX_CACHE_HINT_HITS].u.u64),
                                      U2H(stats[ST_I_PX_WREW].u.u64),
                                      U2H(stats[ST_I_PX_EINT].u.u64));
                }
index de924bec158b0ad7460be0c0605a59ed71c62bb7..9238810ef03b2adeeff82672b0d2336efd0ecfac 100644 (file)
@@ -196,6 +196,7 @@ const struct stat_col stat_cols_px[ST_I_PX_MAX] = {
        [ST_I_PX_REQ_OUT]       = ME_NEW_PX_SHARED("reqbout",       "req_bytes_out_total",           FN_COUNTER, FF_U64, req_out,              STATS_PX_CAP_LFBS, "Total number of request bytes sent since process started"),
        [ST_I_PX_RES_IN]        = ME_NEW_PX_SHARED("resbin",        "res_bytes_in_total",            FN_COUNTER, FF_U64, res_in,               STATS_PX_CAP_LFBS, "Total number of response bytes received since process started"),
        [ST_I_PX_RES_OUT]       = ME_NEW_PX_SHARED("resbout",       "res_bytes_out_total",           FN_COUNTER, FF_U64, res_out,              STATS_PX_CAP_LFBS, "Total number of response bytes sent since process started"),
+       [ST_I_PX_CACHE_HINT_HITS] = ME_NEW_PX_SHARED("cache_hint_hits",    "http_cache_hint_hits_total", FN_COUNTER, FF_U64, p.http.cache_hint_hits, STATS_PX_CAP__FB_, "Total number of HTTP requests for which a 103 Early Hints response was emitted from the cache on this frontend/backend since the worker process started"),
 
 };
 
@@ -236,6 +237,7 @@ static int stcol_hide(enum stat_idx_px idx, enum obj_type *objt)
        case ST_I_PX_INTERCEPTED:
        case ST_I_PX_CACHE_LOOKUPS:
        case ST_I_PX_CACHE_HITS:
+       case ST_I_PX_CACHE_HINT_HITS:
                return px->mode != PR_MODE_HTTP;
 
        case ST_I_PX_CHKFAIL: