dovecot-2.2: lib-index: Simplified writing to dovecot.index.cach...

dovecot at dovecot.org dovecot at dovecot.org
Thu Oct 4 02:35:01 EEST 2012


details:   http://hg.dovecot.org/dovecot-2.2/rev/60c4815778fb
changeset: 15190:60c4815778fb
user:      Timo Sirainen <tss at iki.fi>
date:      Thu Oct 04 02:34:53 2012 +0300
description:
lib-index: Simplified writing to dovecot.index.cache file.
The old method was basically:
 - write max. 32 kB to internal buffer
 - flush it by writing to reserved areas (with no locks)

The reserved areas were acquired by doing (whenever needed):
 - lock dovecot.index.cache
 - reserve data from dovecot.index.cache for writing, potentially increasing
   the file size by writing 0 bytes. the reserved area size varies.
 - unlock dovecot.index.cache

This worked, but if multiple processes were writing to the cache file it
could have left incomplete reserved areas as holes. The holes were attempted
to be filled if they were large enough.

The new method is:
 - write max. 256 kB to internal buffer
 - lock dovecot.index.cache
 - append the buffer to dovecot.index.cache
 - unlock dovecot.index.cache

No reserved areas, holes or anything else weird going on.

Ideally no data would be overwritten in the dovecot.index.cache file, only
appended. Unfortunately currently some data is still overwritten:
 - mail_cache_header.{deleted_space,continued_record_count}
 - mail_cache_header_fields.next_offset when writing a new one
 - mail_cache_header_fields.{last_used,decision}
 - mail_cache_record.prev_offset

The changing headers could eventually be moved to dovecot.index. This
however is a backwards-incompatible change. The record's prev_offset could
maybe simply just not be written in those (somewhat rare) problematic
situations.

diffstat:

 src/doveadm/doveadm-dump-index.c       |    4 +-
 src/lib-index/mail-cache-compress.c    |    8 +-
 src/lib-index/mail-cache-private.h     |   29 +-
 src/lib-index/mail-cache-transaction.c |  611 +++++---------------------------
 src/lib-index/mail-cache.c             |   68 ++-
 5 files changed, 151 insertions(+), 569 deletions(-)

diffs (truncated from 1033 to 300 lines):

diff -r aa5c1d162714 -r 60c4815778fb src/doveadm/doveadm-dump-index.c
--- a/src/doveadm/doveadm-dump-index.c	Thu Oct 04 02:08:23 2012 +0300
+++ b/src/doveadm/doveadm-dump-index.c	Thu Oct 04 02:34:53 2012 +0300
@@ -324,8 +324,8 @@
 	       hdr->file_seq, unixdate2str(hdr->file_seq),
 	       hdr->file_seq - hdr->indexid);
 	printf("continued_record_count = %u\n", hdr->continued_record_count);
-	printf("hole_offset .......... = %u\n", hdr->hole_offset);
-	printf("used_file_size ....... = %u\n", hdr->used_file_size);
+	printf("hole_offset (unused) . = %u\n", hdr->unused_old_hole_offset);
+	printf("used_file_size (old) . = %u\n", hdr->backwards_compat_used_file_size);
 	printf("deleted_space ........ = %u\n", hdr->deleted_space);
 	printf("field_header_offset .. = %u (0x%08x nontranslated)\n",
 	       mail_index_offset_to_uint32(hdr->field_header_offset),
diff -r aa5c1d162714 -r 60c4815778fb src/lib-index/mail-cache-compress.c
--- a/src/lib-index/mail-cache-compress.c	Thu Oct 04 02:08:23 2012 +0300
+++ b/src/lib-index/mail-cache-compress.c	Thu Oct 04 02:34:53 2012 +0300
@@ -272,7 +272,7 @@
 	mail_cache_compress_get_fields(&ctx, used_fields_count);
 	o_stream_nsend(output, ctx.buffer->data, ctx.buffer->used);
 
-	hdr.used_file_size = output->offset;
+	hdr.backwards_compat_used_file_size = output->offset;
 	buffer_free(&ctx.buffer);
 	buffer_free(&ctx.field_seen);
 
@@ -287,12 +287,6 @@
 		array_free(ext_offsets);
 		return -1;
 	}
-
-	if (hdr.used_file_size < MAIL_CACHE_INITIAL_SIZE) {
-		/* grow the file some more. doesn't matter if it fails */
-		(void)file_set_size(fd, MAIL_CACHE_INITIAL_SIZE);
-	}
-
 	o_stream_destroy(&output);
 
 	if (cache->index->fsync_mode == FSYNC_MODE_ALWAYS) {
diff -r aa5c1d162714 -r 60c4815778fb src/lib-index/mail-cache-private.h
--- a/src/lib-index/mail-cache-private.h	Thu Oct 04 02:08:23 2012 +0300
+++ b/src/lib-index/mail-cache-private.h	Thu Oct 04 02:34:53 2012 +0300
@@ -13,9 +13,6 @@
 /* Never compress the file if it's smaller than this */
 #define MAIL_CACHE_COMPRESS_MIN_SIZE (1024*50)
 
-/* Don't bother remembering holes smaller than this */
-#define MAIL_CACHE_MIN_HOLE_SIZE 1024
-
 /* Compress the file when deleted space reaches n% of total size */
 #define MAIL_CACHE_COMPRESS_PERCENTAGE 20
 
@@ -27,15 +24,6 @@
    the latest cache header. */
 #define MAIL_CACHE_HEADER_FIELD_CONTINUE_COUNT 4
 
-/* Initial size for the file */
-#define MAIL_CACHE_INITIAL_SIZE (sizeof(struct mail_cache_header) + 10240)
-
-/* When more space is needed, grow the file n% larger than the previous size */
-#define MAIL_CACHE_GROW_PERCENTAGE 10
-
-/* When allocating space for transactions, don't use blocks larger than this. */
-#define MAIL_CACHE_MAX_RESERVED_BLOCK_SIZE (1024*512)
-
 #define MAIL_CACHE_LOCK_TIMEOUT 10
 #define MAIL_CACHE_LOCK_CHANGE_TIMEOUT 300
 
@@ -58,8 +46,8 @@
 
 	uint32_t continued_record_count;
 
-	uint32_t hole_offset;
-	uint32_t used_file_size;
+	uint32_t unused_old_hole_offset;
+	uint32_t backwards_compat_used_file_size;
 	uint32_t deleted_space;
 
 	uint32_t field_header_offset;
@@ -102,17 +90,6 @@
 	/* array of { uint32_t field; [ uint32_t size; ] { .. } } */
 };
 
-struct mail_cache_hole_header {
-	uint32_t next_offset; /* 0 if no holes left */
-	uint32_t size; /* including this header */
-
-	/* make sure we notice if we're treating hole as mail_cache_record.
-	   magic is a large number so if it's treated as size field, it'll
-	   point outside the file */
-#define MAIL_CACHE_HOLE_HEADER_MAGIC 0xffeedeff
-	uint32_t magic;
-};
-
 struct mail_cache_field_private {
 	struct mail_cache_field field;
 
@@ -230,6 +207,8 @@
 
 int mail_cache_write(struct mail_cache *cache, const void *data, size_t size,
 		     uoff_t offset);
+int mail_cache_append(struct mail_cache *cache, const void *data, size_t size,
+		      uint32_t *offset_r);
 
 int mail_cache_header_fields_read(struct mail_cache *cache);
 int mail_cache_header_fields_update(struct mail_cache *cache);
diff -r aa5c1d162714 -r 60c4815778fb src/lib-index/mail-cache-transaction.c
--- a/src/lib-index/mail-cache-transaction.c	Thu Oct 04 02:08:23 2012 +0300
+++ b/src/lib-index/mail-cache-transaction.c	Thu Oct 04 02:34:53 2012 +0300
@@ -15,16 +15,12 @@
 #include <stddef.h>
 #include <sys/stat.h>
 
-#define MAIL_CACHE_WRITE_BUFFER 32768
+#define MAIL_CACHE_INIT_WRITE_BUFFER (1024*16)
+#define MAIL_CACHE_MAX_WRITE_BUFFER (1024*256)
 
 #define CACHE_TRANS_CONTEXT(obj) \
 	MODULE_CONTEXT(obj, cache_mail_index_transaction_module)
 
-struct mail_cache_reservation {
-	uint32_t offset;
-	uint32_t size;
-};
-
 struct mail_cache_transaction_ctx {
 	union mail_index_transaction_module_context module_ctx;
 	struct mail_index_transaction_vfuncs super;
@@ -39,11 +35,9 @@
 	buffer_t *cache_data;
 	ARRAY(uint32_t) cache_data_seq;
 	uint32_t prev_seq;
-	size_t prev_pos;
+	size_t last_rec_pos;
 
-        ARRAY(struct mail_cache_reservation) reservations;
-	uint32_t reserved_space_offset, reserved_space;
-	uint32_t last_grow_size;
+	uoff_t bytes_written;
 
 	unsigned int tried_compression:1;
 	unsigned int changes:1;
@@ -52,10 +46,9 @@
 static MODULE_CONTEXT_DEFINE_INIT(cache_mail_index_transaction_module,
 				  &mail_index_module_register);
 
-static void
-mail_cache_transaction_free_reservations(struct mail_cache_transaction_ctx *ctx);
-static int mail_cache_link_unlocked(struct mail_cache *cache,
-				    uint32_t old_offset, uint32_t new_offset);
+static int mail_cache_transaction_lock(struct mail_cache_transaction_ctx *ctx);
+static int mail_cache_link_locked(struct mail_cache *cache,
+				  uint32_t old_offset, uint32_t new_offset);
 
 static void mail_index_transaction_cache_reset(struct mail_index_transaction *t)
 {
@@ -105,7 +98,6 @@
 	ctx->cache = view->cache;
 	ctx->view = view;
 	ctx->trans = t;
-	i_array_init(&ctx->reservations, 32);
 
 	i_assert(view->transaction == NULL);
 	view->transaction = ctx;
@@ -132,23 +124,27 @@
 	if (array_is_created(&ctx->cache_data_seq))
 		array_clear(&ctx->cache_data_seq);
 	ctx->prev_seq = 0;
-	ctx->prev_pos = 0;
-
-	array_clear(&ctx->reservations);
-	ctx->reserved_space_offset = 0;
-	ctx->reserved_space = 0;
-	ctx->last_grow_size = 0;
+	ctx->last_rec_pos = 0;
 
 	ctx->changes = FALSE;
 }
 
-static void
-mail_cache_transaction_free(struct mail_cache_transaction_ctx **_ctx)
+void mail_cache_transaction_rollback(struct mail_cache_transaction_ctx **_ctx)
 {
 	struct mail_cache_transaction_ctx *ctx = *_ctx;
 
 	*_ctx = NULL;
 
+	if (ctx->bytes_written > 0) {
+		/* we already wrote to the cache file. we can't (or don't want
+		   to) delete that data, so just mark it as deleted space */
+		if (mail_cache_transaction_lock(ctx) > 0) {
+			ctx->cache->hdr_copy.deleted_space +=
+				ctx->bytes_written;
+			(void)mail_cache_unlock(ctx->cache);
+		}
+	}
+
 	MODULE_CONTEXT_UNSET(ctx->trans, cache_mail_index_transaction_module);
 
 	ctx->view->transaction = NULL;
@@ -159,7 +155,6 @@
 		buffer_free(&ctx->cache_data);
 	if (array_is_created(&ctx->cache_data_seq))
 		array_free(&ctx->cache_data_seq);
-	array_free(&ctx->reservations);
 	i_free(ctx);
 }
 
@@ -279,346 +274,14 @@
 	return 1;
 }
 
-static int mail_cache_grow_file(struct mail_cache *cache, size_t size)
-{
-	struct stat st;
-	uoff_t new_fsize, grow_size;
-
-	i_assert(cache->locked);
-
-	/* grow the file */
-	new_fsize = cache->hdr_copy.used_file_size + size;
-	grow_size = new_fsize / 100 * MAIL_CACHE_GROW_PERCENTAGE;
-	if (grow_size < 16384)
-		grow_size = 16384;
-	new_fsize += grow_size;
-	new_fsize &= ~1023;
-
-	if (fstat(cache->fd, &st) < 0) {
-		mail_cache_set_syscall_error(cache, "fstat()");
-		return -1;
-	}
-
-	if ((uoff_t)st.st_size < new_fsize) {
-		if (file_set_size(cache->fd, new_fsize) < 0) {
-			mail_cache_set_syscall_error(cache, "file_set_size()");
-			return -1;
-		}
-	}
-	return 0;
-}
-
-static bool mail_cache_unlink_hole(struct mail_cache *cache, size_t size,
-				   struct mail_cache_hole_header *hole_r)
-{
-	struct mail_cache_header *hdr = &cache->hdr_copy;
-	struct mail_cache_hole_header hole;
-	uint32_t offset, prev_offset;
-
-	i_assert(cache->locked);
-
-	offset = hdr->hole_offset; prev_offset = 0;
-	while (offset != 0) {
-		if (pread_full(cache->fd, &hole, sizeof(hole), offset) <= 0) {
-			mail_cache_set_syscall_error(cache, "pread_full()");
-			return FALSE;
-		}
-
-		if (hole.magic != MAIL_CACHE_HOLE_HEADER_MAGIC) {
-			mail_cache_set_corrupted(cache,
-				"Invalid magic in hole header");
-			return FALSE;
-		}
-
-		if (hole.size >= size)
-			break;
-
-		prev_offset = offset;
-		offset = hole.next_offset;
-	}
-	if (offset == 0)
-		return FALSE;
-
-	if (prev_offset == 0)
-		hdr->hole_offset = hole.next_offset;
-	else {
-		if (mail_cache_write(cache, &hole.next_offset,
-				     sizeof(hole.next_offset), prev_offset) < 0)
-			return FALSE;
-	}
-	hdr->deleted_space -= hole.size;
-	cache->hdr_modified = TRUE;
-
-	hole_r->next_offset = offset;
-	hole_r->size = hole.size;
-	return TRUE;
-}
-
-static void
-mail_cache_transaction_add_reservation(struct mail_cache_transaction_ctx *ctx,
-				       uint32_t offset, uint32_t size)
-{
-	struct mail_cache_reservation res;
-
-	ctx->reserved_space_offset = offset;
-	ctx->reserved_space = size;
-
-	res.offset = offset;
-	res.size = size;
-
-	array_append(&ctx->reservations, &res, 1);


More information about the dovecot-cvs mailing list