List:Commits« Previous MessageNext Message »
From:vasil.dimov Date:May 24 2012 4:26pm
Subject:bzr push into mysql-trunk branch (vasil.dimov:3783 to 3784) Bug#13838962
View as plain text  
 3784 Vasil Dimov	2012-05-24
      Extend the fix for Bug#13838962 STATISTICS CALCULATION SKIPS DELETE-MARKED
      RECORDS AND CAUSES ASSERTION FAILURE
      
      Counting all delete marked records brings too much nondeterminism because
      purge may or may not have wiped them away when table and index statistics
      are being gathered.
      
      Instead skip delete marked records only on the leaf pages - that is, behave
      as if they do not exist. Delete marks on records on non-leaf pages are
      nonsensical and should not be looked up at all - that is, behave as if
      the non-leaf records do not _contain_ a delete-marked bit.

    modified:
      storage/innobase/dict/dict0stats.cc
      storage/innobase/include/page0page.h
      storage/innobase/include/page0page.ic
 3783 Nuno Carvalho	2012-05-24
      BUG#14021292 - 65152: MYSQL CAN'T START IF RELAY LOGS ARE REMOVED AND SKIP_SLAVE_START = 0
      
      Updated rpl.rpl_binlog_index to new slave start behaviour, when relay 
      log file name is changed directly on mysql.slave_relay_log_info table
      after a missing relay log file error a server restart is required.

    modified:
      mysql-test/suite/rpl/t/rpl_binlog_index.test
=== modified file 'storage/innobase/dict/dict0stats.cc'
--- a/storage/innobase/dict/dict0stats.cc	revid:nuno.carvalho@stripped
+++ b/storage/innobase/dict/dict0stats.cc	revid:vasil.dimov@stripped
@@ -623,6 +623,20 @@ dict_stats_analyze_index_level(
 			(*total_pages)++;
 		}
 
+		/* Skip delete-marked records on the leaf level. If we
+		do not skip them, then ANALYZE quickly after DELETE
+		could count them or not (purge may have already wiped
+		them away) which brings non-determinism. We skip only
+		leaf-level delete marks because delete marks on
+		non-leaf level do not make sense. */
+		if (level == 0 &&
+		    rec_get_deleted_flag(
+			    rec,
+			    page_is_comp(btr_pcur_get_page(&pcur)))) {
+
+			continue;
+		}
+
 		offsets_rec = rec_get_offsets(rec, index, offsets_rec_onstack,
 					      n_uniq, &heap);
 
@@ -680,7 +694,7 @@ dict_stats_analyze_index_level(
 				n_diff[i]++;
 			}
 		} else {
-			/* this is the first record */
+			/* this is the first non-delete marked record */
 			for (i = 1; i <= n_uniq; i++) {
 				n_diff[i] = 1;
 			}
@@ -712,7 +726,8 @@ dict_stats_analyze_index_level(
 
 	/* if *total_pages is left untouched then the above loop was not
 	entered at all and there is one page in the whole tree which is
-	empty */
+	empty or the loop was entered but this is level 0, contains one page
+	and all records are delete-marked */
 	if (*total_pages == 0) {
 
 		ut_ad(level == 0);
@@ -794,8 +809,10 @@ dict_stats_analyze_index_level(
 
 /* aux enum for controlling the behavior of dict_stats_scan_page() @{ */
 typedef enum page_scan_method_enum {
-	COUNT_ALL_NON_BORING,	/* scan all records on the given page
-				and count the number of distinct ones */
+	COUNT_ALL_NON_BORING_AND_SKIP_DEL_MARKED,/* scan all records on
+				the given page and count the number of
+				distinct ones, also ignore delete marked
+				records */
 	QUIT_ON_FIRST_NON_BORING/* quit when the first record that differs
 				from its right neighbor is found */
 } page_scan_method_t;
@@ -840,11 +857,18 @@ dict_stats_scan_page(
 	Because offsets1,offsets2 should be big enough,
 	this memory heap should never be used. */
 	mem_heap_t*	heap			= NULL;
+	const rec_t*	(*get_next)(const rec_t*);
+
+	if (scan_method == COUNT_ALL_NON_BORING_AND_SKIP_DEL_MARKED) {
+		get_next = page_rec_get_next_non_del_marked;
+	} else {
+		get_next = page_rec_get_next_const;
+	}
 
-	rec = page_rec_get_next_const(page_get_infimum_rec(page));
+	rec = get_next(page_get_infimum_rec(page));
 
 	if (page_rec_is_supremum(rec)) {
-		/* the page is empty */
+		/* the page is empty or contains only delete-marked records */
 		*n_diff = 0;
 		*out_rec = NULL;
 		return(NULL);
@@ -853,7 +877,7 @@ dict_stats_scan_page(
 	offsets_rec = rec_get_offsets(rec, index, offsets_rec,
 				      ULINT_UNDEFINED, &heap);
 
-	next_rec = page_rec_get_next_const(rec);
+	next_rec = get_next(rec);
 
 	*n_diff = 1;
 
@@ -902,7 +926,8 @@ dict_stats_scan_page(
 			offsets_rec = offsets_next_rec;
 			offsets_next_rec = offsets_tmp;
 		}
-		next_rec = page_rec_get_next_const(next_rec);
+
+		next_rec = get_next(next_rec);
 	}
 
 func_exit:
@@ -1032,14 +1057,7 @@ dict_stats_analyze_index_below_cur(
 
 	offsets_rec = dict_stats_scan_page(
 		&rec, offsets1, offsets2, index, page, n_prefix,
-		COUNT_ALL_NON_BORING, &n_diff);
-
-	if (root_height > 0) {
-
-		/* empty pages are allowed only if the whole B-tree is empty
-		and contains a single empty page */
-		ut_a(offsets_rec != NULL);
-	}
+		COUNT_ALL_NON_BORING_AND_SKIP_DEL_MARKED, &n_diff);
 
 #if 0
 	DEBUG_PRINTF("      %s(): n_diff below page_no=%lu: " UINT64PF "\n",
@@ -1262,9 +1280,10 @@ dict_stats_analyze_index_for_n_prefix(
 		n_diff_sum_of_all_analyzed_pages += n_diff_on_leaf_page;
 	}
 
-	if (n_diff_sum_of_all_analyzed_pages == 0) {
-		n_diff_sum_of_all_analyzed_pages = 1;
-	}
+	/* n_diff_sum_of_all_analyzed_pages can be 0 here if all the leaf
+	pages sampled contained only delete-marked records. In this case
+	we should assign 0 to index->stat_n_diff_key_vals[n_prefix], which
+	the formula below does. */
 
 	/* See REF01 for an explanation of the algorithm */
 	index->stat_n_diff_key_vals[n_prefix]

=== modified file 'storage/innobase/include/page0page.h'
--- a/storage/innobase/include/page0page.h	revid:nuno.carvalho@stripped
+++ b/storage/innobase/include/page0page.h	revid:vasil.dimov@stripped
@@ -551,6 +551,16 @@ page_rec_get_next_const(
 /*====================*/
 	const rec_t*	rec);	/*!< in: pointer to record */
 /************************************************************//**
+Gets the pointer to the next non delete-marked record on the page.
+If all subsequent records are delete-marked, then this function
+will return the supremum record.
+@return	pointer to next non delete-marked record or pointer to supremum */
+UNIV_INLINE
+const rec_t*
+page_rec_get_next_non_del_marked(
+/*=============================*/
+	const rec_t*	rec);	/*!< in: pointer to record */
+/************************************************************//**
 Sets the pointer to the next record on the page. */
 UNIV_INLINE
 void

=== modified file 'storage/innobase/include/page0page.ic'
--- a/storage/innobase/include/page0page.ic	revid:nuno.carvalho@stripped
+++ b/storage/innobase/include/page0page.ic	revid:vasil.dimov@stripped
@@ -776,6 +776,30 @@ page_rec_get_next_const(
 }
 
 /************************************************************//**
+Gets the pointer to the next non delete-marked record on the page.
+If all subsequent records are delete-marked, then this function
+will return the supremum record.
+@return	pointer to next non delete-marked record or pointer to supremum */
+UNIV_INLINE
+const rec_t*
+page_rec_get_next_non_del_marked(
+/*=============================*/
+	const rec_t*	rec)	/*!< in: pointer to record */
+{
+	const rec_t*	r;
+	ulint		page_is_compact = page_rec_is_comp(rec);
+
+	for (r = page_rec_get_next_const(rec);
+	     !page_rec_is_supremum(r)
+	     && rec_get_deleted_flag(r, page_is_compact);
+	     r = page_rec_get_next_const(r)) {
+		/* noop */
+	}
+
+	return(r);
+}
+
+/************************************************************//**
 Sets the pointer to the next record on the page. */
 UNIV_INLINE
 void

No bundle (reason: useless for push emails).
Thread
bzr push into mysql-trunk branch (vasil.dimov:3783 to 3784) Bug#13838962vasil.dimov27 May