changeset 22430:2d9175af5c99

lib-index: Fix wrong mail_index_modseq_header automatically It happens only on the next sync, although that isn't actually guaranteed to happen. Still, it happens almost always so this should be good enough.
author Timo Sirainen <timo.sirainen@dovecot.fi>
date Tue, 08 Aug 2017 16:56:02 +0300
parents 3ba9331e880d
children 691b5466edb3
files src/lib-index/mail-index-sync.c src/lib-index/mail-transaction-log-file.c src/lib-index/mail-transaction-log-private.h src/lib-index/mail-transaction-log.c src/lib-index/test-mail-transaction-log-file.c
diffstat 5 files changed, 57 insertions(+), 7 deletions(-) [+]
line wrap: on
line diff
--- a/src/lib-index/mail-index-sync.c	Tue Aug 08 16:54:42 2017 +0300
+++ b/src/lib-index/mail-index-sync.c	Tue Aug 08 16:56:02 2017 +0300
@@ -361,7 +361,7 @@
 	}
 
 	if (!mail_index_need_sync(index, flags, log_file_seq, log_file_offset) &&
-	    !index->index_deleted) {
+	    !index->index_deleted && !index->need_recreate) {
 		if (locked)
 			mail_transaction_log_sync_unlock(index->log, "syncing determined unnecessary");
 		return 0;
--- a/src/lib-index/mail-transaction-log-file.c	Tue Aug 08 16:54:42 2017 +0300
+++ b/src/lib-index/mail-transaction-log-file.c	Tue Aug 08 16:56:02 2017 +0300
@@ -1257,7 +1257,7 @@
 
 static int
 get_modseq_next_offset_at(struct mail_transaction_log_file *file,
-			  uint64_t modseq,
+			  uint64_t modseq, bool use_highest,
 			  uoff_t *cur_offset, uint64_t *cur_modseq,
 			  uoff_t *next_offset_r)
 {
@@ -1279,7 +1279,7 @@
 	}
 
 	/* check sync_highest_modseq again in case sync_offset was updated */
-	if (modseq >= file->sync_highest_modseq) {
+	if (modseq >= file->sync_highest_modseq && use_highest) {
 		*next_offset_r = file->sync_offset;
 		return 0;
 	}
@@ -1332,16 +1332,30 @@
 		cur_modseq = cache->highest_modseq;
 	}
 
-	if ((ret = get_modseq_next_offset_at(file, modseq, &cur_offset,
+	if ((ret = get_modseq_next_offset_at(file, modseq, TRUE, &cur_offset,
 					     &cur_modseq, next_offset_r)) <= 0)
 		return ret;
 	if (cur_offset == file->sync_offset) {
 		/* if we got to sync_offset, cur_modseq should be
 		   sync_highest_modseq */
 		mail_index_set_error(file->log->index,
-			"%s: Transaction log changed unexpectedly, "
-			"can't get modseq", file->filepath);
-		return -1;
+			"%s: Transaction log modseq tracking is corrupted - fixing",
+			file->filepath);
+		/* retry getting the offset by reading from the beginning
+		   of the file */
+		cur_offset = file->hdr.hdr_size;
+		cur_modseq = file->hdr.initial_modseq;
+		ret = get_modseq_next_offset_at(file, modseq, FALSE,
+						&cur_offset, &cur_modseq,
+						next_offset_r);
+		if (ret < 0)
+			return -1;
+		i_assert(ret != 0);
+		/* get it fixed on the next sync */
+		file->log->index->need_recreate = TRUE;
+		file->need_rotate = TRUE;
+		/* clear cache, since it's unreliable */
+		memset(file->modseq_cache, 0, sizeof(file->modseq_cache));
 	}
 
 	/* @UNSAFE: cache the value */
--- a/src/lib-index/mail-transaction-log-private.h	Tue Aug 08 16:54:42 2017 +0300
+++ b/src/lib-index/mail-transaction-log-private.h	Tue Aug 08 16:56:02 2017 +0300
@@ -81,6 +81,7 @@
 	unsigned int locked:1;
 	unsigned int locked_sync_offset_updated:1;
 	unsigned int corrupted:1;
+	unsigned int need_rotate:1;
 };
 
 struct mail_transaction_log {
--- a/src/lib-index/mail-transaction-log.c	Tue Aug 08 16:54:42 2017 +0300
+++ b/src/lib-index/mail-transaction-log.c	Tue Aug 08 16:56:02 2017 +0300
@@ -234,6 +234,9 @@
 {
 	struct mail_transaction_log_file *file = log->head;
 
+	if (file->need_rotate)
+		return TRUE;
+
 	if (file->hdr.major_version < MAIL_TRANSACTION_LOG_MAJOR_VERSION ||
 	    (file->hdr.major_version == MAIL_TRANSACTION_LOG_MAJOR_VERSION &&
 	     file->hdr.minor_version < MAIL_TRANSACTION_LOG_MINOR_VERSION)) {
--- a/src/lib-index/test-mail-transaction-log-file.c	Tue Aug 08 16:54:42 2017 +0300
+++ b/src/lib-index/test-mail-transaction-log-file.c	Tue Aug 08 16:56:02 2017 +0300
@@ -369,11 +369,43 @@
 	test_end();
 }
 
+static void
+test_mail_transaction_log_file_get_modseq_next_offset_inconsistency(void)
+{
+	test_begin("mail_transaction_log_file_get_modseq_next_offset() inconsistency");
+
+	struct mail_index *index = test_mail_index_open();
+	struct mail_transaction_log_file *file = index->log->head;
+	uint32_t seq;
+
+	/* add modseq=2 */
+	struct mail_index_view *view = mail_index_view_open(index);
+	struct mail_index_transaction *trans =
+		mail_index_transaction_begin(view, 0);
+	mail_index_append(trans, 1, &seq);
+	test_assert(mail_index_transaction_commit(&trans) == 0);
+	mail_index_view_close(&view);
+
+	/* emulate a broken mail_index_modseq_header header */
+	file->sync_highest_modseq = 3;
+
+	uoff_t next_offset;
+	test_expect_error_string("Transaction log modseq tracking is corrupted");
+	test_assert(mail_transaction_log_file_get_modseq_next_offset(file, 2, &next_offset) == 0);
+	test_expect_no_more_errors();
+	test_assert(next_offset == file->sync_offset);
+
+	mail_index_close(index);
+	mail_index_free(&index);
+	test_end();
+}
+
 int main(void)
 {
 	static void (*const test_functions[])(void) = {
 		test_mail_transaction_update_modseq,
 		test_mail_transaction_log_file_modseq_offsets,
+		test_mail_transaction_log_file_get_modseq_next_offset_inconsistency,
 		NULL
 	};
 	ioloop_time = 1;