MINOR: cache: Add option to avoid removing expired entries in lookup function

Any lookup in the cache tree done through entry_exist or
secondary_entry_exist functions could end up deleting the corresponding
entry if it is expired which prevents from using a rdlock on code paths
that would just perform a lookup on the tree (in
http_action_req_cache_use for instance).
Adding a 'delete_expired' boolean as a parameter allows for "pure"
lookups and thus it will allow to perform operations on the tree that
simply require a rdlock instead of a "heavier" wrlock.
This commit is contained in:
Remi Tricot-Le Breton 2023-11-16 17:38:17 +01:00 committed by William Lallemand
parent ff3cb6dad4
commit 0dfb57bbf9

View File

@ -218,7 +218,14 @@ DECLARE_STATIC_POOL(pool_head_cache_st, "cache_st", sizeof(struct cache_st));
static struct eb32_node *insert_entry(struct cache *cache, struct cache_entry *new_entry); static struct eb32_node *insert_entry(struct cache *cache, struct cache_entry *new_entry);
static void delete_entry(struct cache_entry *del_entry); static void delete_entry(struct cache_entry *del_entry);
struct cache_entry *entry_exist(struct cache *cache, char *hash) /*
* Find a cache_entry in the <cache>'s tree that has the hash <hash>.
* If <delete_expired> is 0 then the entry is left untouched if it is found but
* is already expired, and NULL is returned. Otherwise, the expired entry is
* removed from the tree and NULL is returned.
* Returns a valid (not expired) cache_tree pointer.
*/
struct cache_entry *entry_exist(struct cache *cache, char *hash, int delete_expired)
{ {
struct eb32_node *node; struct eb32_node *node;
struct cache_entry *entry; struct cache_entry *entry;
@ -235,7 +242,7 @@ struct cache_entry *entry_exist(struct cache *cache, char *hash)
if (entry->expire > date.tv_sec) { if (entry->expire > date.tv_sec) {
return entry; return entry;
} else { } else if (delete_expired) {
delete_entry(entry); delete_entry(entry);
entry->eb.key = 0; entry->eb.key = 0;
} }
@ -277,10 +284,13 @@ static int secondary_key_cmp(const char *ref_key, const char *new_key)
* order to get the proper one out of the list, we use a secondary_key. * order to get the proper one out of the list, we use a secondary_key.
* This function simply iterates over all the entries with the same primary_key * This function simply iterates over all the entries with the same primary_key
* until it finds the right one. * until it finds the right one.
* If <delete_expired> is 0 then the entry is left untouched if it is found but
* is already expired, and NULL is returned. Otherwise, the expired entry is
* removed from the tree and NULL is returned.
* Returns the cache_entry in case of success, NULL otherwise. * Returns the cache_entry in case of success, NULL otherwise.
*/ */
struct cache_entry *secondary_entry_exist(struct cache *cache, struct cache_entry *entry, struct cache_entry *secondary_entry_exist(struct cache *cache, struct cache_entry *entry,
const char *secondary_key) const char *secondary_key, int delete_expired)
{ {
struct eb32_node *node = &entry->eb; struct eb32_node *node = &entry->eb;
@ -294,7 +304,7 @@ struct cache_entry *secondary_entry_exist(struct cache *cache, struct cache_entr
* when we find them. Calling delete_entry would be too costly * when we find them. Calling delete_entry would be too costly
* so we simply call eb32_delete. The secondary_entry count will * so we simply call eb32_delete. The secondary_entry count will
* be updated when we try to insert a new entry to this list. */ * be updated when we try to insert a new entry to this list. */
if (entry->expire <= date.tv_sec) { if (entry->expire <= date.tv_sec && delete_expired) {
eb32_delete(&entry->eb); eb32_delete(&entry->eb);
entry->eb.key = 0; entry->eb.key = 0;
} }
@ -304,8 +314,10 @@ struct cache_entry *secondary_entry_exist(struct cache *cache, struct cache_entr
/* Expired entry */ /* Expired entry */
if (entry && entry->expire <= date.tv_sec) { if (entry && entry->expire <= date.tv_sec) {
eb32_delete(&entry->eb); if (delete_expired) {
entry->eb.key = 0; eb32_delete(&entry->eb);
entry->eb.key = 0;
}
entry = NULL; entry = NULL;
} }
@ -1076,7 +1088,7 @@ enum act_return http_action_store_cache(struct act_rule *rule, struct proxy *px,
* unsafe request (such as PUT, POST or DELETE). */ * unsafe request (such as PUT, POST or DELETE). */
cache_wrlock(cache); cache_wrlock(cache);
old = entry_exist(cache, txn->cache_hash); old = entry_exist(cache, txn->cache_hash, 1);
if (old) { if (old) {
eb32_delete(&old->eb); eb32_delete(&old->eb);
old->eb.key = 0; old->eb.key = 0;
@ -1140,11 +1152,11 @@ enum act_return http_action_store_cache(struct act_rule *rule, struct proxy *px,
goto out; goto out;
cache_wrlock(cache); cache_wrlock(cache);
old = entry_exist(cache, txn->cache_hash); old = entry_exist(cache, txn->cache_hash, 1);
if (old) { if (old) {
if (vary_signature) if (vary_signature)
old = secondary_entry_exist(cconf->c.cache, old, old = secondary_entry_exist(cconf->c.cache, old,
txn->cache_secondary_hash); txn->cache_secondary_hash, 1);
if (old) { if (old) {
if (!old->complete) { if (!old->complete) {
/* An entry with the same primary key is already being /* An entry with the same primary key is already being
@ -1840,7 +1852,7 @@ enum act_return http_action_req_cache_use(struct act_rule *rule, struct proxy *p
shctx_lock(shctx_ptr(cache)); shctx_lock(shctx_ptr(cache));
cache_wrlock(cache); cache_wrlock(cache);
res = entry_exist(cache, s->txn->cache_hash); res = entry_exist(cache, s->txn->cache_hash, 0);
/* We must not use an entry that is not complete but the check will be /* We must not use an entry that is not complete but the check will be
* performed after we look for a potential secondary entry (in case of * performed after we look for a potential secondary entry (in case of
* Vary). */ * Vary). */
@ -1859,7 +1871,7 @@ enum act_return http_action_req_cache_use(struct act_rule *rule, struct proxy *p
shctx_lock(shctx_ptr(cache)); shctx_lock(shctx_ptr(cache));
cache_wrlock(cache); cache_wrlock(cache);
sec_entry = secondary_entry_exist(cache, res, sec_entry = secondary_entry_exist(cache, res,
s->txn->cache_secondary_hash); s->txn->cache_secondary_hash, 0);
if (sec_entry && sec_entry != res) { if (sec_entry && sec_entry != res) {
/* The wrong row was added to the hot list. */ /* The wrong row was added to the hot list. */
shctx_row_dec_hot(shctx_ptr(cache), entry_block); shctx_row_dec_hot(shctx_ptr(cache), entry_block);