Skip to content

Commit 5ceb59c

Browse files
committed
Optimize cache miss path with deferred cache write (configurable)
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
1 parent 855eb10 commit 5ceb59c

File tree

5 files changed

+99
-0
lines changed

5 files changed

+99
-0
lines changed

configs/records.yaml.default.in

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,12 @@ records:
8686
# https://docs.trafficserver.apache.org/en/latest/admin-guide/files/records.yaml.en.html#proxy-config-http-cache-when-to-revalidate
8787
when_to_revalidate: 0
8888

89+
# Defer cache open write until response headers are received.
90+
# Improves performance for non-cacheable responses but may affect
91+
# read-while-write and request coalescing for popular uncached URLs.
92+
# 0 - disabled (default, safe), 1 - enabled
93+
defer_write_on_miss: 0
94+
8995
##############################################################################
9096
# Origin server connect attempts. Docs:
9197
# https://docs.trafficserver.apache.org/en/latest/admin-guide/files/records.yaml.en.html#origin-server-connect-attempts

include/proxy/http/HttpConfig.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -578,6 +578,11 @@ struct OverridableHttpConfigParams {
578578

579579
MgmtByte cache_open_write_fail_action = 0;
580580

581+
////////////////////////////////////////////////
582+
// Defer cache open write until response hdrs //
583+
////////////////////////////////////////////////
584+
MgmtByte cache_defer_write_on_miss = 0;
585+
581586
////////////////////////
582587
// Check Post request //
583588
////////////////////////

include/proxy/http/HttpTransact.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -693,6 +693,10 @@ class HttpTransact
693693
bool is_method_stats_incremented = false;
694694
bool skip_ip_allow_yaml = false;
695695

696+
/// True if we deferred opening the cache for write until after response headers.
697+
/// This is an optimization to avoid cache overhead for non-cacheable responses.
698+
bool cache_open_write_deferred = false;
699+
696700
/// True if the response is cacheable because of negative caching configuration.
697701
///
698702
/// This being true implies the following:
@@ -997,6 +1001,7 @@ class HttpTransact
9971001
static void set_cache_prepare_write_action_for_new_request(State *s);
9981002
static void build_response_from_cache(State *s, HTTPWarningCode warning_code);
9991003
static void handle_cache_write_lock(State *s);
1004+
static void handle_deferred_cache_write_complete(State *s);
10001005
static void HandleResponse(State *s);
10011006
static void HandleUpdateCachedObject(State *s);
10021007
static void HandleUpdateCachedObjectContinue(State *s);

src/proxy/http/HttpConfig.cc

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1087,6 +1087,7 @@ HttpConfig::startup()
10871087
HttpEstablishStaticConfigByte(c.disallow_post_100_continue, "proxy.config.http.disallow_post_100_continue");
10881088

10891089
HttpEstablishStaticConfigByte(c.oride.cache_open_write_fail_action, "proxy.config.http.cache.open_write_fail_action");
1090+
HttpEstablishStaticConfigByte(c.oride.cache_defer_write_on_miss, "proxy.config.http.cache.defer_write_on_miss");
10901091

10911092
HttpEstablishStaticConfigByte(c.oride.cache_when_to_revalidate, "proxy.config.http.cache.when_to_revalidate");
10921093
HttpEstablishStaticConfigByte(c.oride.cache_required_headers, "proxy.config.http.cache.required_headers");
@@ -1391,6 +1392,7 @@ HttpConfig::reconfigure()
13911392
params->oride.max_cache_open_write_retries);
13921393
}
13931394
}
1395+
params->oride.cache_defer_write_on_miss = m_master.oride.cache_defer_write_on_miss;
13941396

13951397
params->oride.cache_when_to_revalidate = m_master.oride.cache_when_to_revalidate;
13961398
params->max_post_size = m_master.max_post_size;

src/proxy/http/HttpTransact.cc

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3231,6 +3231,62 @@ HttpTransact::handle_cache_write_lock(State *s)
32313231
}
32323232
}
32333233

3234+
///////////////////////////////////////////////////////////////////////////////
3235+
// Name : handle_deferred_cache_write_complete
3236+
// Description: Called after cache write opens for deferred cache write.
3237+
// We already have the server response headers, so we continue
3238+
// with cache write handling.
3239+
//
3240+
// Possible Next States From Here:
3241+
// - handle_cache_operation_on_forward_server_response (if cache write succeeded)
3242+
// - handle_no_cache_operation_on_forward_server_response (if cache write failed)
3243+
//
3244+
///////////////////////////////////////////////////////////////////////////////
3245+
void
3246+
HttpTransact::handle_deferred_cache_write_complete(State *s)
3247+
{
3248+
TxnDbg(dbg_ctl_http_trans, "handle_deferred_cache_write_complete");
3249+
TxnDbg(dbg_ctl_http_seq, "Entering handle_deferred_cache_write_complete");
3250+
3251+
ink_assert(s->cache_info.action == CacheAction_t::PREPARE_TO_WRITE);
3252+
3253+
switch (s->cache_info.write_lock_state) {
3254+
case CacheWriteLock_t::SUCCESS:
3255+
// Cache write lock acquired successfully
3256+
TxnDbg(dbg_ctl_http_trans, "[hdcwc] cache write lock acquired");
3257+
SET_UNPREPARE_CACHE_ACTION(s->cache_info);
3258+
// Now we have WRITE action, handle the response with caching
3259+
handle_cache_operation_on_forward_server_response(s);
3260+
return;
3261+
3262+
case CacheWriteLock_t::FAIL:
3263+
// Could not get cache write lock, continue without caching
3264+
TxnDbg(dbg_ctl_http_trans, "[hdcwc] cache write lock failed, continuing without cache");
3265+
Metrics::Counter::increment(http_rsb.cache_open_write_fail_count);
3266+
s->cache_info.action = CacheAction_t::NO_ACTION;
3267+
s->cache_info.write_status = CacheWriteStatus_t::LOCK_MISS;
3268+
break;
3269+
3270+
case CacheWriteLock_t::READ_RETRY:
3271+
// This shouldn't happen for deferred write since we don't have an object to read
3272+
TxnDbg(dbg_ctl_http_trans, "[hdcwc] unexpected READ_RETRY for deferred write");
3273+
s->cache_info.action = CacheAction_t::NO_ACTION;
3274+
s->cache_info.write_status = CacheWriteStatus_t::LOCK_MISS;
3275+
break;
3276+
3277+
case CacheWriteLock_t::INIT:
3278+
default:
3279+
ink_release_assert(0);
3280+
break;
3281+
}
3282+
3283+
// If we get here, cache write failed - continue without caching
3284+
// The original handle_no_cache_operation_on_forward_server_response will be called
3285+
// but we need to skip the deferred cache write check since we just tried it
3286+
// s->cache_open_write_deferred is already false from the first call
3287+
handle_no_cache_operation_on_forward_server_response(s);
3288+
}
3289+
32343290
///////////////////////////////////////////////////////////////////////////////
32353291
// Name : HandleCacheOpenReadMiss
32363292
// Description: cache looked up, miss or hit, but needs authorization
@@ -3276,6 +3332,13 @@ HttpTransact::HandleCacheOpenReadMiss(State *s)
32763332
s->cache_info.action = CacheAction_t::NO_ACTION;
32773333
} else if (s->api_server_response_no_store) { // plugin may have decided not to cache the response
32783334
s->cache_info.action = CacheAction_t::NO_ACTION;
3335+
} else if (s->txn_conf->cache_defer_write_on_miss) {
3336+
// Defer cache open write until after response headers are received.
3337+
// This avoids cache overhead for non-cacheable responses but may
3338+
// affect read-while-write and request coalescing for popular URLs.
3339+
s->cache_open_write_deferred = true;
3340+
s->cache_info.action = CacheAction_t::NO_ACTION;
3341+
TxnDbg(dbg_ctl_http_trans, "[HandleCacheOpenReadMiss] deferring cache open write until response");
32793342
} else {
32803343
HttpTransact::set_cache_prepare_write_action_for_new_request(s);
32813344
}
@@ -4677,6 +4740,24 @@ HttpTransact::handle_no_cache_operation_on_forward_server_response(State *s)
46774740
TxnDbg(dbg_ctl_http_trans, "(hncoofsr)");
46784741
TxnDbg(dbg_ctl_http_seq, "Entering handle_no_cache_operation_on_forward_server_response");
46794742

4743+
// Check if we deferred the cache open write and the response is cacheable.
4744+
// If so, we need to open the cache for write now.
4745+
if (s->cache_open_write_deferred && s->txn_conf->cache_http) {
4746+
bool cacheable = is_response_cacheable(s, &s->hdr_info.client_request, &s->hdr_info.server_response);
4747+
TxnDbg(dbg_ctl_http_trans, "[hncoofsr] deferred cache write, response %s cacheable", cacheable ? "is" : "is not");
4748+
4749+
if (cacheable) {
4750+
// Response is cacheable - open the cache for write now
4751+
s->cache_open_write_deferred = false;
4752+
s->cache_info.action = CacheAction_t::PREPARE_TO_WRITE;
4753+
s->cache_info.write_lock_state = CacheWriteLock_t::INIT;
4754+
// After cache write opens, continue with handle_cache_operation_on_forward_server_response
4755+
TRANSACT_RETURN(StateMachineAction_t::CACHE_ISSUE_WRITE, handle_deferred_cache_write_complete);
4756+
}
4757+
// Not cacheable - continue with no-cache operation
4758+
s->cache_open_write_deferred = false;
4759+
}
4760+
46804761
bool keep_alive = s->current.server->keep_alive == HTTPKeepAlive::KEEPALIVE;
46814762
const char *warn_text = nullptr;
46824763

0 commit comments

Comments
 (0)