]> 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 14:17:35 +0000 (17:17 +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 f14e88326ff5b88883d94026da1c51286ba54902..8d52140d0c5830f662bf5a4bcec5702c7c5a81a5 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 cbb114e4a5ffae5f73f410e9df09d96553f03a2a..58dd42c624bb69a8d3204040af5aa5af970cdc90 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 692ae58666d758ae49b75619e547c461e052f4f6..28a7ba0d4926f8129f98dade5544c3136bd12c17 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 b6434f7a68bfcf5451c3c77f8eccfba9501ec198..64810144b4cb5009e43c1365cc887795d069e208 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 f86ca20365db37d3910afa9fd4bb87a65549016e..08aaf448dfd9b44ccd6977afed482152c155c32a 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)