]> git.kaiwu.me - haproxy.git/commitdiff
MINOR: cache: allow customizing ratio for early hints
authorMaxime Henrion <mhenrion@haproxy.com>
Mon, 1 Jun 2026 21:12:21 +0000 (17:12 -0400)
committerWilly Tarreau <w@1wt.eu>
Fri, 10 Jul 2026 14:42:35 +0000 (16:42 +0200)
It is now possible to give a "ratio" parameter to the "early-hints"
option to indicate what percentage of blocks to reserve for hints-only
entries. Defaults to 25%.

doc/configuration.txt
src/cache.c

index f25c460056dc233319363fd23717766291029958..3ced6c90ee7c4b0992a3362984b4cba4ee2cc23e 100644 (file)
@@ -20249,7 +20249,7 @@ cache <name>
   Declare a cache section, allocate a shared cache memory named <name>, the
   size of cache is mandatory (see keyword "total-max-size" below).
 
-early-hints <on/off>
+early-hints <on/off> [ratio <integer>]
   Enable or disable support for HTTP 103 Early Hints responses (see RFC 8297).
   When enabled, the cache remembers relevant "Link" response headers (with a
   "rel" parameter of preload, preconnect, dns-prefetch, modulepreload or
@@ -20261,6 +20261,12 @@ early-hints <on/off>
   Individual "cache-use" rules may opt out of this behavior with the
   "no-early-hints" keyword. The default value is off (disabled).
 
+  The optional "ratio" argument sets the percentage of total cache blocks
+  that may be reserved for hints-only entries (1 to 99, default 25). Higher
+  values keep hints alive longer at the cost of less space for full responses;
+  lower values prioritize full responses. This argument only makes sense when
+  "early-hints" is set to on, and is ignored otherwise.
+
 max-age <seconds>
   Define the maximum expiration duration. The expiration is set as the lowest
   value between the s-maxage or max-age (in this order) directive in the
index 6b2e8c28de0ac14a8be1e311b6f2bf1a03ef876a..f0c6417aff82b2165848f8a5bb165f1611be9d78 100644 (file)
@@ -50,7 +50,7 @@
 #define CACHE_CF_EARLY_HINTS       0x00000002 /* enable HTTP 103 Early Hints (disabled by default) */
 
 /* Soft cap on the number of cache blocks that may be held by hints entries. */
-#define CACHE_HINTS_CAP(cache)     ((cache)->maxblocks / 4)
+#define CACHE_HINTS_CAP(cache)     ((cache)->maxblocks * (cache)->early_hints_ratio / 100)
 
 static uint64_t cache_hash_seed = 0;
 
@@ -79,6 +79,7 @@ struct cache {
        unsigned int maxobjsz;   /* max-object-size (in bytes) */
        unsigned int max_secondary_entries;  /* maximum number of secondary entries with the same primary hash */
        uint8_t flags;           /* configuration flags, see CACHE_CF_* */
+       uint8_t early_hints_ratio;           /* percentage of cache reserved for hints entries (1..99) */
        char id[33];             /* cache name */
 };
 
@@ -1305,10 +1306,10 @@ static int cache_strip_entry(struct shared_context *shctx,
 
 /*
  * Free one entry's blocks. If the hints pool is at or above the limit
- * (set to 25%), first try to evict the oldest hint entry. Otherwise,
- * pop the oldest full entry and try to strip it. If the entry doesn't
- * contain the relevant Link headers, or if we are past the limit for
- * hints blocks, evict it entirely.
+ * (as defined by early_hints_ratio), first try to evict the oldest hints
+ * entry. Otherwise, pop the oldest full entry and try to strip it. If the
+ * entry doesn't contain the relevant Link headers, or if we are past the
+ * limit for hints blocks, evict it entirely.
  * Returns 1 on success, 0 if nothing could be freed.
  */
 static int cache_make_room(struct shared_context *shctx)
@@ -2774,6 +2775,7 @@ int cfg_parse_cache(const char *file, int linenum, char **args, int kwm)
                        tmp_cache_config->maxage = 60;
                        tmp_cache_config->maxblocks = 0;
                        tmp_cache_config->maxobjsz = 0;
+                       tmp_cache_config->early_hints_ratio = 25;
                        tmp_cache_config->max_secondary_entries = DEFAULT_MAX_SECONDARY_ENTRY;
                }
        } else if (strcmp(args[0], "total-max-size") == 0) {
@@ -2860,7 +2862,7 @@ int cfg_parse_cache(const char *file, int linenum, char **args, int kwm)
                        err_code |= ERR_WARN;
                }
        } else if (strcmp(args[0], "early-hints") == 0) {
-               if (alertif_too_many_args(1, file, linenum, args, &err_code)) {
+               if (alertif_too_many_args(3, file, linenum, args, &err_code)) {
                        err_code |= ERR_ABORT;
                        goto out;
                }
@@ -2874,6 +2876,32 @@ int cfg_parse_cache(const char *file, int linenum, char **args, int kwm)
                                   file, linenum, args[0]);
                        err_code |= ERR_WARN;
                }
+
+               if (*args[2]) {
+                       char *err;
+                       unsigned int ratio;
+
+                       if (strcmp(args[2], "ratio") != 0) {
+                               ha_alert("parsing [%s:%d]: '%s' unexpected argument '%s', expected 'ratio'.\n",
+                                        file, linenum, args[0], args[2]);
+                               err_code |= ERR_ALERT | ERR_FATAL;
+                               goto out;
+                       }
+                       if (!*args[3]) {
+                               ha_alert("parsing [%s:%d]: '%s ratio' expects an integer argument between 1 and 99.\n",
+                                        file, linenum, args[0]);
+                               err_code |= ERR_ALERT | ERR_FATAL;
+                               goto out;
+                       }
+                       ratio = strtoul(args[3], &err, 10);
+                       if (err == args[3] || *err != '\0' || ratio < 1 || ratio > 99) {
+                               ha_alert("parsing [%s:%d]: '%s ratio' expects an integer argument between 1 and 99, got '%s'.\n",
+                                        file, linenum, args[0], args[3]);
+                               err_code |= ERR_ALERT | ERR_FATAL;
+                               goto out;
+                       }
+                       tmp_cache_config->early_hints_ratio = ratio;
+               }
        } else if (strcmp(args[0], "max-secondary-entries") == 0) {
                unsigned int max_sec_entries;
                char *err;