]> git.ipfire.org Git - thirdparty/dovecot/core.git/commitdiff
lib-index: Track .log.2 rotation time in index header
authorTimo Sirainen <timo.sirainen@dovecot.fi>
Tue, 11 Jul 2017 12:35:16 +0000 (15:35 +0300)
committerTimo Sirainen <timo.sirainen@dovecot.fi>
Wed, 12 Jul 2017 21:43:50 +0000 (00:43 +0300)
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.

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

index bc5a41df042161de6cd6f533c385ea163d7c8888..9ecd1377911202e61dc4134da8388ba1acfbed63 100644 (file)
@@ -151,6 +151,7 @@ static void dump_hdr(struct mail_index *index)
                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));
index 1caa3f1a2ef076e3fa1ce328bda30f9fc20b5fe3..8ca40634e884006ca60d77bfc7df6dd215be1f90 100644 (file)
@@ -292,7 +292,8 @@ int mail_index_map_check_header(struct mail_index_map *map,
        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) {
index 10360958dd83848a47bb4295e940e9ba8ca64de9..453ef94c24b72139cd76849a9b40e8cf1ef0abab 100644 (file)
@@ -174,6 +174,7 @@ struct mail_index {
        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;
index 77582523724401c5b177b644e8a7a36a77bc8f45..24de5e1192a412ce73ba388c1b652738ffb7093e 100644 (file)
@@ -870,6 +870,14 @@ int mail_index_sync_commit(struct mail_index_sync_ctx **_ctx)
                                &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)
index 809e81dce388b98fb81e816376020276967707dd..69ff718567813c5e8b02e6cd23f0257f61592467 100644 (file)
@@ -140,6 +140,10 @@ void mail_index_write(struct mail_index *index, bool want_rotate)
                        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;
                }
        }
 
index 1e1a289d7d9460538d69b5a1ceb7e3c7e02803cf..9d6f2897836059f5eae50a734753e6a4f7c5b28a 100644 (file)
@@ -105,7 +105,11 @@ struct mail_index_header {
        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. */
index adf7648cd9bf4c29ffb1b4a95f251bd388fca058..60e9aad423fac6057b58a235cfe9f401b794a469 100644 (file)
@@ -39,21 +39,37 @@ mail_transaction_log_alloc(struct mail_index *index)
 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)