MINOR: cache: add a counter for cache hits serving early hints

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.
This commit is contained in:
Maxime Henrion 2026-07-07 09:10:59 -04:00 committed by Willy Tarreau
parent 25fcee5b0c
commit 6f8c422252
10 changed files with 37 additions and 8 deletions

View 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 |

View 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:

View 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

View 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 */

View 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

View 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
};

View 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

View 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");

View 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));
}

View 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: