changeset 22442:a6f1f04b70e4

lib-index: Add mail_cache_close_mail() to smartly drop cached data with INDEX=MEMORY Instead of reseting the entire transaction buffer when 256 kB is reached, just drop mails have have been marked closed with mail_cache_close_mail(). If that's not enough, continue deleting forcibly until the buffer is below 256 kB. This is especially important when mail_prefetch_count>0 and INDEX=MEMORY. In that case there can be multiple mails that are being added to cache and used later on. If they were dropped from cache too early, the work would have to be done all over again.
author Timo Sirainen <timo.sirainen@dovecot.fi>
date Fri, 18 Aug 2017 17:34:14 +0300
parents 31ab1147d15d
children 6ad4c4203dba
files src/lib-index/mail-cache-transaction.c src/lib-index/mail-cache.h
diffstat 2 files changed, 54 insertions(+), 1 deletions(-) [+]
line wrap: on
line diff
--- a/src/lib-index/mail-cache-transaction.c	Wed Aug 16 12:44:02 2017 +0300
+++ b/src/lib-index/mail-cache-transaction.c	Fri Aug 18 17:34:14 2017 +0300
@@ -39,6 +39,7 @@
 
 	buffer_t *cache_data;
 	ARRAY(struct mail_cache_transaction_rec) cache_data_seq;
+	ARRAY_TYPE(seq_range) cache_data_wanted_seqs;
 	uint32_t prev_seq, min_seq;
 	size_t last_rec_pos;
 
@@ -159,6 +160,8 @@
 		buffer_free(&ctx->cache_data);
 	if (array_is_created(&ctx->cache_data_seq))
 		array_free(&ctx->cache_data_seq);
+	if (array_is_created(&ctx->cache_data_wanted_seqs))
+		array_free(&ctx->cache_data_wanted_seqs);
 	i_free(ctx);
 }
 
@@ -463,9 +466,40 @@
 	ctx->min_seq = 0;
 
 	array_clear(&ctx->cache_data_seq);
+	array_clear(&ctx->cache_data_wanted_seqs);
 	return ret;
 }
 
+static void
+mail_cache_transaction_drop_unwanted(struct mail_cache_transaction_ctx *ctx,
+				     size_t space_needed)
+{
+	struct mail_cache_transaction_rec *recs;
+	unsigned int i, count;
+
+	recs = array_get_modifiable(&ctx->cache_data_seq, &count);
+	/* find out how many records to delete. delete all unwanted sequences,
+	   and if that's not enough delete some more. */
+	for (i = 0; i < count; i++) {
+		if (seq_range_exists(&ctx->cache_data_wanted_seqs, recs[i].seq)) {
+			if (recs[i].cache_data_pos >= space_needed)
+				break;
+			/* we're going to forcibly delete it - remove it also
+			   from the array since it's no longer useful there */
+			seq_range_array_remove(&ctx->cache_data_wanted_seqs,
+					       recs[i].seq);
+		}
+	}
+	unsigned int deleted_count = i;
+	size_t deleted_space = i < count ?
+		recs[i].cache_data_pos : ctx->last_rec_pos;
+	for (; i < count; i++)
+		recs[i].cache_data_pos -= deleted_space;
+	ctx->last_rec_pos -= deleted_space;
+	array_delete(&ctx->cache_data_seq, 0, deleted_count);
+	buffer_delete(ctx->cache_data, 0, deleted_space);
+}
+
 static size_t
 mail_cache_transaction_update_last_rec_size(struct mail_cache_transaction_ctx *ctx)
 {
@@ -513,6 +547,7 @@
 			buffer_create_dynamic(default_pool,
 					      MAIL_CACHE_INIT_WRITE_BUFFER);
 		i_array_init(&ctx->cache_data_seq, 64);
+		i_array_init(&ctx->cache_data_wanted_seqs, 32);
 	}
 
 	i_zero(&new_rec);
@@ -727,6 +762,7 @@
 	if (ctx->prev_seq != seq) {
 		mail_cache_transaction_switch_seq(ctx);
 		ctx->prev_seq = seq;
+		seq_range_array_add(&ctx->cache_data_wanted_seqs, seq);
 
 		/* remember roughly what we have modified, so cache lookups can
 		   look into transactions to see changes. */
@@ -750,7 +786,12 @@
 		   cache file had been compressed and was reopened, return
 		   without adding the cached data since cache_data buffer
 		   doesn't contain the cache_rec anymore. */
-		if (mail_cache_transaction_flush(ctx) < 0) {
+		if (MAIL_INDEX_IS_IN_MEMORY(ctx->cache->index)) {
+			/* just drop the old data to free up memory */
+			size_t space_needed = ctx->cache_data->used +
+				full_size - MAIL_CACHE_MAX_WRITE_BUFFER;
+			mail_cache_transaction_drop_unwanted(ctx, space_needed);
+		} else if (mail_cache_transaction_flush(ctx) < 0) {
 			/* make sure the transaction is reset, so we don't
 			   constantly try to flush for each call to this
 			   function */
@@ -812,3 +853,10 @@
 
 	return mail_cache_field_exists(ctx->view, seq, field_idx) == 0;
 }
+
+void mail_cache_close_mail(struct mail_cache_transaction_ctx *ctx,
+			   uint32_t seq)
+{
+	if (array_is_created(&ctx->cache_data_wanted_seqs))
+		seq_range_array_remove(&ctx->cache_data_wanted_seqs, seq);
+}
--- a/src/lib-index/mail-cache.h	Wed Aug 16 12:44:02 2017 +0300
+++ b/src/lib-index/mail-cache.h	Fri Aug 18 17:34:14 2017 +0300
@@ -115,6 +115,11 @@
    returned only if the decision is a forced no. */
 bool mail_cache_field_can_add(struct mail_cache_transaction_ctx *ctx,
 			      uint32_t seq, unsigned int field_idx);
+/* Notify cache that the mail is now closed. Any records added with
+   mail_cache_add() are unlikely to be required again. This mainly tells
+   INDEX=MEMORY that it can free up the memory used by the mail. */
+void mail_cache_close_mail(struct mail_cache_transaction_ctx *ctx,
+			   uint32_t seq);
 
 /* Returns 1 if field exists, 0 if not, -1 if error. */
 int mail_cache_field_exists(struct mail_cache_view *view, uint32_t seq,