changeset 5655:501aab713b0d HEAD

Write transactions with two pwrite() calls instead of lots of tiny ones.
author Timo Sirainen <tss@iki.fi>
date Tue, 22 May 2007 23:01:14 +0300
parents 29764126e1a6
children 1273ce0585b3
files src/lib-index/mail-transaction-log-append.c
diffstat 1 files changed, 141 insertions(+), 170 deletions(-) [+]
line wrap: on
line diff
--- a/src/lib-index/mail-transaction-log-append.c	Tue May 22 22:50:06 2007 +0300
+++ b/src/lib-index/mail-transaction-log-append.c	Tue May 22 23:01:14 2007 +0300
@@ -10,100 +10,103 @@
 #include "mail-index-transaction-private.h"
 #include "mail-transaction-log-private.h"
 
-static int log_append_buffer(struct mail_transaction_log_file *file,
-			     const buffer_t *buf, const buffer_t *hdr_buf,
-			     enum mail_transaction_type type, bool external)
+struct log_append_context {
+	struct mail_transaction_log_file *file;
+	struct mail_index_transaction *trans;
+	buffer_t *output;
+};
+
+static void log_append_buffer(struct log_append_context *ctx,
+			      const buffer_t *buf, const buffer_t *hdr_buf,
+			      enum mail_transaction_type type)
 {
 	struct mail_transaction_header hdr;
-	const void *data, *hdr_data;
-	size_t size, hdr_data_size;
-	uoff_t offset;
 	uint32_t hdr_size;
 
 	i_assert((type & MAIL_TRANSACTION_TYPE_MASK) != 0);
-
-	data = buffer_get_data(buf, &size);
-	if (size == 0)
-		return 0;
-
-	i_assert((size % 4) == 0);
+	i_assert((buf->used % 4) == 0);
+	i_assert(hdr_buf == NULL || (hdr_buf->used % 4) == 0);
 
-	if (hdr_buf != NULL) {
-		hdr_data = buffer_get_data(hdr_buf, &hdr_data_size);
-		i_assert((hdr_data_size % 4) == 0);
-	} else {
-		hdr_data = NULL;
-		hdr_data_size = 0;
-	}
+	if (buf->used == 0)
+		return;
 
 	memset(&hdr, 0, sizeof(hdr));
 	hdr.type = type;
 	if (type == MAIL_TRANSACTION_EXPUNGE)
 		hdr.type |= MAIL_TRANSACTION_EXPUNGE_PROT;
-	if (external)
+	if (ctx->trans->external)
 		hdr.type |= MAIL_TRANSACTION_EXTERNAL;
 
-	hdr_size = mail_index_uint32_to_offset(sizeof(hdr) + size +
-					       hdr_data_size);
-	if (!MAIL_TRANSACTION_LOG_FILE_IN_MEMORY(file)) {
-		do {
-			offset = file->sync_offset;
-			if (file->first_append_size == 0) {
-				/* size will be written later once everything
-				   is in disk */
-				file->first_append_size = hdr_size;
-			} else {
-				hdr.size = hdr_size;
-			}
-			if (pwrite_full(file->fd, &hdr, sizeof(hdr),
-					offset) < 0)
-				break;
-			offset += sizeof(hdr);
+	hdr_size = mail_index_uint32_to_offset(sizeof(hdr) + buf->used +
+					       (hdr_buf == NULL ? 0 :
+						hdr_buf->used));
+	if (!MAIL_TRANSACTION_LOG_FILE_IN_MEMORY(ctx->file) &&
+	    ctx->file->first_append_size == 0) {
+		/* size will be written later once everything
+		   is in disk */
+		ctx->file->first_append_size = hdr_size;
+	} else {
+		hdr.size = hdr_size;
+	}
+
+	buffer_append(ctx->output, &hdr, sizeof(hdr));
+	if (hdr_buf != NULL)
+		buffer_append(ctx->output, hdr_buf->data, hdr_buf->used);
+	buffer_append(ctx->output, buf->data, buf->used);
+}
+
+static int log_buffer_move_to_memory(struct log_append_context *ctx)
+{
+	struct mail_transaction_log_file *file = ctx->file;
 
-			if (hdr_data_size > 0) {
-				if (pwrite_full(file->fd, hdr_data,
-						hdr_data_size, offset) < 0)
-					break;
-				offset += hdr_data_size;
-			}
+	/* first we need to truncate this latest write so that log syncing
+	   doesn't break */
+	if (ftruncate(file->fd, file->sync_offset) < 0) {
+		mail_index_file_set_syscall_error(file->log->index,
+						  file->filepath,
+						  "ftruncate()");
+	}
+
+	if (mail_index_move_to_memory(file->log->index) < 0)
+		return -1;
+	i_assert(MAIL_TRANSACTION_LOG_FILE_IN_MEMORY(file));
 
-			if (pwrite_full(file->fd, data, size, offset) < 0)
-				break;
+	i_assert(file->buffer_offset + file->buffer->used ==
+		 file->sync_offset);
+	buffer_append_buf(file->buffer, ctx->output, 0, (size_t)-1);
+	file->sync_offset = file->buffer_offset + file->buffer->used;
+	return 0;
+}
 
-			file->sync_offset = offset + size;
-			return 0;
-		} while (0);
+static int log_buffer_write(struct log_append_context *ctx)
+{
+	struct mail_transaction_log_file *file = ctx->file;
 
-		/* write failure. */
+	if (MAIL_TRANSACTION_LOG_FILE_IN_MEMORY(file)) {
+		file->sync_offset = file->buffer_offset + file->buffer->used;
+		return 0;
+	}
+
+	i_assert(file->first_append_size != 0);
+	if (pwrite_full(file->fd, ctx->output->data, ctx->output->used,
+			file->sync_offset) < 0) {
+		/* write failure, fallback to in-memory indexes. */
 		mail_index_file_set_syscall_error(file->log->index,
 						  file->filepath,
 						  "pwrite_full()");
-		if (!ENOSPACE(errno))
-			return -1;
-
-		/* not enough space. fallback to in-memory indexes. first
-		   we need to truncate this latest write so that log syncing
-		   doesn't break */
-		if (ftruncate(file->fd, file->sync_offset) < 0) {
-			mail_index_file_set_syscall_error(file->log->index,
-							  file->filepath,
-							  "ftruncate()");
-			return -1;
-		}
-
-		if (mail_index_move_to_memory(file->log->index) < 0)
-			return -1;
-		i_assert(MAIL_TRANSACTION_LOG_FILE_IN_MEMORY(file));
+		return log_buffer_move_to_memory(ctx);
 	}
 
-	hdr.size = hdr_size;
-
-	i_assert(file->buffer_offset + file->buffer->used ==
-		 file->sync_offset);
-	buffer_append(file->buffer, &hdr, sizeof(hdr));
-	buffer_append(file->buffer, hdr_data, hdr_data_size);
-	buffer_append(file->buffer, data, size);
-	file->sync_offset = file->buffer_offset + file->buffer->used;
+	/* now that the whole transaction has been written, rewrite the first
+	   record's size so the transaction becomes visible */
+	if (pwrite_full(file->fd, &file->first_append_size,
+			sizeof(uint32_t), file->sync_offset) < 0) {
+		mail_index_file_set_syscall_error(file->log->index,
+						  file->filepath,
+						  "pwrite_full()");
+		return log_buffer_move_to_memory(ctx);
+	}
+	file->sync_offset += ctx->output->used;
 	return 0;
 }
 
@@ -140,10 +143,10 @@
 	return buf;
 }
 
-static int log_append_ext_intro(struct mail_transaction_log_file *file,
-				struct mail_index_transaction *t,
-				uint32_t ext_id, uint32_t reset_id)
+static void log_append_ext_intro(struct log_append_context *ctx,
+				 uint32_t ext_id, uint32_t reset_id)
 {
+	struct mail_index_transaction *t = ctx->trans;
 	const struct mail_index_registered_ext *rext;
         struct mail_transaction_ext_intro *intro;
 	buffer_t *buf;
@@ -198,14 +201,13 @@
 	if ((buf->used % 4) != 0)
 		buffer_append_zero(buf, 4 - (buf->used % 4));
 
-	return log_append_buffer(file, buf, NULL, MAIL_TRANSACTION_EXT_INTRO,
-				 t->external);
+	log_append_buffer(ctx, buf, NULL, MAIL_TRANSACTION_EXT_INTRO);
 }
 
-static int
-mail_transaction_log_append_ext_intros(struct mail_transaction_log_file *file,
-				       struct mail_index_transaction *t)
+static void
+mail_transaction_log_append_ext_intros(struct log_append_context *ctx)
 {
+	struct mail_index_transaction *t = ctx->trans;
         const struct mail_transaction_ext_intro *resize;
 	struct mail_transaction_ext_reset ext_reset;
 	unsigned int update_count, resize_count, reset_count, ext_count;
@@ -249,24 +251,18 @@
 		if ((ext_id < resize_count && resize[ext_id].name_size) ||
 		    (ext_id < update_count &&
 		     array_is_created(&update[ext_id])) ||
-		    ext_reset.new_reset_id != 0) {
-			if (log_append_ext_intro(file, t, ext_id, 0) < 0)
-				return -1;
-		}
+		    ext_reset.new_reset_id != 0)
+			log_append_ext_intro(ctx, ext_id, 0);
 		if (ext_reset.new_reset_id != 0) {
-			if (log_append_buffer(file, buf, NULL,
-					      MAIL_TRANSACTION_EXT_RESET,
-					      t->external) < 0)
-				return -1;
+			log_append_buffer(ctx, buf, NULL,
+					  MAIL_TRANSACTION_EXT_RESET);
 		}
 	}
-
-	return 0;
 }
 
-static int log_append_ext_rec_updates(struct mail_transaction_log_file *file,
-				      struct mail_index_transaction *t)
+static void log_append_ext_rec_updates(struct log_append_context *ctx)
 {
+	struct mail_index_transaction *t = ctx->trans;
 	ARRAY_TYPE(seq_array) *updates;
 	const uint32_t *reset;
 	unsigned int ext_id, count, reset_count;
@@ -292,20 +288,15 @@
 
 		reset_id = ext_id < reset_count && reset[ext_id] != 0 ?
 			reset[ext_id] : 0;
-		if (log_append_ext_intro(file, t, ext_id, reset_id) < 0)
-			return -1;
+		log_append_ext_intro(ctx, ext_id, reset_id);
 
-		if (log_append_buffer(file, updates[ext_id].arr.buffer, NULL,
-				      MAIL_TRANSACTION_EXT_REC_UPDATE,
-				      t->external) < 0)
-			return -1;
+		log_append_buffer(ctx, updates[ext_id].arr.buffer, NULL,
+				  MAIL_TRANSACTION_EXT_REC_UPDATE);
 	}
-	return 0;
 }
 
-static int
-log_append_keyword_update(struct mail_transaction_log_file *file,
-			  struct mail_index_transaction *t,
+static void
+log_append_keyword_update(struct log_append_context *ctx,
 			  buffer_t *hdr_buf, enum modify_type modify_type,
 			  const char *keyword, const buffer_t *buffer)
 {
@@ -321,12 +312,11 @@
 	if ((hdr_buf->used % 4) != 0)
 		buffer_append_zero(hdr_buf, 4 - (hdr_buf->used % 4));
 
-	return log_append_buffer(file, buffer, hdr_buf,
-				 MAIL_TRANSACTION_KEYWORD_UPDATE, t->external);
+	log_append_buffer(ctx, buffer, hdr_buf,
+			  MAIL_TRANSACTION_KEYWORD_UPDATE);
 }
 
-static int log_append_keyword_updates(struct mail_transaction_log_file *file,
-				      struct mail_index_transaction *t)
+static void log_append_keyword_updates(struct log_append_context *ctx)
 {
         const struct mail_index_transaction_keyword_update *updates;
 	const char *const *keywords;
@@ -335,27 +325,23 @@
 
 	hdr_buf = buffer_create_dynamic(pool_datastack_create(), 64);
 
-	keywords = array_get_modifiable(&t->view->index->keywords,
+	keywords = array_get_modifiable(&ctx->trans->view->index->keywords,
 					&keywords_count);
-	updates = array_get_modifiable(&t->keyword_updates, &count);
+	updates = array_get_modifiable(&ctx->trans->keyword_updates, &count);
 	i_assert(count <= keywords_count);
 
 	for (i = 0; i < count; i++) {
 		if (array_is_created(&updates[i].add_seq)) {
-			if (log_append_keyword_update(file, t, hdr_buf,
+			log_append_keyword_update(ctx, hdr_buf,
 					MODIFY_ADD, keywords[i],
-					updates[i].add_seq.arr.buffer) < 0)
-				return -1;
+					updates[i].add_seq.arr.buffer);
 		}
 		if (array_is_created(&updates[i].remove_seq)) {
-			if (log_append_keyword_update(file, t, hdr_buf,
+			log_append_keyword_update(ctx, hdr_buf,
 					MODIFY_REMOVE, keywords[i],
-					updates[i].remove_seq.arr.buffer) < 0)
-				return -1;
+					updates[i].remove_seq.arr.buffer);
 		}
 	}
-
-	return 0;
 }
 
 #define LOG_WANT_ROTATE(file) \
@@ -379,10 +365,10 @@
 	struct mail_transaction_log *log;
 	struct mail_transaction_log_file *file;
 	struct mail_index_header idx_hdr;
+	struct log_append_context ctx;
 	uoff_t append_offset;
 	unsigned int old_hidden_syncs_count;
 	unsigned int lock_id;
-	int ret;
 
 	index = mail_index_view_get_index(view);
 	log = index->log;
@@ -423,61 +409,59 @@
 
 	file = log->head;
 	file->first_append_size = 0;
-	append_offset = file->sync_offset;
 
 	if (file->sync_offset < file->buffer_offset)
 		file->sync_offset = file->buffer_offset;
 
+	memset(&ctx, 0, sizeof(ctx));
+	ctx.file = file;
+	ctx.trans = t;
+	ctx.output = buffer_create_dynamic(default_pool, 1024);
+
+	/* Transactions can be hidden. Mark all such  */
 	old_hidden_syncs_count = !array_is_created(&view->syncs_hidden) ? 0 :
 		array_count(&view->syncs_hidden);
 
-	ret = 0;
-
 	/* send all extension introductions and resizes before appends
 	   to avoid resize overhead as much as possible */
-        ret = mail_transaction_log_append_ext_intros(file, t);
+        mail_transaction_log_append_ext_intros(&ctx);
 
-	if (t->pre_hdr_changed && ret == 0) {
-		ret = log_append_buffer(file,
-					log_get_hdr_update_buffer(t, TRUE),
-					NULL, MAIL_TRANSACTION_HEADER_UPDATE,
-					t->external);
+	if (t->pre_hdr_changed) {
+		log_append_buffer(&ctx,
+				  log_get_hdr_update_buffer(t, TRUE),
+				  NULL, MAIL_TRANSACTION_HEADER_UPDATE);
 	}
-	if (array_is_created(&t->appends) && ret == 0) {
-		ret = log_append_buffer(file, t->appends.arr.buffer, NULL,
-					MAIL_TRANSACTION_APPEND, t->external);
+	if (array_is_created(&t->appends)) {
+		log_append_buffer(&ctx, t->appends.arr.buffer, NULL,
+				  MAIL_TRANSACTION_APPEND);
 	}
-	if (array_is_created(&t->updates) && ret == 0) {
-		ret = log_append_buffer(file, t->updates.arr.buffer, NULL,
-					MAIL_TRANSACTION_FLAG_UPDATE,
-					t->external);
+	if (array_is_created(&t->updates)) {
+		log_append_buffer(&ctx, t->updates.arr.buffer, NULL,
+				  MAIL_TRANSACTION_FLAG_UPDATE);
 	}
 
-	if (array_is_created(&t->ext_rec_updates) && ret == 0)
-		ret = log_append_ext_rec_updates(file, t);
+	if (array_is_created(&t->ext_rec_updates))
+		log_append_ext_rec_updates(&ctx);
 
 	/* keyword resets before updates */
-	if (array_is_created(&t->keyword_resets) && ret == 0) {
-		ret = log_append_buffer(file, t->keyword_resets.arr.buffer,
-					NULL, MAIL_TRANSACTION_KEYWORD_RESET,
-					t->external);
+	if (array_is_created(&t->keyword_resets)) {
+		log_append_buffer(&ctx, t->keyword_resets.arr.buffer,
+				  NULL, MAIL_TRANSACTION_KEYWORD_RESET);
 	}
-	if (array_is_created(&t->keyword_updates) && ret == 0)
-		ret = log_append_keyword_updates(file, t);
+	if (array_is_created(&t->keyword_updates))
+		log_append_keyword_updates(&ctx);
 
-	if (array_is_created(&t->expunges) && ret == 0) {
-		ret = log_append_buffer(file, t->expunges.arr.buffer, NULL,
-					MAIL_TRANSACTION_EXPUNGE, t->external);
+	if (array_is_created(&t->expunges)) {
+		log_append_buffer(&ctx, t->expunges.arr.buffer, NULL,
+				  MAIL_TRANSACTION_EXPUNGE);
 	}
 
-	if (t->post_hdr_changed && ret == 0) {
-		ret = log_append_buffer(file,
-					log_get_hdr_update_buffer(t, FALSE),
-					NULL, MAIL_TRANSACTION_HEADER_UPDATE,
-					t->external);
+	if (t->post_hdr_changed) {
+		log_append_buffer(&ctx, log_get_hdr_update_buffer(t, FALSE),
+				  NULL, MAIL_TRANSACTION_HEADER_UPDATE);
 	}
 
-	if (ret == 0 && file->sync_offset < file->last_size) {
+	if (file->sync_offset < file->last_size) {
 		/* there is some garbage at the end of the transaction log
 		   (eg. previous write failed). remove it so reader doesn't
 		   break because of it. */
@@ -491,35 +475,22 @@
 		}
 	}
 
-	if (ret == 0 && file->first_append_size != 0) {
-		if (!MAIL_TRANSACTION_LOG_FILE_IN_MEMORY(file)) {
-			/* synced - rewrite first record's header */
-			ret = pwrite_full(file->fd, &file->first_append_size,
-					  sizeof(uint32_t), append_offset);
-			if (ret < 0) {
-				mail_index_file_set_syscall_error(index,
-					file->filepath, "pwrite()");
-			}
-		} else {
-			/* changed into in-memory buffer in the middle */
-			buffer_write(file->buffer,
-				     append_offset - file->buffer_offset,
-				     &file->first_append_size,
-				     sizeof(file->first_append_size));
-		}
+	append_offset = file->sync_offset;
+	if (log_buffer_write(&ctx) < 0) {
+		buffer_free(ctx.output);
+		return -1;
 	}
+	buffer_free(ctx.output);
 
-	if (t->hide_transaction && ret == 0) {
+	if (t->hide_transaction) {
 		/* mark the area covered by this transaction hidden */
 		mail_index_view_add_hidden_transaction(view, file->hdr.file_seq,
 			append_offset, file->sync_offset - append_offset);
 	}
-	if (ret < 0)
-		file->sync_offset = append_offset;
 
 	*log_file_seq_r = file->hdr.file_seq;
 	*log_file_offset_r = file->sync_offset;
-	return ret;
+	return 0;
 }
 
 int mail_transaction_log_append(struct mail_index_transaction *t,