MEDIUM: cache: Add a secondary entry counter and insertion limitation

Add an arbitrary maximum number of secondary entries per primary hash
(10 for now) to the cache. This prevents the cache from being filled
with duplicates of the same resource.
This works thanks to an entry counter that is kept in one of the
duplicates of the list (the last one).
When an entry is added to the list, the ebtree's implementation ensures
that it will be added to the end of the existing list so the only thing
to do to keep the counter updated is to get the previous counter from
the second to last entry.
Likewise, when an entry is explicitely deleted, we update the counter
from the list's last item.
diff --git a/src/cache.c b/src/cache.c
index 9ed7b97..bba757f 100644
--- a/src/cache.c
+++ b/src/cache.c
@@ -104,6 +104,8 @@
 	struct shared_block *first_block;
 };
 
+#define SECONDARY_ENTRY_MAX_COUNT 10
+
 struct cache_entry {
 	unsigned int complete;    /* An entry won't be valid until complete is not null. */
 	unsigned int latest_validation;     /* latest validation date */
@@ -116,6 +118,7 @@
 	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
 					        * to build secondary keys for this cache entry. */
+	unsigned int secondary_entries_count;  /* Should only be filled in the last entry of a list of dup entries */
 
 	unsigned int etag_length; /* Length of the ETag value (if one was found in the response). */
 	unsigned int etag_offset; /* Offset of the ETag value in the data buffer. */
@@ -139,6 +142,9 @@
 
 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 void delete_entry(struct cache_entry *del_entry);
+
 struct cache_entry *entry_exist(struct cache *cache, char *hash)
 {
 	struct eb32_node *node;
@@ -157,7 +163,7 @@
 	if (entry->expire > now.tv_sec) {
 		return entry;
 	} else {
-		eb32_delete(node);
+		delete_entry(entry);
 		entry->eb.key = 0;
 	}
 	return NULL;
@@ -181,6 +187,16 @@
 
 	while (entry && memcmp(entry->secondary_key, secondary_key, HTTP_CACHE_SEC_KEY_LEN) != 0) {
 		node = eb32_next_dup(node);
+
+		/* Make the best use of this iteration and clear expired entries
+		 * when we find them. Calling delete_entry would be too costly
+		 * so we simply call eb32_delete. The secondary_entry count will
+		 * be updated when we try to insert a new entry to this list. */
+		if (entry->expire <= now.tv_sec) {
+			eb32_delete(&entry->eb);
+			entry->eb.key = 0;
+		}
+
 		entry = node ? eb32_entry(node, struct cache_entry, eb) : NULL;
 	}
 
@@ -194,6 +210,92 @@
 	return entry;
 }
 
+
+
+/*
+ * This function inserts a cache_entry in the cache's ebtree. In case of
+ * duplicate entries (vary), it then checks that the number of entries did not
+ * reach the max number of secondary entries. If this entry should not have been
+ * created, remove it.
+ * In the regular case (unique entries), this function does not do more than a
+ * simple insert. In case of secondary entries, it will at most cost an
+ * insertion+max_sec_entries time checks and entry deletion.
+ * Returns the newly inserted node in case of success, NULL otherwise.
+ */
+static struct eb32_node *insert_entry(struct cache *cache, struct cache_entry *new_entry)
+{
+	struct eb32_node *prev = NULL;
+	struct cache_entry *entry = NULL;
+	unsigned int entry_count = 0;
+
+	struct eb32_node *node = eb32_insert(&cache->entries, &new_entry->eb);
+
+	/* We should not have multiple entries with the same primary key unless
+	 * the entry has a non null vary signature. */
+	if (!new_entry->secondary_key_signature)
+		return node;
+
+	prev = eb32_prev_dup(node);
+	if (prev != NULL) {
+		/* The last entry of a duplicate list should contain the current
+		 * number of entries in the list. */
+		entry = container_of(prev, struct cache_entry, eb);
+		entry_count = entry->secondary_entries_count;
+
+		if (entry_count >= SECONDARY_ENTRY_MAX_COUNT) {
+			/* Too many entries for this primary key, delete
+			 * the newly inserted one. */
+			entry = container_of(prev, struct cache_entry, eb);
+			eb32_delete(node);
+			node->key = 0;
+			return NULL;
+		}
+	}
+
+	new_entry->secondary_entries_count = entry_count + 1;
+
+	return node;
+}
+
+
+/*
+ * This function removes an entry from the ebtree. If the entry was a duplicate
+ * (in case of Vary), it updates the secondary entry counter in another
+ * duplicate entry (the last entry of the dup list).
+ */
+static void delete_entry(struct cache_entry *del_entry)
+{
+	struct eb32_node *prev = NULL, *next = NULL;
+	struct cache_entry *entry = NULL;
+	struct eb32_node *last = NULL;
+
+	if (del_entry->secondary_key_signature) {
+		next = &del_entry->eb;
+
+		/* Look for last entry of the duplicates list. */
+		while ((next = eb32_next_dup(next))) {
+			last = next;
+		}
+
+		if (last) {
+			entry = container_of(last, struct cache_entry, eb);
+			--entry->secondary_entries_count;
+		}
+		else {
+			/* The current entry is the last one, look for the
+			 * previous one to update its counter. */
+			prev = eb32_prev_dup(&del_entry->eb);
+			if (prev) {
+				entry = container_of(prev, struct cache_entry, eb);
+				entry->secondary_entries_count = del_entry->secondary_entries_count - 1;
+			}
+		}
+	}
+	eb32_delete(&del_entry->eb);
+	del_entry->eb.key = 0;
+}
+
+
 static inline struct shared_context *shctx_ptr(struct cache *cache)
 {
 	return (struct shared_context *)((unsigned char *)cache - ((struct shared_context *)NULL)->data);
@@ -644,7 +746,7 @@
 	struct cache_entry *object = (struct cache_entry *)block->data;
 
 	if (first == block && object->eb.key)
-		eb32_delete(&object->eb);
+		delete_entry(object);
 	object->eb.key = 0;
 }
 
@@ -844,7 +946,7 @@
 				shctx_unlock(shctx);
 				goto out;
 			}
-			eb32_delete(&old->eb);
+			delete_entry(old);
 			old->eb.key = 0;
 		}
 	}
@@ -871,7 +973,7 @@
 		memcpy(object->secondary_key, txn->cache_secondary_hash, HTTP_CACHE_SEC_KEY_LEN);
 
 	/* Insert the entry in the tree even if the payload is not cached yet. */
-	if (eb32_insert(&cache->entries, &object->eb) != &object->eb) {
+	if (insert_entry(cache, object) != &object->eb) {
 		object->eb.key = 0;
 		shctx_unlock(shctx);
 		goto out;
@@ -956,7 +1058,6 @@
 	/* register the buffer in the filter ctx for filling it with data*/
 	if (cache_ctx) {
 		cache_ctx->first_block = first;
-
 		/* store latest value and expiration time */
 		object->latest_validation = now.tv_sec;
 		object->expire = now.tv_sec + effective_maxage;
@@ -969,7 +1070,7 @@
 		shctx_lock(shctx);
 		first->len = 0;
 		if (object->eb.key)
-			eb32_delete(&object->eb);
+			delete_entry(object);
 		object->eb.key = 0;
 		shctx_row_dec_hot(shctx, first);
 		shctx_unlock(shctx);