Skip to content

Commit c29608c

Browse files
committed
Enhancement for cache_use_stale
Now in cache we don't lock cache entry, we just reference count it. Relying on that fact this patch introduce new approach for storing stale response, without building it each time when we have stale record in the cache. Simply find the record in the cache, save it to `TfwHttpReq` as `stale_ce` and keep the reference. If `stale_ce` was not used, just put the the reference, if it needed just build the response then put the reference and forward response to client.
1 parent 77a2d38 commit c29608c

File tree

5 files changed

+157
-84
lines changed

5 files changed

+157
-84
lines changed

fw/cache.c

Lines changed: 42 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -3018,6 +3018,38 @@ tfw_cache_build_resp(TfwHttpReq *req, TfwCacheEntry *ce, long age)
30183018
return NULL;
30193019
}
30203020

3021+
TfwHttpResp *
3022+
tfw_cache_build_resp_stale(TfwHttpReq *req)
3023+
{
3024+
TDB *db = node_db();
3025+
TfwCacheEntry *ce = req->stale_ce;
3026+
TfwHttpResp *resp = tfw_cache_build_resp(req, ce, req->stale_ce_age);
3027+
3028+
#if defined(DUBEG)
3029+
if (resp)
3030+
T_DBG("Cache: Stale response assigned to req [%p] w/ key=%lx, \
3031+
ce=%p", req, ce->trec.key, ce);
3032+
else
3033+
T_DBG("Cache: Cannot assigne stale response to req [%p] w/ \
3034+
key=%lx, ce=%p", req, ce->trec.key, ce);
3035+
#endif
3036+
3037+
tdb_rec_put(db, ce);
3038+
/* Set to NULL to prevent double free in req destructor. */
3039+
req->stale_ce = NULL;
3040+
3041+
return resp;
3042+
}
3043+
3044+
/**
3045+
* Release cache entry reference.
3046+
*/
3047+
void
3048+
tfw_cache_put_entry(void *ce)
3049+
{
3050+
tdb_rec_put(node_db(), ce);
3051+
}
3052+
30213053
static bool
30223054
tfw_cache_can_use_stale(TfwHttpReq *req, TfwCacheEntry *ce, long age)
30233055
{
@@ -3093,10 +3125,12 @@ cache_req_process_node(TfwHttpReq *req, tfw_http_cache_cb_t action)
30933125
TFW_INC_STAT_BH(cache.misses);
30943126
goto out;
30953127
}
3128+
req->stale_ce = ce;
3129+
req->stale_ce_age = age;
30963130
}
30973131

3098-
T_DBG("Cache: service request [%p] w/ key=%lx, ce=%p",
3099-
req, ce->trec.key, ce);
3132+
T_DBG("Cache: service request [%p] w/ key=%lx, ce=%p", req,
3133+
ce->trec.key, ce);
31003134

31013135
TFW_INC_STAT_BH(cache.hits);
31023136

@@ -3116,20 +3150,8 @@ cache_req_process_node(TfwHttpReq *req, tfw_http_cache_cb_t action)
31163150
}
31173151
}
31183152

3119-
resp = tfw_cache_build_resp(req, ce, age);
3120-
3121-
if (resp && stale) {
3122-
req->resp = NULL;
3123-
req->stale_resp = resp;
3124-
3125-
if (ce->flags & TFW_CE_STALE_IF_ERROR) {
3126-
resp->cache_ctl.stale_if_error = ce->stale_if_error;
3127-
resp->cache_ctl.flags |= TFW_HTTP_CC_STALE_IF_ERROR;
3128-
}
3129-
3130-
T_DBG("Cache: Stale response assigned to req [%p] w/ key=%lx, \
3131-
ce=%p", req, ce->trec.key, ce);
3132-
}
3153+
if (!stale)
3154+
resp = tfw_cache_build_resp(req, ce, age);
31333155

31343156
/*
31353157
* The stream of HTTP/2-request should be closed here since we have
@@ -3141,7 +3163,7 @@ cache_req_process_node(TfwHttpReq *req, tfw_http_cache_cb_t action)
31413163
* is stale, request will be forwarded to server, some forwardning
31423164
* functions requires alive stream. E.g: @tfw_http_req_evict_dropped().
31433165
*/
3144-
if (resp && TFW_MSG_H2(req) && !stale) {
3166+
if (resp && TFW_MSG_H2(req)) {
31453167
id = tfw_h2_req_stream_id(req);
31463168
if (unlikely(!id)) {
31473169
tfw_http_msg_free((TfwHttpMsg *)resp);
@@ -3151,7 +3173,7 @@ cache_req_process_node(TfwHttpReq *req, tfw_http_cache_cb_t action)
31513173
tfw_h2_req_unlink_stream(req);
31523174
}
31533175
out:
3154-
if (!resp && (req->cache_ctl.flags & TFW_HTTP_CC_OIFCACHED)) {
3176+
if (!stale && !resp && (req->cache_ctl.flags & TFW_HTTP_CC_OIFCACHED)) {
31553177
tfw_http_send_err_resp(req, 504, "resource not cached");
31563178
} else {
31573179
/*
@@ -3163,8 +3185,9 @@ cache_req_process_node(TfwHttpReq *req, tfw_http_cache_cb_t action)
31633185
set_bit(TFW_HTTP_B_REQ_HEAD_TO_GET, req->flags);
31643186
action((TfwHttpMsg *)req);
31653187
}
3188+
/* For stale we put entry after building response, during forwarding. */
3189+
if (ce && !stale)
31663190
put:
3167-
if (ce)
31683191
tdb_rec_put(db, ce);
31693192
}
31703193

fw/cache.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@
2525

2626
int tfw_cache_process(TfwHttpMsg *msg, tfw_http_cache_cb_t action);
2727
bool tfw_cache_is_enabled_or_not_configured(void);
28+
TfwHttpResp *tfw_cache_build_resp_stale(TfwHttpReq *req);
29+
void tfw_cache_put_entry(void *ce);
2830

2931
extern unsigned int cache_default_ttl;
3032

fw/http.c

Lines changed: 92 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -1483,13 +1483,80 @@ tfw_http_nip_req_resched_err(TfwSrvConn *srv_conn, TfwHttpReq *req,
14831483
" re-forwarded or re-scheduled");
14841484
}
14851485

1486-
static inline void
1487-
tfw_http_send_err_resp_nolog(TfwHttpReq *req, int status)
1486+
/**
1487+
* The request is serviced from cache.
1488+
* Send the response as is and unrefer its data.
1489+
*/
1490+
static void
1491+
tfw_http_req_cache_service(TfwHttpResp *resp)
14881492
{
1493+
TfwHttpReq *req = resp->req;
1494+
1495+
WARN_ON_ONCE(!list_empty(&req->fwd_list));
1496+
WARN_ON_ONCE(!list_empty(&req->nip_list));
1497+
14891498
if (TFW_MSG_H2(req))
1490-
tfw_h2_send_err_resp(req, status, false);
1499+
tfw_h2_resp_fwd(resp);
14911500
else
1492-
tfw_h1_send_err_resp(req, status);
1501+
tfw_http_resp_fwd(resp);
1502+
1503+
TFW_INC_STAT_BH(clnt.msgs_fromcache);
1504+
}
1505+
1506+
/**
1507+
* Build stale response from the @req->stale_ce and link request with response.
1508+
* Forward response to client, free previous unsuccessful response from upstream
1509+
* @hmresp.
1510+
*
1511+
* @return true if response successfully forwarded otherwise false.
1512+
*/
1513+
static bool
1514+
__tfw_http_resp_fwd_stale(TfwHttpMsg *hmresp)
1515+
{
1516+
TfwHttpReq *req = hmresp->req;
1517+
TfwHttpResp *stale_resp;
1518+
bool sent = false;
1519+
1520+
tfw_stream_unlink_msg(hmresp->stream);
1521+
/* Unlink response. */
1522+
req->resp = NULL;
1523+
1524+
stale_resp = tfw_cache_build_resp_stale(req);
1525+
/* For HTTP2 response will not be built if stream already closed. */
1526+
if (!stale_resp)
1527+
goto free;
1528+
1529+
req->resp->conn = hmresp->conn;
1530+
hmresp->pair = NULL;
1531+
1532+
if (TFW_MSG_H2(req))
1533+
tfw_h2_req_unlink_stream(req);
1534+
1535+
tfw_http_req_cache_service(req->resp);
1536+
sent = true;
1537+
1538+
free:
1539+
tfw_http_msg_free(hmresp);
1540+
1541+
return sent;
1542+
}
1543+
1544+
/**
1545+
* The same as @__tfw_http_resp_fwd_stale(), but used in case when we don't
1546+
* have response from upstream.
1547+
*/
1548+
static bool
1549+
__tfw_http_resp_fwd_stale_noresp(TfwHttpReq *req)
1550+
{
1551+
if (!tfw_cache_build_resp_stale(req))
1552+
return false;
1553+
1554+
if (TFW_MSG_H2(req))
1555+
tfw_h2_req_unlink_stream(req);
1556+
1557+
tfw_http_req_cache_service(req->resp);
1558+
1559+
return true;
14931560
}
14941561

14951562
static bool
@@ -1511,9 +1578,8 @@ static bool
15111578
tfw_http_resp_should_fwd_stale(TfwHttpReq *req, unsigned short status)
15121579
{
15131580
TfwCacheUseStale *stale_opt;
1514-
TfwHttpResp *resp = req->stale_resp;
15151581

1516-
if (!resp)
1582+
if (!req->stale_ce)
15171583
return false;
15181584

15191585
stale_opt = tfw_vhost_get_cache_use_stale(req->location, req->vhost);
@@ -1536,29 +1602,21 @@ tfw_http_resp_should_fwd_stale(TfwHttpReq *req, unsigned short status)
15361602
* from cache. Therefore we response with inaccurate age or even
15371603
* with violation of max-stale param.
15381604
*/
1539-
return (resp->cache_ctl.flags & TFW_HTTP_CC_STALE_IF_ERROR ||
1540-
req->cache_ctl.flags & TFW_HTTP_CC_STALE_IF_ERROR) &&
1541-
tfw_http_use_stale_if_error(status);
1605+
return tfw_http_use_stale_if_error(status);
15421606
}
15431607

1544-
/**
1545-
* The request is serviced from cache.
1546-
* Send the response as is and unrefer its data.
1547-
*/
1548-
static void
1549-
tfw_http_req_cache_service(TfwHttpResp *resp)
1608+
static inline void
1609+
tfw_http_send_err_resp_nolog(TfwHttpReq *req, int status)
15501610
{
1551-
TfwHttpReq *req = resp->req;
1552-
1553-
WARN_ON_ONCE(!list_empty(&req->fwd_list));
1554-
WARN_ON_ONCE(!list_empty(&req->nip_list));
1555-
1556-
if (TFW_MSG_H2(req))
1557-
tfw_h2_resp_fwd(resp);
1558-
else
1559-
tfw_http_resp_fwd(resp);
1560-
1561-
TFW_INC_STAT_BH(clnt.msgs_fromcache);
1611+
/* Response must be freed before calling tfw_http_send_err_resp_nolog(). */
1612+
if (tfw_http_resp_should_fwd_stale(req, status)) {
1613+
__tfw_http_resp_fwd_stale_noresp(req);
1614+
} else {
1615+
if (TFW_MSG_H2(req))
1616+
tfw_h2_send_err_resp(req, status, false);
1617+
else
1618+
tfw_h1_send_err_resp(req, status);
1619+
}
15621620
}
15631621

15641622
/* Common interface for sending error responses. */
@@ -1569,16 +1627,7 @@ tfw_http_send_err_resp(TfwHttpReq *req, int status, const char *reason)
15691627
T_WARN_ADDR_STATUS(reason, &req->conn->peer->addr,
15701628
TFW_NO_PORT, status);
15711629

1572-
/* Response must be freed before calling tfw_http_send_err_resp(). */
1573-
if (tfw_http_resp_should_fwd_stale(req, status)) {
1574-
req->resp = req->stale_resp;
1575-
req->stale_resp = NULL;
1576-
if (TFW_MSG_H2(req))
1577-
tfw_h2_req_unlink_stream(req);
1578-
tfw_http_req_cache_service(req->resp);
1579-
} else {
1580-
tfw_http_send_err_resp_nolog(req, status);
1581-
}
1630+
tfw_http_send_err_resp_nolog(req, status);
15821631
}
15831632

15841633
static void
@@ -2647,8 +2696,8 @@ tfw_http_req_destruct(void *msg)
26472696
if (req->old_head)
26482697
ss_skb_queue_purge(&req->old_head);
26492698

2650-
if (req->stale_resp)
2651-
tfw_http_msg_free((TfwHttpMsg *)req->stale_resp);
2699+
if (req->stale_ce)
2700+
tfw_cache_put_entry(req->stale_ce);
26522701
}
26532702

26542703
/**
@@ -6658,25 +6707,6 @@ tfw_http_resp_terminate(TfwHttpMsg *hm)
66586707
tfw_http_resp_cache(hm);
66596708
}
66606709

6661-
static void
6662-
__tfw_http_resp_fwd_stale(TfwHttpMsg *hmresp)
6663-
{
6664-
TfwHttpReq *req = hmresp->req;
6665-
6666-
tfw_stream_unlink_msg(hmresp->stream);
6667-
req->resp = req->stale_resp;
6668-
req->stale_resp = NULL;
6669-
req->resp->conn = hmresp->conn;
6670-
hmresp->pair = NULL;
6671-
6672-
if (TFW_MSG_H2(req))
6673-
tfw_h2_req_unlink_stream(req);
6674-
6675-
tfw_http_req_cache_service(req->resp);
6676-
6677-
tfw_http_msg_free(hmresp);
6678-
}
6679-
66806710
static int
66816711
tfw_http_resp_fwd_stale(TfwHttpMsg *hmresp)
66826712
{
@@ -6711,7 +6741,8 @@ tfw_http_resp_fwd_stale(TfwHttpMsg *hmresp)
67116741
return T_BLOCK;
67126742
}
67136743

6714-
__tfw_http_resp_fwd_stale(hmresp);
6744+
if (!__tfw_http_resp_fwd_stale(hmresp))
6745+
return T_BAD;
67156746

67166747
return T_OK;
67176748
}
@@ -6949,6 +6980,7 @@ tfw_http_resp_process(TfwConn *conn, TfwStream *stream, struct sk_buff *skb,
69496980
if (unlikely(r != T_OK)) {
69506981
if (hmsib)
69516982
tfw_http_conn_msg_free(hmsib);
6983+
return r;
69526984
}
69536985

69546986
*split = NULL;
@@ -6998,7 +7030,7 @@ tfw_http_resp_process(TfwConn *conn, TfwStream *stream, struct sk_buff *skb,
69987030
__tfw_http_resp_fwd_stale(hmresp);
69997031
/*
70007032
* Close connection with backend immediately
7001-
* and try to reastablish it later.
7033+
* and try to re-establish it later.
70027034
*/
70037035
r = T_BAD;
70047036
} else {
@@ -7013,7 +7045,7 @@ tfw_http_resp_process(TfwConn *conn, TfwStream *stream, struct sk_buff *skb,
70137045
HTTP2_ECODE_PROTO);
70147046
/*
70157047
* Close connection with backend immediately
7016-
* and try to reastablish it later.
7048+
* and try to re-establish it later.
70177049
*/
70187050
r = T_BAD;
70197051
}

fw/http.h

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -347,8 +347,8 @@ typedef struct {
347347
* the same;
348348
* @old_head - Original request head. Required for keep request data until
349349
* the response is sent to the client;
350-
* @stale_resp - Stale response retrieved from the cache. Must be assigned only
351-
* when "cache_use_stale" is configured;
350+
* @stale_ce - Stale cache entry retrieved from the cache. Must be assigned
351+
* only when "cache_use_stale" is configured;
352352
* @pit - iterator for tracking transformed data allocation (applicable
353353
* for HTTP/2 mode only);
354354
* @userinfo - userinfo in URI, not mandatory;
@@ -361,7 +361,9 @@ typedef struct {
361361
* @nip_list - member in the queue of non-idempotent requests;
362362
* @jtxtstamp - time the request is forwarded to a server, in jiffies;
363363
* @jrxtstamp - time the request is received from a client, in jiffies;
364-
* @tm_header - time HTTP header started coming;
364+
* @tm_header - time HTTP header started coming. Only rx path;
365+
* @stale_ce_age - calculated age of stale response. Must be assigned only when
366+
* "cache_use_stale" is configured on tx path with cache;
365367
* @tm_bchunk - time previous chunk of HTTP body had come at;
366368
* @hash - hash value for caching calculated for the request;
367369
* @frang_st - current state of FRANG classifier;
@@ -382,7 +384,7 @@ struct tfw_http_req_t {
382384
TfwHttpSess *sess;
383385
TfwClient *peer;
384386
struct sk_buff *old_head;
385-
TfwHttpResp *stale_resp;
387+
void *stale_ce;
386388
TfwHttpCond cond;
387389
TfwMsgParseIter pit;
388390
TfwStr userinfo;
@@ -394,7 +396,10 @@ struct tfw_http_req_t {
394396
struct list_head nip_list;
395397
unsigned long jtxtstamp;
396398
unsigned long jrxtstamp;
397-
unsigned long tm_header;
399+
union {
400+
unsigned long tm_header;
401+
long stale_ce_age;
402+
};
398403
unsigned long tm_bchunk;
399404
unsigned long hash;
400405
unsigned int frang_st;

0 commit comments

Comments
 (0)