changeset 20393:53ba3feb039a

dbox: Optimize POP3 MAIL_FETCH_UIDL_BACKEND. We keep track of the highest UID known to have POP3 UIDL in index's header. If saving adds a newer message, it'll also update the header. When fetching UIDL_BACKEND, we can need to check only mails with lower UIDs. There are some race conditions here, but normally UIDLs are set only once during migration so it shouldn't matter.
author Timo Sirainen <timo.sirainen@dovecot.fi>
date Mon, 20 Jun 2016 20:06:38 +0300
parents 596b61c6a86c
children 0993d4b55bc8
files src/lib-storage/index/Makefile.am src/lib-storage/index/dbox-common/dbox-mail.c src/lib-storage/index/dbox-common/dbox-save.c src/lib-storage/index/dbox-common/dbox-save.h src/lib-storage/index/dbox-multi/mdbox-save.c src/lib-storage/index/dbox-single/sdbox-save.c src/lib-storage/index/index-pop3-uidl.c src/lib-storage/index/index-pop3-uidl.h src/lib-storage/index/index-storage.c src/lib-storage/index/index-transaction.c src/lib-storage/mail-storage-private.h
diffstat 11 files changed, 173 insertions(+), 2 deletions(-) [+]
line wrap: on
line diff
--- a/src/lib-storage/index/Makefile.am	Mon Jun 20 11:39:55 2016 +0300
+++ b/src/lib-storage/index/Makefile.am	Mon Jun 20 20:06:38 2016 +0300
@@ -20,6 +20,7 @@
 	index-mail-binary.c \
 	index-mail-headers.c \
 	index-mailbox-size.c \
+	index-pop3-uidl.c \
 	index-rebuild.c \
 	index-search.c \
 	index-search-result.c \
@@ -41,6 +42,7 @@
 	index-attachment.h \
 	index-mail.h \
 	index-mailbox-size.h \
+	index-pop3-uidl.h \
 	index-rebuild.h \
 	index-search-private.h \
 	index-search-result.h \
--- a/src/lib-storage/index/dbox-common/dbox-mail.c	Mon Jun 20 11:39:55 2016 +0300
+++ b/src/lib-storage/index/dbox-common/dbox-mail.c	Mon Jun 20 20:06:38 2016 +0300
@@ -5,6 +5,7 @@
 #include "str.h"
 #include "index-storage.h"
 #include "index-mail.h"
+#include "index-pop3-uidl.h"
 #include "dbox-attachment.h"
 #include "dbox-storage.h"
 #include "dbox-file.h"
@@ -223,15 +224,31 @@
 			  const char **value_r)
 {
 	struct dbox_mail *mail = (struct dbox_mail *)_mail;
+	int ret;
 
 	/* keep the UIDL in cache file, otherwise POP3 would open all
 	   mail files and read the metadata. same for GUIDs if they're
 	   used. */
 	switch (field) {
 	case MAIL_FETCH_UIDL_BACKEND:
-		return dbox_get_cached_metadata(mail, DBOX_METADATA_POP3_UIDL,
-						MAIL_CACHE_POP3_UIDL, value_r);
+		if (!index_pop3_uidl_can_exist(_mail)) {
+			*value_r = "";
+			return 0;
+		}
+		ret = dbox_get_cached_metadata(mail, DBOX_METADATA_POP3_UIDL,
+					       MAIL_CACHE_POP3_UIDL, value_r);
+		if (ret == 0) {
+			index_pop3_uidl_update_exists(&mail->imail.mail.mail,
+						      (*value_r)[0] != '\0');
+		}
+		return ret;
 	case MAIL_FETCH_POP3_ORDER:
+		if (!index_pop3_uidl_can_exist(_mail)) {
+			/* we're assuming that if there's a POP3 order, there's
+			   also a UIDL */
+			*value_r = "";
+			return 0;
+		}
 		return dbox_get_cached_metadata(mail, DBOX_METADATA_POP3_ORDER,
 						MAIL_CACHE_POP3_ORDER, value_r);
 	case MAIL_FETCH_GUID:
--- a/src/lib-storage/index/dbox-common/dbox-save.c	Mon Jun 20 11:39:55 2016 +0300
+++ b/src/lib-storage/index/dbox-common/dbox-save.c	Mon Jun 20 20:06:38 2016 +0300
@@ -166,11 +166,15 @@
 		str_printfa(str, "%c%s\n", DBOX_METADATA_POP3_UIDL,
 			    mdata->pop3_uidl);
 		ctx->have_pop3_uidls = TRUE;
+		ctx->highest_pop3_uidl_seq =
+			I_MAX(ctx->highest_pop3_uidl_seq, ctx->seq);
 	}
 	if (mdata->pop3_order != 0) {
 		str_printfa(str, "%c%u\n", DBOX_METADATA_POP3_ORDER,
 			    mdata->pop3_order);
 		ctx->have_pop3_orders = TRUE;
+		ctx->highest_pop3_uidl_seq =
+			I_MAX(ctx->highest_pop3_uidl_seq, ctx->seq);
 	}
 
 	guid = mdata->guid;
--- a/src/lib-storage/index/dbox-common/dbox-save.h	Mon Jun 20 11:39:55 2016 +0300
+++ b/src/lib-storage/index/dbox-common/dbox-save.h	Mon Jun 20 20:06:38 2016 +0300
@@ -14,6 +14,7 @@
 
 	struct ostream *dbox_output;
 
+	uint32_t highest_pop3_uidl_seq;
 	unsigned int failed:1;
 	unsigned int finished:1;
 	unsigned int have_pop3_uidls:1;
--- a/src/lib-storage/index/dbox-multi/mdbox-save.c	Mon Jun 20 11:39:55 2016 +0300
+++ b/src/lib-storage/index/dbox-multi/mdbox-save.c	Mon Jun 20 20:06:38 2016 +0300
@@ -11,6 +11,7 @@
 #include "ostream.h"
 #include "write-full.h"
 #include "index-mail.h"
+#include "index-pop3-uidl.h"
 #include "mail-copy.h"
 #include "dbox-save.h"
 #include "mdbox-storage.h"
@@ -326,6 +327,17 @@
 	mail_index_append_finish_uids(ctx->ctx.trans, hdr->next_uid,
 				      &_t->changes->saved_uids);
 
+	if (ctx->ctx.highest_pop3_uidl_seq != 0) {
+		struct seq_range_iter iter;
+		uint32_t uid;
+
+		seq_range_array_iter_init(&iter, &_t->changes->saved_uids);
+		if (!seq_range_array_iter_nth(&iter,
+				ctx->ctx.highest_pop3_uidl_seq-1, &uid))
+			i_unreached();
+		index_pop3_uidl_set_max_uid(&ctx->mbox->box, ctx->ctx.trans, uid);
+	}
+
 	/* save map UIDs to mailbox index */
 	if (first_map_uid != 0)
 		mdbox_save_set_map_uids(ctx, first_map_uid, last_map_uid);
--- a/src/lib-storage/index/dbox-single/sdbox-save.c	Mon Jun 20 11:39:55 2016 +0300
+++ b/src/lib-storage/index/dbox-single/sdbox-save.c	Mon Jun 20 20:06:38 2016 +0300
@@ -12,6 +12,7 @@
 #include "write-full.h"
 #include "index-mail.h"
 #include "mail-copy.h"
+#include "index-pop3-uidl.h"
 #include "dbox-attachment.h"
 #include "dbox-save.h"
 #include "sdbox-storage.h"
@@ -248,6 +249,10 @@
 		i_assert(ret);
 		if (sdbox_file_assign_uid(sfile, uid) < 0)
 			return -1;
+		if (ctx->ctx.highest_pop3_uidl_seq == i+1) {
+			index_pop3_uidl_set_max_uid(&ctx->mbox->box,
+				ctx->ctx.trans, uid);
+		}
 	}
 	i_assert(!seq_range_array_iter_nth(&iter, n, &uid));
 	return 0;
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/lib-storage/index/index-pop3-uidl.c	Mon Jun 20 20:06:38 2016 +0300
@@ -0,0 +1,101 @@
+/* Copyright (c) 2016 Dovecot authors, see the included COPYING file */
+
+#include "lib.h"
+#include "index-storage.h"
+#include "index-mail.h"
+#include "index-pop3-uidl.h"
+
+void index_pop3_uidl_set_max_uid(struct mailbox *box,
+				 struct mail_index_transaction *trans,
+				 uint32_t uid)
+{
+	struct mailbox_index_pop3_uidl uidl;
+
+	memset(&uidl, 0, sizeof(uidl));
+	uidl.max_uid_with_pop3_uidl = uid;
+
+	mail_index_update_header_ext(trans, box->pop3_uidl_hdr_ext_id,
+				     0, &uidl, sizeof(uidl));
+}
+
+bool index_pop3_uidl_can_exist(struct mail *mail)
+{
+	struct mailbox_index_pop3_uidl uidl;
+	const void *data;
+	size_t size;
+
+	/* We'll assume that if the header exists, it's up-to-date. normally
+	   UIDLs are set only during migration, so this value never changes.
+	   Also even if it does, it becomes out-of-date only when the mailbox
+	   is modified with old Dovecot versions. To fix that we'd have to
+	   add and keep updating "max tracked uid" in this header for every
+	   saved mail, which isn't worth it. */
+	mail_index_get_header_ext(mail->transaction->view,
+				  mail->box->pop3_uidl_hdr_ext_id,
+				  &data, &size);
+	if (size < sizeof(uidl)) {
+		/* this header isn't set yet */
+		return TRUE;
+	}
+	memcpy(&uidl, data, size);
+	return mail->uid <= uidl.max_uid_with_pop3_uidl;
+}
+
+void index_pop3_uidl_update_exists(struct mail *mail, bool exists)
+{
+	struct mailbox_transaction_context *trans = mail->transaction;
+
+	if (exists) {
+		if (trans->highest_pop3_uidl_uid < mail->uid) {
+			trans->highest_pop3_uidl_uid = mail->uid;
+			trans->prev_pop3_uidl_tracking_seq = mail->seq;
+		}
+	} else if (mail->seq == trans->prev_pop3_uidl_tracking_seq+1) {
+		trans->prev_pop3_uidl_tracking_seq++;
+	} else {
+		/* skipping mails. we don't know the state. */
+	}
+}
+
+void index_pop3_uidl_update_exists_finish(struct mailbox_transaction_context *trans)
+{
+	struct mail_index_view *view;
+	struct mailbox_index_pop3_uidl uidl;
+	const void *data;
+	size_t size;
+	bool seen_all_msgs;
+
+	if (trans->highest_pop3_uidl_uid == 0)
+		return;
+
+	/* First check that we actually looked at UIDL for all messages.
+	   Otherwise we can't say for sure if the newest messages had UIDLs. */
+	if (trans->prev_pop3_uidl_tracking_seq !=
+	    mail_index_view_get_messages_count(trans->view))
+		return;
+
+	/* Just to be sure: Refresh the index and check again. POP3 keeps
+	   transactions open for duration of the entire session. Maybe another
+	   process already added new mails (and already updated this header).
+	   This check is racy, but normally UIDLs aren't added after migration
+	   so it's a bit questionable if it's even worth having this check in
+	   there. */
+	view = mail_index_view_open(trans->box->index);
+	seen_all_msgs = mail_index_refresh(trans->box->index) == 0 &&
+		trans->prev_pop3_uidl_tracking_seq ==
+		mail_index_view_get_messages_count(view);
+	mail_index_view_close(&view);
+	if (!seen_all_msgs)
+		return;
+
+	/* check if we have already the same header */
+	mail_index_get_header_ext(trans->view, trans->box->pop3_uidl_hdr_ext_id,
+				  &data, &size);
+	if (size >= sizeof(uidl)) {
+		memcpy(&uidl, data, size);
+		if (trans->highest_pop3_uidl_uid == uidl.max_uid_with_pop3_uidl)
+			return;
+	}
+	index_pop3_uidl_set_max_uid(trans->box, trans->itrans,
+				    trans->highest_pop3_uidl_uid);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/lib-storage/index/index-pop3-uidl.h	Mon Jun 20 20:06:38 2016 +0300
@@ -0,0 +1,16 @@
+#ifndef INDEX_POP3_H
+#define INDEX_POP3_H
+
+struct mail_index_transaction;
+struct mail;
+struct mailbox;
+struct mailbox_transaction_context;
+
+void index_pop3_uidl_set_max_uid(struct mailbox *box,
+				 struct mail_index_transaction *trans,
+				 uint32_t uid);
+bool index_pop3_uidl_can_exist(struct mail *mail);
+void index_pop3_uidl_update_exists(struct mail *mail, bool exists);
+void index_pop3_uidl_update_exists_finish(struct mailbox_transaction_context *trans);
+
+#endif
--- a/src/lib-storage/index/index-storage.c	Mon Jun 20 11:39:55 2016 +0300
+++ b/src/lib-storage/index/index-storage.c	Mon Jun 20 20:06:38 2016 +0300
@@ -293,6 +293,9 @@
 		mail_index_ext_register(box->index, "hdr-vsize",
 					sizeof(struct mailbox_index_vsize), 0,
 					sizeof(uint64_t));
+	box->pop3_uidl_hdr_ext_id =
+		mail_index_ext_register(box->index, "hdr-pop3-uidl",
+					sizeof(struct mailbox_index_pop3_uidl), 0, 0);
 
 	box->opened = TRUE;
 
--- a/src/lib-storage/index/index-transaction.c	Mon Jun 20 11:39:55 2016 +0300
+++ b/src/lib-storage/index/index-transaction.c	Mon Jun 20 20:06:38 2016 +0300
@@ -5,6 +5,7 @@
 #include "dict.h"
 #include "index-storage.h"
 #include "index-sync-private.h"
+#include "index-pop3-uidl.h"
 #include "index-mail.h"
 
 static void index_transaction_free(struct mailbox_transaction_context *t)
@@ -28,6 +29,7 @@
 	struct index_mailbox_sync_pvt_context *pvt_sync_ctx = NULL;
 	int ret = 0;
 
+	index_pop3_uidl_update_exists_finish(t);
 	if (t->nontransactional_changes)
 		t->changes->changed = TRUE;
 
--- a/src/lib-storage/mail-storage-private.h	Mon Jun 20 11:39:55 2016 +0300
+++ b/src/lib-storage/mail-storage-private.h	Mon Jun 20 20:06:38 2016 +0300
@@ -292,6 +292,10 @@
 	uint32_t message_count;
 };
 
+struct mailbox_index_pop3_uidl {
+	uint32_t max_uid_with_pop3_uidl;
+};
+
 struct mailbox_index_first_saved {
 	uint32_t uid;
 	uint32_t timestamp;
@@ -346,6 +350,7 @@
 	enum mailbox_feature enabled_features;
 	struct mail_msgpart_partial_cache partial_cache;
 	uint32_t vsize_hdr_ext_id;
+	uint32_t pop3_uidl_hdr_ext_id;
 
 	/* MAIL_RECENT flags handling */
 	ARRAY_TYPE(seq_range) recent_flags;
@@ -551,6 +556,9 @@
 	struct mail_transaction_commit_changes *changes;
 	ARRAY(union mailbox_transaction_module_context *) module_contexts;
 
+	uint32_t prev_pop3_uidl_tracking_seq;
+	uint32_t highest_pop3_uidl_uid;
+
 	struct mail_save_context *save_ctx;
 	/* number of mails saved/copied within this transaction. */
 	unsigned int save_count;