/* Flags for cached entries. */
#define CACHE_EF_COMPLETE 0x00000001 /* fully written and valid */
+#define CACHE_EF_STRIPPED 0x00000002 /* entry stripped, only early hints data remains */
/* Flags for configuration. */
#define CACHE_CF_VARY_PROCESSING 0x00000001 /* manage Vary header (disabled by default) */
struct cache {
struct cache_tree trees[CACHE_TREE_NUM];
struct list list; /* cache linked list */
+ struct list full_lru; /* LRU list of full entries */
+ struct list hints_lru; /* LRU list of hints entries */
+ unsigned int hints_blocks; /* sum of block counts of hints entries */
unsigned int maxage; /* max-age */
unsigned int maxblocks;
unsigned int maxobjsz; /* max-object-size (in bytes) */
char hash[20];
struct list cleanup_list;/* List used between the cache_free_blocks and cache_reserve_finish calls */
+ struct list lru; /* Link in the cache's full_lru or hints_lru */
char secondary_key[HTTP_CACHE_SEC_KEY_LEN]; /* Optional secondary key. */
unsigned int secondary_key_signature; /* Bitfield of the HTTP headers that should be used
return (struct shared_block *)((unsigned char *)entry - offsetof(struct shared_block, data));
}
+/*
+ * Reattach a row that the cache had detached for reading or writing, and
+ * ensure the corresponding entry is at the tail of its appropriate LRU. If
+ * the entry is not yet in any LRU and is complete, it is inserted; otherwise
+ * it is moved to the tail. LRU bookkeeping is skipped when the cache has
+ * early-hints disabled. Must be called under shctx wrlock.
+ */
+static inline void cache_row_reattach(struct cache *cache, struct shared_block *first)
+{
+ struct cache_entry *entry = (struct cache_entry *)first->data;
+
+ if ((cache->flags & CACHE_CF_EARLY_HINTS) &&
+ (LIST_INLIST(&entry->lru) || (entry->flags & CACHE_EF_COMPLETE))) {
+ struct list *lru;
+
+ if (entry->flags & CACHE_EF_STRIPPED)
+ lru = &cache->hints_lru;
+ else
+ lru = &cache->full_lru;
+ if (LIST_INLIST(&entry->lru))
+ LIST_DELETE(&entry->lru);
+ LIST_APPEND(lru, &entry->lru);
+ }
+ shctx_row_reattach(shctx_ptr(cache), first);
+}
static int
cache_store_init(struct proxy *px, struct flt_conf *fconf)
release_entry_unlocked(&cache->trees[object->eb.key % CACHE_TREE_NUM], object);
}
shctx_wrlock(shctx);
- shctx_row_reattach(shctx, st->first_block);
+ cache_row_reattach(cache, st->first_block);
shctx_wrunlock(shctx);
}
if (st) {
filter->ctx = NULL; /* disable cache */
release_entry_unlocked(&cache->trees[object->eb.key % CACHE_TREE_NUM], object);
shctx_wrlock(shctx);
- shctx_row_reattach(shctx, st->first_block);
+ cache_row_reattach(cache, st->first_block);
shctx_wrunlock(shctx);
pool_free(pool_head_cache_st, st);
}
/* The whole payload was cached, the entry can now be used. */
object->flags |= CACHE_EF_COMPLETE;
/* remove from the hotlist */
- shctx_row_reattach(shctx, st->first_block);
+ cache_row_reattach(cache, st->first_block);
shctx_wrunlock(shctx);
}
struct cache *cache = (struct cache *)data;
struct cache_tree *cache_tree;
+ if (LIST_INLIST(&object->lru)) {
+ LIST_DEL_INIT(&object->lru);
+ if (object->flags & CACHE_EF_STRIPPED)
+ cache->hints_blocks -= first->block_count;
+ }
+
if (object->eb.key) {
- object->flags &= ~CACHE_EF_COMPLETE;
+ object->flags &= ~(CACHE_EF_COMPLETE | CACHE_EF_STRIPPED);
cache_tree = &cache->trees[object->eb.key % CACHE_TREE_NUM];
retain_entry(object);
HA_SPIN_LOCK(CACHE_LOCK, &cache_tree->cleanup_lock);
*/
object = (struct cache_entry *)first->data;
memset(object, 0, sizeof(*object));
+ LIST_INIT(&object->lru);
object->eb.key = key;
object->secondary_key_signature = vary_signature;
/* We need to temporarily set a valid expiring time until the actual one
release_entry_unlocked(cache_tree, object);
}
shctx_wrlock(shctx);
- shctx_row_reattach(shctx, first);
+ cache_row_reattach(cache, first);
shctx_wrunlock(shctx);
}
release_entry(ctx->cache_tree, cache_ptr, 1);
shctx_wrlock(shctx);
- shctx_row_reattach(shctx, first);
+ cache_row_reattach(ctx->cache, first);
shctx_wrunlock(shctx);
}
entry_block = block_ptr(res);
shctx_wrlock(shctx);
- if (res->flags & CACHE_EF_COMPLETE) {
+ if ((res->flags & CACHE_EF_COMPLETE) && !(res->flags & CACHE_EF_STRIPPED)) {
shctx_row_detach(shctx, entry_block);
detached = 1;
} else {
release_entry(cache_tree, res, 0);
shctx_wrlock(shctx);
if (detached)
- shctx_row_reattach(shctx, entry_block);
+ cache_row_reattach(cache, entry_block);
shctx_wrunlock(shctx);
}
else if (sec_entry != res) {
retain_entry(sec_entry);
shctx_wrlock(shctx);
if (detached)
- shctx_row_reattach(shctx, entry_block);
+ cache_row_reattach(cache, entry_block);
entry_block = block_ptr(sec_entry);
shctx_row_detach(shctx, entry_block);
shctx_wrunlock(shctx);
res = NULL;
shctx_wrlock(shctx);
- shctx_row_reattach(shctx, entry_block);
+ cache_row_reattach(cache, entry_block);
shctx_wrunlock(shctx);
}
}
* the server. */
if (!res) {
return ACT_RET_CONT;
- } else if (!(res->flags & CACHE_EF_COMPLETE)) {
+ } else if (!(res->flags & CACHE_EF_COMPLETE) || (res->flags & CACHE_EF_STRIPPED)) {
release_entry(cache_tree, res, 1);
shctx_wrlock(shctx);
- shctx_row_reattach(shctx, entry_block);
+ cache_row_reattach(cache, entry_block);
shctx_wrunlock(shctx);
return ACT_RET_CONT;
}
s->target = NULL;
release_entry(cache_tree, res, 1);
shctx_wrlock(shctx);
- shctx_row_reattach(shctx, entry_block);
+ cache_row_reattach(cache, entry_block);
shctx_wrunlock(shctx);
return ACT_RET_CONT;
}
LIST_APPEND(&caches, &cache->list);
LIST_DELETE(&cache_config->list);
free(cache_config);
+ LIST_INIT(&cache->full_lru);
+ LIST_INIT(&cache->hints_lru);
+ cache->hints_blocks = 0;
for (i = 0; i < CACHE_TREE_NUM; ++i) {
cache->trees[i].entries = EB_ROOT;
HA_RWLOCK_INIT(&cache->trees[i].lock);