Optimize cache miss path with deferred cache write#12782
Optimize cache miss path with deferred cache write#12782bryancall wants to merge 3 commits intoapache:masterfrom
Conversation
059afe8 to
5ceb59c
Compare
masaori335
left a comment
There was a problem hiding this comment.
Deferred cache write seems useful if we know almost all the responses are not cacheable but still need to enable http cache for some reason.
There was a problem hiding this comment.
Pull request overview
This PR introduces a configuration option proxy.config.http.cache.defer_write_on_miss to optimize the cache miss path by deferring cache write operations until after response headers are received. This avoids unnecessary cache overhead for non-cacheable responses, potentially improving throughput significantly (~21x for Cache-Control: no-store responses). The default is disabled (0) to maintain safe, backwards-compatible behavior.
Key Changes
- New configuration option
cache_defer_write_on_miss(default: 0/disabled) for deferring cache writes - New function
handle_deferred_cache_write_completeto handle cache write completion after response evaluation - Modified
HandleCacheOpenReadMissto set deferred write flag when configured - Modified
handle_no_cache_operation_on_forward_server_responseto conditionally open cache for write after response header evaluation
Reviewed changes
Copilot reviewed 7 out of 7 changed files in this pull request and generated 1 comment.
Show a summary per file
| File | Description |
|---|---|
src/proxy/http/HttpTransact.cc |
Core logic for deferred cache write feature including new handler function and integration points |
src/proxy/http/HttpConfig.cc |
Configuration setup and reconfigure logic for new option |
include/ts/apidefs.h.in |
API enum TS_CONFIG_HTTP_CACHE_DEFER_WRITE_ON_MISS for plugin access |
include/proxy/http/OverridableConfigDefs.h |
X-macro definition for auto-generating configuration mappings |
include/proxy/http/HttpTransact.h |
State struct member cache_open_write_deferred and function declaration |
include/proxy/http/HttpConfig.h |
Configuration struct member cache_defer_write_on_miss |
configs/records.yaml.default.in |
Default configuration documentation and value (0/disabled) |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
|
[approve ci] |
|
Seems this has conflicts now. |
a373000 to
b9a6d7f
Compare
Add configuration option proxy.config.http.cache.defer_write_on_miss (default: 0/disabled) to defer opening the cache for write until after response headers are received and evaluated for cacheability. When enabled, this optimization: - Avoids unnecessary cache overhead for non-cacheable responses - Can improve throughput ~21x for non-cacheable content Trade-offs when enabled: - May affect read-while-write functionality - May reduce request coalescing for popular uncached URLs - Recommended only for traffic patterns with mostly unique URLs or predominantly non-cacheable content The optimization works by: 1. On cache miss, set cache_open_write_deferred=true instead of immediately opening cache for write 2. Go to origin and fetch response headers 3. Check if response is cacheable: - If not cacheable (no-store, etc.): skip cache write entirely - If cacheable: open cache for write and proceed normally
Add defer_write_on_miss to OVERRIDABLE_CONFIGS to enable per-transaction override via: - conf_remap plugin in remap.config - TSHttpTxnConfigIntSet() plugin API - Lua plugin, header_rewrite, etc.
Add the missing registration for proxy.config.http.cache.defer_write_on_miss in RecordsConfig.cc so the records system recognizes the config variable.
b9a6d7f to
21d2cd1
Compare
|
[approve ci autest] |
zwoop
left a comment
There was a problem hiding this comment.
Summary of my (and Claude) comments;
- How is this tested? Should we add some tests, or rely on existing tests?
- No documentation for new configuration
- We may want to documented interaction with
cache_open_write_fail_action - Similarly, document interaction with read-while-write
| # https://docs.trafficserver.apache.org/en/latest/admin-guide/files/records.yaml.en.html#proxy-config-http-cache-when-to-revalidate | ||
| when_to_revalidate: 0 | ||
|
|
||
| # Defer cache open write until response headers are received. |
There was a problem hiding this comment.
Instead of this long comment, shouldn't it just link to the relevant docs anchor?
| // | ||
| /////////////////////////////////////////////////////////////////////////////// | ||
| void | ||
| HttpTransact::handle_deferred_cache_write_complete(State *s) |
There was a problem hiding this comment.
This one is hard to review :) Are there any existing tests that hits this code (with the setting enabled, obviously)? If not, this feels like we ought to have an autest (ideally start a new proxy-verify "framework" test?).
| s->cache_info.action = CacheAction_t::NO_ACTION; | ||
| } else if (s->txn_conf->cache_defer_write_on_miss) { | ||
| // Defer cache open write until after response headers are received. | ||
| // This avoids cache overhead for non-cacheable responses but may |
There was a problem hiding this comment.
In general, I find Claud's comments to be overly verbose. If needed, this feels like it can be a one-line. It's pretty clear that
} else if (s->txn_conf->cache_defer_write_on_miss) {
will "defer cache open write" ... If the code is self documenting, we shouldn't state the obvious. I'd suggest you look through all the comments (or ask to) and remove the comments that are obvious.
| TxnDbg(dbg_ctl_http_trans, "[hncoofsr] deferred cache write, response %s cacheable", cacheable ? "is" : "is not"); | ||
|
|
||
| if (cacheable) { | ||
| // Response is cacheable - open the cache for write now |
There was a problem hiding this comment.
Again, stating the obvious in this comment, if (cacheable) is self explanatory.
| MgmtByte cache_open_write_fail_action = 0; | ||
|
|
||
| //////////////////////////////////////////////// | ||
| // Defer cache open write until response hdrs // |
There was a problem hiding this comment.
Probably a useful comment, but this is almost too short :) Shouldn't it be something like "...until response headers are received" ?
| bool skip_ip_allow_yaml = false; | ||
|
|
||
| /// True if we deferred opening the cache for write until after response headers. | ||
| /// This is an optimization to avoid cache overhead for non-cacheable responses. |
There was a problem hiding this comment.
The second comment here sounds like something that would be documented as part of the new setting. We know why we added this ...
| } | ||
| } | ||
|
|
||
| /////////////////////////////////////////////////////////////////////////////// |
| } | ||
|
|
||
| // If we get here, cache write failed - continue without caching | ||
| // The original handle_no_cache_operation_on_forward_server_response will be called |
There was a problem hiding this comment.
Well, yeah, I can see that handle_no_cache_operation_on_forward_server_response is called ...
| // # 6 - retry cache read on write lock failure, if retries exhausted serve stale if allowed, otherwise goto origin | ||
| {RECT_CONFIG, "proxy.config.http.cache.open_write_fail_action", RECD_INT, "0", RECU_DYNAMIC, RR_NULL, RECC_NULL, nullptr, RECA_NULL} | ||
| , | ||
| // # defer_write_on_miss: |
There was a problem hiding this comment.
I don't know that we need this verbose documentation here either, either leave it out, or refer to the Docs section as before.
| handle_cache_operation_on_forward_server_response(s); | ||
| return; | ||
|
|
||
| case CacheWriteLock_t::FAIL: |
There was a problem hiding this comment.
From Claude, sounds like there's a bit of overlap between these two settings, that should at least be documented.
HttpTransact.cc:3364:handle_deferred_cache_write_completeFAIL case does not consultcache_open_write_fail_action. The normal path inhandle_cache_write_lock(line 3156) checks this setting and can return 502 viaERROR_ON_MISS,ERROR_ON_MISS_STALE_ON_REVALIDATE, orERROR_ON_MISS_OR_REVALIDATE. The deferred path unconditionally treats FAIL asLOCK_MISSand serves the origin response. This is arguably correct (we already fetched from origin, so returning 502 would be wasteful), but the interaction betweendefer_write_on_missandcache_open_write_fail_actionneeds to be documented as a known trade-off. Users relying onERROR_ON_MISSfor thundering-herd protection should understand that the deferred path bypasses it.
| break; | ||
|
|
||
| case CacheWriteLock_t::READ_RETRY: | ||
| // This shouldn't happen for deferred write since we don't have an object to read |
There was a problem hiding this comment.
If this shouldn't happen, should it be a InkAssert() at least ? Not sure that TxnDbg is the right choice here either, maybe a notice or something else ?
Add configuration option
proxy.config.http.cache.defer_write_on_missto defer opening the cache for write until after response headers are received and evaluated for cacheability.Performance Impact
When enabled, this optimization:
Cache-Control: no-storeresponses)Trade-offs
When enabled:
Configuration
How it works
cache_open_write_deferred=trueinstead of immediately opening cache for writeNote: Default is currently enabled for CI testing. Will be changed to disabled before merge. After CI passed on setting the default to 1, I changed it to be 0.