changeset 7563:6de1aed24ce5 HEAD

Added mail_index_ext_reset_inc() to atomically increase extension's reset_id. Added clear_data parameter to mail_index_ext_reset*().
author Timo Sirainen <tss@iki.fi>
date Thu, 29 May 2008 04:47:53 +0300
parents 4c702defc245
children 4a9ce9df52c5
files src/lib-index/mail-cache-compress.c src/lib-index/mail-index-sync-ext.c src/lib-index/mail-index-sync-update.c src/lib-index/mail-index-transaction-private.h src/lib-index/mail-index-transaction.c src/lib-index/mail-index.h src/lib-index/mail-transaction-log-append.c src/lib-index/mail-transaction-log.h src/lib-storage/index/dbox/dbox-sync-rebuild.c
diffstat 9 files changed, 153 insertions(+), 46 deletions(-) [+]
line wrap: on
line diff
--- a/src/lib-index/mail-cache-compress.c	Wed May 28 19:03:42 2008 +0300
+++ b/src/lib-index/mail-cache-compress.c	Thu May 29 04:47:53 2008 +0300
@@ -414,7 +414,7 @@
 
 	/* once we're sure that the compression was successful,
 	   update the offsets */
-	mail_index_ext_reset(trans, cache->ext_id, file_seq);
+	mail_index_ext_reset(trans, cache->ext_id, file_seq, TRUE);
 	offsets = array_get(&ext_offsets, &count);
 	for (i = 0; i < count; i++) {
 		if (offsets[i] != 0) {
--- a/src/lib-index/mail-index-sync-ext.c	Wed May 28 19:03:42 2008 +0300
+++ b/src/lib-index/mail-index-sync-ext.c	Thu May 29 04:47:53 2008 +0300
@@ -424,7 +424,8 @@
 			ctx->cur_ext_ignore = FALSE;
 		} else {
 			/* extension was reset and this transaction hadn't
-			   yet seen it. ignore this update. */
+			   yet seen it. ignore this update (except for
+			   resets). */
 			ctx->cur_ext_ignore = TRUE;
 		}
 
@@ -476,31 +477,13 @@
 	return 1;
 }
 
-int mail_index_sync_ext_reset(struct mail_index_sync_map_ctx *ctx,
-			      const struct mail_transaction_ext_reset *u)
+static void mail_index_sync_ext_clear(struct mail_index_view *view,
+				      struct mail_index_map *map,
+				      struct mail_index_ext *ext)
 {
-	struct mail_index_view *view = ctx->view;
-	struct mail_index_map *map = view->map;
-	struct mail_index_ext_header *ext_hdr;
-        struct mail_index_ext *ext;
 	struct mail_index_record *rec;
 	uint32_t i;
 
-	if (ctx->cur_ext_map_idx == (uint32_t)-1) {
-		mail_index_sync_set_corrupted(ctx,
-			"Extension reset without intro prefix");
-		return -1;
-	}
-	if (ctx->cur_ext_ignore)
-		return 1;
-
-	/* a new index file will be created, so the old data won't be
-	   accidentally used by other processes. */
-	map = mail_index_sync_get_atomic_map(ctx);
-
-	ext = array_idx_modifiable(&map->extensions, ctx->cur_ext_map_idx);
-	ext->reset_id = u->new_reset_id;
-
 	memset(buffer_get_space_unsafe(map->hdr_copy_buf, ext->hdr_offset,
 				       ext->hdr_size), 0, ext->hdr_size);
 	map->hdr_base = map->hdr_copy_buf->data;
@@ -512,10 +495,34 @@
 	}
 	map->rec_map->write_seq_first = 1;
 	map->rec_map->write_seq_last = view->map->rec_map->records_count;
+}
+
+int mail_index_sync_ext_reset(struct mail_index_sync_map_ctx *ctx,
+			      const struct mail_transaction_ext_reset *u)
+{
+	struct mail_index_map *map = ctx->view->map;
+	struct mail_index_ext_header *ext_hdr;
+        struct mail_index_ext *ext;
+
+	if (ctx->cur_ext_map_idx == (uint32_t)-1) {
+		mail_index_sync_set_corrupted(ctx,
+			"Extension reset without intro prefix");
+		return -1;
+	}
+	/* since we're resetting the extension, don't check cur_ext_ignore */
+
+	/* a new index file will be created, so the old data won't be
+	   accidentally used by other processes. */
+	map = mail_index_sync_get_atomic_map(ctx);
+
+	ext = array_idx_modifiable(&map->extensions, ctx->cur_ext_map_idx);
+	ext->reset_id = u->new_reset_id;
+
+	if (!u->preserve_data)
+		mail_index_sync_ext_clear(ctx->view, map, ext);
 
 	ext_hdr = get_ext_header(map, ext);
 	ext_hdr->reset_id = u->new_reset_id;
-
 	return 1;
 }
 
--- a/src/lib-index/mail-index-sync-update.c	Wed May 28 19:03:42 2008 +0300
+++ b/src/lib-index/mail-index-sync-update.c	Thu May 29 04:47:53 2008 +0300
@@ -532,15 +532,17 @@
 		break;
 	}
 	case MAIL_TRANSACTION_EXT_RESET: {
-		const struct mail_transaction_ext_reset *rec = data;
+		struct mail_transaction_ext_reset rec;
 
-		if (hdr->size != sizeof(*rec)) {
+		/* old versions have only new_reset_id */
+		if (hdr->size < sizeof(uint32_t)) {
 			mail_index_sync_set_corrupted(ctx,
 				"ext reset: invalid record size");
 			ret = -1;
 			break;
 		}
-		ret = mail_index_sync_ext_reset(ctx, rec);
+		memcpy(&rec, data, I_MIN(hdr->size, sizeof(rec)));
+		ret = mail_index_sync_ext_reset(ctx, &rec);
 		break;
 	}
 	case MAIL_TRANSACTION_EXT_HDR_UPDATE: {
@@ -781,6 +783,9 @@
 		map->hdr_base = map->hdr_copy_buf->data;
 	}
 
+	mail_transaction_log_view_get_prev_pos(view->log_view,
+					       &prev_seq, &prev_offset);
+
 	mail_index_sync_map_init(&sync_map_ctx, view, type);
 	if (reset) {
 		/* Reset the entire index. Leave only indexid and
--- a/src/lib-index/mail-index-transaction-private.h	Wed May 28 19:03:42 2008 +0300
+++ b/src/lib-index/mail-index-transaction-private.h	Thu May 29 04:47:53 2008 +0300
@@ -51,8 +51,9 @@
 		     struct mail_index_transaction_ext_hdr_update *);
 	ARRAY_DEFINE(ext_rec_updates, ARRAY_TYPE(seq_array));
 	ARRAY_DEFINE(ext_resizes, struct mail_transaction_ext_intro);
-	ARRAY_DEFINE(ext_resets, uint32_t);
+	ARRAY_DEFINE(ext_resets, struct mail_transaction_ext_reset);
 	ARRAY_DEFINE(ext_reset_ids, uint32_t);
+	ARRAY_DEFINE(ext_reset_atomic, uint32_t);
 
 	ARRAY_DEFINE(keyword_updates,
 		     struct mail_index_transaction_keyword_update);
--- a/src/lib-index/mail-index-transaction.c	Wed May 28 19:03:42 2008 +0300
+++ b/src/lib-index/mail-index-transaction.c	Thu May 29 04:47:53 2008 +0300
@@ -75,6 +75,8 @@
 		array_free(&t->ext_resets);
 	if (array_is_created(&t->ext_reset_ids))
 		array_free(&t->ext_reset_ids);
+	if (array_is_created(&t->ext_reset_atomic))
+		array_free(&t->ext_reset_atomic);
 
 	t->first_new_seq = mail_index_view_get_messages_count(t->view)+1;
 	t->last_new_seq = 0;
@@ -1087,18 +1089,36 @@
 }
 
 void mail_index_ext_reset(struct mail_index_transaction *t, uint32_t ext_id,
-			  uint32_t reset_id)
+			  uint32_t reset_id, bool clear_data)
 {
+	struct mail_transaction_ext_reset reset;
+
 	i_assert(reset_id != 0);
 
+	memset(&reset, 0, sizeof(reset));
+	reset.new_reset_id = reset_id;
+	reset.preserve_data = !clear_data;
+
 	mail_index_ext_set_reset_id(t, ext_id, reset_id);
 
 	if (!array_is_created(&t->ext_resets))
 		i_array_init(&t->ext_resets, ext_id + 2);
-	array_idx_set(&t->ext_resets, ext_id, &reset_id);
+	array_idx_set(&t->ext_resets, ext_id, &reset);
 	t->log_ext_updates = TRUE;
 }
 
+void mail_index_ext_reset_inc(struct mail_index_transaction *t, uint32_t ext_id,
+			      uint32_t prev_reset_id, bool clear_data)
+{
+	uint32_t expected_reset_id = prev_reset_id + 1;
+
+	mail_index_ext_reset(t, ext_id, (uint32_t)-1, clear_data);
+
+	if (!array_is_created(&t->ext_reset_atomic))
+		i_array_init(&t->ext_reset_atomic, ext_id + 2);
+	array_idx_set(&t->ext_reset_atomic, ext_id, &expected_reset_id);
+}
+
 static bool
 mail_index_transaction_has_ext_changes(struct mail_index_transaction *t)
 {
@@ -1123,11 +1143,11 @@
 		}
 	}
 	if (array_is_created(&t->ext_resets)) {
-		const uint32_t *ids;
+		const struct mail_transaction_ext_reset *resets;
 
-		ids = array_get(&t->ext_resets, &count);
+		resets = array_get(&t->ext_resets, &count);
 		for (i = 0; i < count; i++) {
-			if (ids[i] != 0)
+			if (resets[i].new_reset_id != 0)
 				return TRUE;
 		}
 	}
--- a/src/lib-index/mail-index.h	Wed May 28 19:03:42 2008 +0300
+++ b/src/lib-index/mail-index.h	Thu May 29 04:47:53 2008 +0300
@@ -428,14 +428,21 @@
 			   uint32_t hdr_size, uint16_t record_size,
 			   uint16_t record_align);
 
-/* Reset extension records and header. Any updates for this extension which
-   were issued before the writer had seen this reset are discarded. reset_id is
-   used to figure this out, so it must be different every time. */
+/* Reset extension. Any updates for this extension which were issued before the
+   writer had seen this reset are discarded. reset_id is used to figure this
+   out, so it must be different every time. If clear_data=TRUE, records and
+   header is zeroed. */
 void mail_index_ext_reset(struct mail_index_transaction *t, uint32_t ext_id,
-			  uint32_t reset_id);
-/* Discard existing extension updates and write new updates using the given
-   reset_id. The difference to mail_index_ext_reset() is that this doesn't
-   clear any existing record or header data. */
+			  uint32_t reset_id, bool clear_data);
+/* Like mail_index_ext_reset(), but increase extension's reset_id atomically
+   when the transaction is being committed. If prev_reset_id doesn't match the
+   latest reset_id, the reset_id isn't increased and all extension changes are
+   ignored. */
+void mail_index_ext_reset_inc(struct mail_index_transaction *t, uint32_t ext_id,
+			      uint32_t prev_reset_id, bool clear_data);
+/* Discard existing extension updates in this transaction and write new updates
+   using the given reset_id. The difference to mail_index_ext_reset() is that
+   this doesn't clear any existing record or header data. */
 void mail_index_ext_set_reset_id(struct mail_index_transaction *t,
 				 uint32_t ext_id, uint32_t reset_id);
 /* Get the current reset_id for given extension. Returns TRUE if it exists. */
--- a/src/lib-index/mail-transaction-log-append.c	Wed May 28 19:03:42 2008 +0300
+++ b/src/lib-index/mail-transaction-log-append.c	Thu May 29 04:47:53 2008 +0300
@@ -166,6 +166,58 @@
 	return buf;
 }
 
+static void
+ext_reset_update_atomic(struct mail_index_transaction *t,
+			uint32_t ext_id, uint32_t expected_reset_id)
+{
+	const struct mail_index_ext *map_ext;
+	struct mail_transaction_ext_reset *reset;
+	uint32_t idx, reset_id;
+
+	if (!mail_index_map_get_ext_idx(t->view->index->map, ext_id, &idx)) {
+		/* new extension */
+		reset_id = 1;
+	} else {
+		map_ext = array_idx(&t->view->index->map->extensions, idx);
+		reset_id = map_ext->reset_id + 1;
+	}
+	if (reset_id != expected_reset_id) {
+		/* ignore this extension update */
+		mail_index_ext_set_reset_id(t, ext_id, 0);
+		return;
+	}
+
+	if (reset_id == 0)
+		reset_id++;
+
+	array_idx_set(&t->ext_reset_ids, ext_id, &reset_id);
+
+	/* reseting existing data is optional */
+	if (array_is_created(&t->ext_resets)) {
+		reset = array_idx_modifiable(&t->ext_resets, ext_id);
+		if (reset->new_reset_id == (uint32_t)-1)
+			reset->new_reset_id = reset_id;
+	}
+}
+
+static void
+transaction_update_atomic_reset_ids(struct mail_index_transaction *t)
+{
+	const uint32_t *expected_reset_ids;
+	unsigned int ext_id, count;
+
+	if (!array_is_created(&t->ext_reset_atomic))
+		return;
+
+	expected_reset_ids = array_get(&t->ext_reset_atomic, &count);
+	for (ext_id = 0; ext_id < count; ext_id++) {
+		if (expected_reset_ids[ext_id] != 0) {
+			ext_reset_update_atomic(t, ext_id,
+						expected_reset_ids[ext_id]);
+		}
+	}
+}
+
 static void log_append_ext_intro(struct log_append_context *ctx,
 				 uint32_t ext_id, uint32_t reset_id)
 {
@@ -257,7 +309,8 @@
 	unsigned int update_count, resize_count, ext_count = 0;
 	unsigned int hdrs_count, reset_id_count, reset_count;
 	uint32_t ext_id, reset_id;
-	const uint32_t *reset_ids, *reset;
+	const struct mail_transaction_ext_reset *reset;
+	const uint32_t *reset_ids;
 	const ARRAY_TYPE(seq_array) *update;
 	buffer_t *buf;
 
@@ -304,15 +357,15 @@
 	}
 
 	memset(&ext_reset, 0, sizeof(ext_reset));
-
 	buf = buffer_create_data(pool_datastack_create(),
 				 &ext_reset, sizeof(ext_reset));
 	buffer_set_used_size(buf, sizeof(ext_reset));
 
 	for (ext_id = 0; ext_id < ext_count; ext_id++) {
-		ext_reset.new_reset_id =
-			ext_id < reset_count && reset[ext_id] != 0 ?
-			reset[ext_id] : 0;
+		if (ext_id < reset_count)
+			ext_reset = reset[ext_id];
+		else
+			ext_reset.new_reset_id = 0;
 		if ((ext_id < resize_count && resize[ext_id].name_size) ||
 		    (ext_id < update_count &&
 		     array_is_created(&update[ext_id])) ||
@@ -499,6 +552,18 @@
 			return -1;
 	}
 
+	if (array_is_created(&t->ext_reset_atomic)) {
+		if (mail_index_map(t->view->index,
+				   MAIL_INDEX_SYNC_HANDLER_HEAD) <= 0)
+			return -1;
+		transaction_update_atomic_reset_ids(t);
+
+		if (!TRANSACTION_HAS_CHANGES(t)) {
+			/* we aborted the ext changes, nothing else to do */
+			return 0;
+		}
+	}
+
 	file = log->head;
 
 	if (file->sync_offset < file->buffer_offset)
--- a/src/lib-index/mail-transaction-log.h	Wed May 28 19:03:42 2008 +0300
+++ b/src/lib-index/mail-transaction-log.h	Thu May 29 04:47:53 2008 +0300
@@ -95,6 +95,8 @@
 
 struct mail_transaction_ext_reset {
 	uint32_t new_reset_id;
+	uint8_t preserve_data;
+	uint8_t unused_padding[3];
 };
 
 /* these are set for the last ext_intro */
--- a/src/lib-storage/index/dbox/dbox-sync-rebuild.c	Wed May 28 19:03:42 2008 +0300
+++ b/src/lib-storage/index/dbox/dbox-sync-rebuild.c	Thu May 29 04:47:53 2008 +0300
@@ -70,7 +70,7 @@
 		ctx->cache_used = TRUE;
 		ctx->cache_reset_id = reset_id;
 		mail_index_ext_reset(ctx->trans, ctx->cache_ext_id,
-				     ctx->cache_reset_id);
+				     ctx->cache_reset_id, TRUE);
 	}
 	if (ctx->cache_reset_id == reset_id) {
 		mail_index_update_ext(ctx->trans, new_seq,