changeset 22326:ba1a35c5ead7

lib-index: Track .log.2 rotation time in index header This avoids unnecessarily stat()ing the file. Also it's a bit better since it's tracking the actual rotation time, not the mtime of what the .log file happened to have at the time of rotation. The initial rotation timestamp is written only to the dovecot.index header without going through dovecot.index.log. This works, because the dovecot.index is written practically always after a log rotation. For the rare cases when it doesn't happen, the dovecot.index.log.2 just gets deleted later after the next log rotation.
author Timo Sirainen <timo.sirainen@dovecot.fi>
date Tue, 11 Jul 2017 15:35:16 +0300
parents e01bc3015b2f
children dc4a976b82a6
files src/doveadm/doveadm-dump-index.c src/lib-index/mail-index-map-hdr.c src/lib-index/mail-index-private.h src/lib-index/mail-index-sync.c src/lib-index/mail-index-write.c src/lib-index/mail-index.h src/lib-index/mail-transaction-log.c
diffstat 7 files changed, 42 insertions(+), 7 deletions(-) [+]
line wrap: on
line diff
--- a/src/doveadm/doveadm-dump-index.c	Tue Jul 11 15:33:56 2017 +0300
+++ b/src/doveadm/doveadm-dump-index.c	Tue Jul 11 15:35:16 2017 +0300
@@ -151,6 +151,7 @@
 		printf("log file head offset ..... = %u\n", hdr->log_file_head_offset);
 	}
 	if (hdr->minor_version >= 3) {
+		printf("log2 rotate time ......... = %u (%s)\n", hdr->log2_rotate_time, unixdate2str(hdr->log2_rotate_time));
 		printf("last temp file scan ...... = %u (%s)\n", hdr->last_temp_file_scan, unixdate2str(hdr->last_temp_file_scan));
 	}
 	printf("day stamp ................ = %u (%s)\n", hdr->day_stamp, unixdate2str(hdr->day_stamp));
--- a/src/lib-index/mail-index-map-hdr.c	Tue Jul 11 15:33:56 2017 +0300
+++ b/src/lib-index/mail-index-map-hdr.c	Tue Jul 11 15:35:16 2017 +0300
@@ -292,7 +292,8 @@
 	case 2:
 		/* pre-v2.2 (although should have been done in v2.1 already):
 		   make sure the old unused fields are cleared */
-		map->hdr.unused_old_sync_size = 0;
+		map->hdr.unused_old_sync_size_part1 = 0;
+		map->hdr.log2_rotate_time = 0;
 		map->hdr.last_temp_file_scan = 0;
 	}
 	if (hdr->first_recent_uid == 0) {
--- a/src/lib-index/mail-index-private.h	Tue Jul 11 15:33:56 2017 +0300
+++ b/src/lib-index/mail-index-private.h	Tue Jul 11 15:35:16 2017 +0300
@@ -174,6 +174,7 @@
 	uoff_t log_rotate_min_size, log_rotate_max_size;
 	unsigned int log_rotate_min_created_ago_secs;
 	unsigned int log_rotate_log2_stale_secs;
+	uint32_t pending_log2_rotate_time;
 
 	pool_t extension_pool;
 	ARRAY(struct mail_index_registered_ext) extensions;
--- a/src/lib-index/mail-index-sync.c	Tue Jul 11 15:33:56 2017 +0300
+++ b/src/lib-index/mail-index-sync.c	Tue Jul 11 15:35:16 2017 +0300
@@ -870,6 +870,14 @@
 				&next_uid, sizeof(next_uid), FALSE);
 		}
 	}
+	if (index->pending_log2_rotate_time != 0) {
+		uint32_t log2_rotate_time = index->pending_log2_rotate_time;
+
+		mail_index_update_header(ctx->ext_trans,
+			offsetof(struct mail_index_header, log2_rotate_time),
+			&log2_rotate_time, sizeof(log2_rotate_time), TRUE);
+		index->pending_log2_rotate_time = 0;
+	}
 
 	ret2 = mail_index_transaction_commit(&ctx->ext_trans);
 	if (cache_lock != NULL)
--- a/src/lib-index/mail-index-write.c	Tue Jul 11 15:33:56 2017 +0300
+++ b/src/lib-index/mail-index-write.c	Tue Jul 11 15:35:16 2017 +0300
@@ -140,6 +140,10 @@
 			hdr->log_file_seq = file->hdr.file_seq;
 			hdr->log_file_head_offset =
 				hdr->log_file_tail_offset = file->hdr.hdr_size;
+			/* Assume .log.2 was created successfully. If it
+			   wasn't, it just causes an extra stat() and gets
+			   fixed later on. */
+			hdr->log2_rotate_time = ioloop_time;
 		}
 	}
 
--- a/src/lib-index/mail-index.h	Tue Jul 11 15:33:56 2017 +0300
+++ b/src/lib-index/mail-index.h	Tue Jul 11 15:35:16 2017 +0300
@@ -105,7 +105,11 @@
 	uint32_t log_file_tail_offset;
 	uint32_t log_file_head_offset;
 
-	uint64_t unused_old_sync_size;
+	uint32_t unused_old_sync_size_part1;
+	/* Timestamp of when .log was rotated into .log.2. This can be used to
+	   optimize checking when it's time to unlink it without stat()ing it.
+	   0 = unknown, -1 = .log.2 doesn't exists. */
+	uint32_t log2_rotate_time;
 	uint32_t last_temp_file_scan;
 
 	/* daily first UIDs that have been added to index. */
--- a/src/lib-index/mail-transaction-log.c	Tue Jul 11 15:33:56 2017 +0300
+++ b/src/lib-index/mail-transaction-log.c	Tue Jul 11 15:35:16 2017 +0300
@@ -39,21 +39,37 @@
 static void mail_transaction_log_2_unlink_old(struct mail_transaction_log *log)
 {
 	struct stat st;
+	uint32_t log2_rotate_time = log->index->map->hdr.log2_rotate_time;
 
 	if (MAIL_INDEX_IS_IN_MEMORY(log->index))
 		return;
 
-	if (nfs_safe_stat(log->filepath2, &st) < 0) {
-		if (errno != ENOENT) {
+	if (log2_rotate_time == 0) {
+		if (nfs_safe_stat(log->filepath2, &st) == 0)
+			log2_rotate_time = st.st_mtime;
+		else if (errno == ENOENT)
+			log2_rotate_time = (uint32_t)-1;
+		else {
 			mail_index_set_error(log->index,
 				"stat(%s) failed: %m", log->filepath2);
+			return;
 		}
-		return;
 	}
 
-	if (ioloop_time - st.st_mtime >= (time_t)log->index->log_rotate_log2_stale_secs &&
-	    !log->index->readonly)
+	if (log2_rotate_time != (uint32_t)-1 &&
+	    ioloop_time - log2_rotate_time >= (time_t)log->index->log_rotate_log2_stale_secs &&
+	    !log->index->readonly) {
 		i_unlink_if_exists(log->filepath2);
+		log2_rotate_time = (uint32_t)-1;
+	}
+
+	if (log2_rotate_time != log->index->map->hdr.log2_rotate_time) {
+		/* Write this as part of the next sync's transaction. We're
+		   here because we're already opening a sync lock, so it'll
+		   always happen. It's also required especially with mdbox map
+		   index, which doesn't like changes done outside syncing. */
+		log->index->pending_log2_rotate_time = log2_rotate_time;
+	}
 }
 
 int mail_transaction_log_open(struct mail_transaction_log *log)