/* We know that the writes have been flushed to disk now
and in recovery we will find them in the doublewrite buffer
-@@ -1378,7 +1378,7 @@
+@@ -1375,10 +1375,11 @@
+ ulint high;
+ ulint count = 0;
+ buf_pool_t* buf_pool = buf_pool_get(space, offset);
++ ibool is_forward_scan;
ut_ad(flush_type == BUF_FLUSH_LRU || flush_type == BUF_FLUSH_LIST);
/* If there is little space, it is better not to flush
any block except from the end of the LRU list */
+@@ -1405,7 +1406,32 @@
+ high = fil_space_get_size(space);
+ }
+
+- for (i = low; i < high; i++) {
++ if (srv_flush_neighbor_pages == 2) {
++
++ /* In the case of contiguous flush where the requested page
++ does not fall at the start of flush area, first scan backward
++ from the page and later forward from it. */
++ is_forward_scan = (offset == low);
++ }
++ else {
++ is_forward_scan = TRUE;
++ }
++
++scan:
++ if (srv_flush_neighbor_pages == 2) {
++ if (is_forward_scan) {
++ i = offset;
++ }
++ else {
++ i = offset - 1;
++ }
++ }
++ else {
++ i = low;
++ }
++
++ for (; is_forward_scan ? (i < high) : (i >= low);
++ is_forward_scan ? i++ : i--) {
+
+ buf_page_t* bpage;
+
+@@ -1434,6 +1460,12 @@
+ if (!bpage) {
+
+ buf_pool_mutex_exit(buf_pool);
++ if (srv_flush_neighbor_pages == 2) {
++
++ /* This is contiguous neighbor page flush and
++ the pages here are not contiguous. */
++ break;
++ }
+ continue;
+ }
+
+@@ -1470,6 +1502,22 @@
+ }
+ }
+ buf_pool_mutex_exit(buf_pool);
++
++ if (srv_flush_neighbor_pages == 2) {
++
++ /* We are trying to do the contiguous neighbor page
++ flush, but the last page we checked was unflushable,
++ making a "hole" in the flush, so stop this attempt. */
++ break;
++ }
++ }
++
++ if (!is_forward_scan) {
++
++ /* Backward scan done, now do the forward scan */
++ ut_a (srv_flush_neighbor_pages == 2);
++ is_forward_scan = TRUE;
++ goto scan;
+ }
+
+ return(count);
--- a/storage/innobase/buf/buf0rea.c
+++ b/storage/innobase/buf/buf0rea.c
-@@ -260,6 +260,10 @@
- = BUF_READ_AHEAD_LINEAR_AREA(buf_pool);
+@@ -427,6 +427,10 @@
+ = BUF_READ_AHEAD_AREA(buf_pool);
ulint threshold;
+ if (!(srv_read_ahead & 2)) {
return(0);
--- a/storage/innobase/fil/fil0fil.c
+++ b/storage/innobase/fil/fil0fil.c
-@@ -2601,7 +2601,7 @@
+@@ -2609,7 +2609,7 @@
os_thread_sleep(20000);
goto retry;
-@@ -2815,7 +2815,7 @@
+@@ -2823,7 +2823,7 @@
goto error_exit;
}
if (!ret) {
fputs("InnoDB: Error: file flush of tablespace ", stderr);
-@@ -3001,7 +3001,7 @@
+@@ -3009,7 +3009,7 @@
}
}
if (!success) {
goto func_exit;
-@@ -3023,7 +3023,7 @@
+@@ -3031,7 +3031,7 @@
goto func_exit;
}
func_exit:
os_file_close(file);
ut_free(buf2);
-@@ -4006,7 +4006,7 @@
+@@ -4014,7 +4014,7 @@
size_after_extend, *actual_size); */
mutex_exit(&fil_system->mutex);
return(success);
}
-@@ -4577,8 +4577,9 @@
+@@ -4585,8 +4585,9 @@
void
fil_flush(
/*======*/
{
fil_space_t* space;
fil_node_t* node;
-@@ -4649,7 +4650,7 @@
+@@ -4657,7 +4658,7 @@
/* fprintf(stderr, "Flushing to file %s\n",
node->name); */
mutex_enter(&fil_system->mutex);
-@@ -4732,7 +4733,7 @@
+@@ -4740,7 +4741,7 @@
a non-existing space id. */
for (i = 0; i < n_space_ids; i++) {
static handler *innobase_create_handler(handlerton *hton,
TABLE_SHARE *table,
-@@ -839,6 +845,17 @@
+@@ -841,6 +847,17 @@
}
}
/********************************************************************//**
Obtain the InnoDB transaction of a MySQL thread.
@return reference to transaction pointer */
-@@ -2442,6 +2459,9 @@
+@@ -2471,6 +2488,9 @@
srv_n_read_io_threads = (ulint) innobase_read_io_threads;
srv_n_write_io_threads = (ulint) innobase_write_io_threads;
srv_force_recovery = (ulint) innobase_force_recovery;
srv_use_doublewrite_buf = (ibool) innobase_use_doublewrite;
-@@ -11036,7 +11056,7 @@
+@@ -11141,7 +11161,7 @@
PLUGIN_VAR_OPCMDARG | PLUGIN_VAR_READONLY,
"Purge threads can be either 0 or 1.",
NULL, NULL,
0, /* Minimum value */
1, 0); /* Maximum value */
-@@ -11078,12 +11098,18 @@
+@@ -11183,12 +11203,18 @@
innodb_file_format_max_validate,
innodb_file_format_max_update, "Antelope");
static MYSQL_SYSVAR_STR(flush_method, innobase_file_flush_method,
PLUGIN_VAR_RQCMDARG | PLUGIN_VAR_READONLY,
-@@ -11183,7 +11209,7 @@
+@@ -11293,7 +11319,7 @@
static MYSQL_SYSVAR_LONGLONG(buffer_pool_size, innobase_buffer_pool_size,
PLUGIN_VAR_RQCMDARG | PLUGIN_VAR_READONLY,
"The size of the memory buffer InnoDB uses to cache data and indexes of its tables.",
static MYSQL_SYSVAR_LONG(buffer_pool_instances, innobase_buffer_pool_instances,
PLUGIN_VAR_RQCMDARG | PLUGIN_VAR_READONLY,
-@@ -11335,6 +11361,95 @@
+@@ -11442,6 +11468,127 @@
"trigger a readahead.",
NULL, NULL, 56, 0, 64, 0);
+ "Control soft limit of checkpoint age. (0 : not control)",
+ NULL, NULL, 0, 0, ~0UL, 0);
+
-+static MYSQL_SYSVAR_ULONG(flush_neighbor_pages, srv_flush_neighbor_pages,
-+ PLUGIN_VAR_RQCMDARG,
-+ "Enable/Disable flushing also neighbor pages. 0:disable 1:enable",
-+ NULL, NULL, 1, 0, 1, 0);
++static
++void
++innodb_flush_neighbor_pages_update(
++ THD* thd,
++ struct st_mysql_sys_var* var,
++ void* var_ptr,
++ const void* save)
++{
++ *(long *)var_ptr = (*(long *)save) % 3;
++}
++
++const char *flush_neighbor_pages_names[]=
++{
++ "none", /* 0 */
++ "area",
++ "cont", /* 2 */
++ /* For compatibility with the older patch */
++ "0", /* "none" + 3 */
++ "1", /* "area" + 3 */
++ "2", /* "cont" + 3 */
++ NullS
++};
++
++TYPELIB flush_neighbor_pages_typelib=
++{
++ array_elements(flush_neighbor_pages_names) - 1,
++ "flush_neighbor_pages_typelib",
++ flush_neighbor_pages_names,
++ NULL
++};
++
++static MYSQL_SYSVAR_ENUM(flush_neighbor_pages, srv_flush_neighbor_pages,
++ PLUGIN_VAR_RQCMDARG, "Neighbor page flushing behaviour: none: do not flush, "
++ "[area]: flush selected pages one-by-one, "
++ "cont: flush a contiguous block of pages", NULL,
++ innodb_flush_neighbor_pages_update, 1, &flush_neighbor_pages_typelib);
+
+static
+void
static struct st_mysql_sys_var* innobase_system_variables[]= {
MYSQL_SYSVAR(additional_mem_pool_size),
MYSQL_SYSVAR(autoextend_increment),
-@@ -11355,6 +11470,7 @@
+@@ -11462,6 +11609,7 @@
MYSQL_SYSVAR(file_format_check),
MYSQL_SYSVAR(file_format_max),
MYSQL_SYSVAR(flush_log_at_trx_commit),
MYSQL_SYSVAR(flush_method),
MYSQL_SYSVAR(force_recovery),
MYSQL_SYSVAR(large_prefix),
-@@ -11393,6 +11509,13 @@
+@@ -11501,6 +11649,13 @@
MYSQL_SYSVAR(show_verbose_locks),
MYSQL_SYSVAR(show_locks_held),
MYSQL_SYSVAR(version),
MYSQL_SYSVAR(change_buffering),
--- a/storage/innobase/ibuf/ibuf0ibuf.c
+++ b/storage/innobase/ibuf/ibuf0ibuf.c
-@@ -514,8 +514,10 @@
+@@ -523,8 +523,10 @@
grow in size, as the references on the upper levels of the tree can
change */
mutex_create(ibuf_pessimistic_insert_mutex_key,
&ibuf_pessimistic_insert_mutex,
-@@ -2753,9 +2755,11 @@
+@@ -2763,9 +2765,11 @@
size = ibuf->size;
max_size = ibuf->max_size;
--- a/storage/innobase/include/buf0rea.h
+++ b/storage/innobase/include/buf0rea.h
-@@ -124,8 +124,7 @@
+@@ -149,8 +149,7 @@
/** The size in pages of the area which the read-ahead algorithms read if
invoked */
/** read only pages belonging to the insert buffer tree */
--- a/storage/innobase/include/fil0fil.h
+++ b/storage/innobase/include/fil0fil.h
-@@ -658,8 +658,9 @@
+@@ -663,8 +663,9 @@
void
fil_flush(
/*======*/
+extern char srv_use_global_flush_log_at_trx_commit;
extern char srv_adaptive_flushing;
-
-@@ -216,6 +217,16 @@
+ /* If this flag is TRUE, then we will load the indexes' (and tables') metadata
+@@ -221,6 +222,16 @@
extern ulong srv_max_purge_lag;
extern ulong srv_replication_delay;
/*-------------------------------------------*/
extern ulint srv_n_rows_inserted;
-@@ -394,8 +405,9 @@
+@@ -399,8 +410,9 @@
when writing data files, but do flush
after writing to log files */
SRV_UNIX_NOSYNC, /*!< do not flush after writing */
/* Try to flush dirty pages so as to avoid IO bursts at
the checkpoints. */
-@@ -402,6 +403,17 @@
+@@ -404,6 +405,17 @@
UNIV_INTERN ulong srv_replication_delay = 0;
+#define PCT_IBUF_IO(pct) ((ulint) (srv_io_capacity * srv_ibuf_accel_rate * ((double) pct / 10000.0)))
+
+UNIV_INTERN ulint srv_checkpoint_age_target = 0;
-+UNIV_INTERN ulint srv_flush_neighbor_pages = 1; /* 0:disable 1:enable */
++UNIV_INTERN ulint srv_flush_neighbor_pages = 1; /* 0:disable 1:area 2:contiguous */
+
+UNIV_INTERN ulint srv_enable_unsafe_group_commit = 0; /* 0:disable 1:enable */
+UNIV_INTERN ulint srv_read_ahead = 3; /* 1: random 2: linear 3: Both */
/*-------------------------------------------*/
UNIV_INTERN ulong srv_n_spin_wait_rounds = 30;
UNIV_INTERN ulong srv_n_free_tickets_to_enter = 500;
-@@ -2709,7 +2721,7 @@
+@@ -2713,7 +2725,7 @@
ut_ad(!mutex_own(&kernel_mutex));
do {
/* Check for shutdown and change in purge config. */
-@@ -2742,6 +2754,7 @@
+@@ -2746,6 +2758,7 @@
ulint n_pages_purged = 0;
ulint n_bytes_merged;
ulint n_pages_flushed;
ulint n_bytes_archived;
ulint n_tables_to_drop;
ulint n_ios;
-@@ -2749,7 +2762,20 @@
+@@ -2753,7 +2766,20 @@
ulint n_ios_very_old;
ulint n_pend_ios;
ulint next_itr_time;
#ifdef UNIV_DEBUG_THREAD_CREATION
fprintf(stderr, "Master thread starts, id %lu\n",
-@@ -2771,6 +2797,9 @@
+@@ -2775,6 +2801,9 @@
mutex_exit(&kernel_mutex);
loop:
/*****************************************************************/
/* ---- When there is database activity by users, we cycle in this
-@@ -2801,9 +2830,13 @@
+@@ -2805,9 +2834,13 @@
/* Sleep for 1 second on entrying the for loop below the first time. */
next_itr_time = ut_time_ms() + 1000;
/* ALTER TABLE in MySQL requires on Unix that the table handler
can drop tables lazily after there no longer are SELECT
queries to them. */
-@@ -2827,6 +2860,7 @@
+@@ -2831,6 +2864,7 @@
srv_main_thread_op_info = "sleeping";
srv_main_1_second_loops++;
if (next_itr_time > cur_time
&& srv_shutdown_state == SRV_SHUTDOWN_NONE) {
-@@ -2837,10 +2871,26 @@
+@@ -2841,10 +2875,26 @@
(next_itr_time - cur_time)
* 1000));
srv_main_sleeps++;
/* Flush logs if needed */
srv_sync_log_buffer_in_background();
-@@ -2860,7 +2910,7 @@
+@@ -2864,7 +2914,7 @@
if (n_pend_ios < SRV_PEND_IO_THRESHOLD
&& (n_ios - n_ios_old < SRV_RECENT_IO_ACTIVITY)) {
srv_main_thread_op_info = "doing insert buffer merge";
/* Flush logs if needed */
srv_sync_log_buffer_in_background();
-@@ -2877,7 +2927,11 @@
+@@ -2881,7 +2931,11 @@
n_pages_flushed = buf_flush_list(
PCT_IO(100), IB_ULONGLONG_MAX);
/* Try to keep the rate of flushing of dirty
pages such that redo log generation does not
-@@ -2893,6 +2947,224 @@
+@@ -2897,6 +2951,224 @@
n_flush,
IB_ULONGLONG_MAX);
}
}
if (srv_activity_count == old_activity_count) {
-@@ -2941,12 +3213,12 @@
+@@ -2945,12 +3217,12 @@
even if the server were active */
srv_main_thread_op_info = "doing insert buffer merge";
srv_main_thread_op_info = "master purging";
srv_master_do_purge();
-@@ -3024,7 +3296,7 @@
+@@ -3028,7 +3300,7 @@
}
}
srv_main_thread_op_info = "master purging";
srv_master_do_purge();
-@@ -3049,7 +3321,7 @@
+@@ -3053,7 +3325,7 @@
buf_flush_list below. Otherwise, the system favors
clean pages over cleanup throughput. */
n_bytes_merged = ibuf_contract_for_n_pages(FALSE,
}
srv_main_thread_op_info = "reserving kernel mutex";
-@@ -3189,6 +3461,7 @@
+@@ -3193,6 +3465,7 @@
srv_slot_t* slot;
ulint retries = 0;
ulint n_total_purged = ULINT_UNDEFINED;
ut_a(srv_n_purge_threads == 1);
-@@ -3209,9 +3482,12 @@
+@@ -3213,9 +3486,12 @@
mutex_exit(&kernel_mutex);
/* If there are very few records to purge or the last
purge didn't purge any records then wait for activity.
-@@ -3258,6 +3534,16 @@
+@@ -3262,6 +3538,16 @@
} while (n_pages_purged > 0 && !srv_fast_shutdown);
srv_sync_log_buffer_in_background();
mutex_enter(&kernel_mutex);
--- a/storage/innobase/srv/srv0start.c
+++ b/storage/innobase/srv/srv0start.c
-@@ -1217,6 +1217,9 @@
+@@ -1237,6 +1237,9 @@
} else if (0 == ut_strcmp(srv_file_flush_method_str, "O_DIRECT")) {
srv_unix_file_flush_method = SRV_UNIX_O_DIRECT;
/* Write the log but do not flush it to disk */
+--- a/mysql-test/include/default_mysqld.cnf
++++ b/mysql-test/include/default_mysqld.cnf
+@@ -29,7 +29,7 @@
+ max_heap_table_size= 1M
+
+ loose-innodb_data_file_path= ibdata1:10M:autoextend
+-loose-innodb_buffer_pool_size= 8M
++loose-innodb_buffer_pool_size= 32M
+ loose-innodb_write_io_threads= 2
+ loose-innodb_read_io_threads= 2
+ loose-innodb_log_buffer_size= 1M
+--- a/mysql-test/suite/innodb/r/innodb.result
++++ b/mysql-test/suite/innodb/r/innodb.result
+@@ -1678,7 +1678,7 @@
+ drop table t1;
+ SELECT variable_value FROM information_schema.global_status WHERE LOWER(variable_name) = 'innodb_buffer_pool_pages_total';
+ variable_value
+-511
++2047
+ SELECT variable_value FROM information_schema.global_status WHERE LOWER(variable_name) = 'innodb_page_size';
+ variable_value
+ 16384
+--- /dev/null
++++ b/mysql-test/suite/innodb/r/percona_flush_contiguous_neighbors.result
+@@ -0,0 +1,21 @@
++DROP TABLE IF EXISTS t1;
++CREATE TABLE t1 (id INT AUTO_INCREMENT, foo CHAR(255), PRIMARY KEY (id)) ENGINE=InnoDB;
++INSERT INTO t1(foo) VALUES ('a'), ('b');
++INSERT INTO t1(foo) SELECT foo FROM t1;
++INSERT INTO t1(foo) SELECT foo FROM t1;
++INSERT INTO t1(foo) SELECT foo FROM t1;
++INSERT INTO t1(foo) SELECT foo FROM t1;
++INSERT INTO t1(foo) SELECT foo FROM t1;
++INSERT INTO t1(foo) SELECT foo FROM t1;
++INSERT INTO t1(foo) SELECT foo FROM t1;
++INSERT INTO t1(foo) SELECT foo FROM t1;
++INSERT INTO t1(foo) SELECT foo FROM t1;
++INSERT INTO t1(foo) SELECT foo FROM t1;
++INSERT INTO t1(foo) SELECT foo FROM t1;
++INSERT INTO t1(foo) SELECT foo FROM t1;
++INSERT INTO t1(foo) SELECT foo FROM t1;
++INSERT INTO t1(foo) SELECT foo FROM t1;
++INSERT INTO t1(foo) SELECT foo FROM t1;
++INSERT INTO t1(foo) SELECT foo FROM t1;
++INSERT INTO t1(foo) SELECT foo FROM t1;
++DROP TABLE t1;
+--- /dev/null
++++ b/mysql-test/suite/innodb/t/percona_flush_contiguous_neighbors-master.opt
+@@ -0,0 +1 @@
++--innodb_flush_neighbor_pages=cont
+--- /dev/null
++++ b/mysql-test/suite/innodb/t/percona_flush_contiguous_neighbors.test
+@@ -0,0 +1,36 @@
++# Test for innodb_flush_neighbor_pages=contiguous.
++# The test is very crude: we simply overflow the buffer pool with such a number of
++# new/modified pages that some flushing is bound to happen.
++
++--source include/have_innodb.inc
++
++--disable_warnings
++DROP TABLE IF EXISTS t1;
++--enable_warnings
++
++CREATE TABLE t1 (id INT AUTO_INCREMENT, foo CHAR(255), PRIMARY KEY (id)) ENGINE=InnoDB;
++
++INSERT INTO t1(foo) VALUES ('a'), ('b');
++INSERT INTO t1(foo) SELECT foo FROM t1;
++INSERT INTO t1(foo) SELECT foo FROM t1;
++INSERT INTO t1(foo) SELECT foo FROM t1;
++INSERT INTO t1(foo) SELECT foo FROM t1;
++INSERT INTO t1(foo) SELECT foo FROM t1;
++INSERT INTO t1(foo) SELECT foo FROM t1;
++INSERT INTO t1(foo) SELECT foo FROM t1;
++INSERT INTO t1(foo) SELECT foo FROM t1;
++INSERT INTO t1(foo) SELECT foo FROM t1;
++INSERT INTO t1(foo) SELECT foo FROM t1;
++INSERT INTO t1(foo) SELECT foo FROM t1;
++INSERT INTO t1(foo) SELECT foo FROM t1;
++INSERT INTO t1(foo) SELECT foo FROM t1;
++INSERT INTO t1(foo) SELECT foo FROM t1;
++INSERT INTO t1(foo) SELECT foo FROM t1;
++INSERT INTO t1(foo) SELECT foo FROM t1;
++INSERT INTO t1(foo) SELECT foo FROM t1;
++
++# TODO: cannot record a stable value here. A check of > 0 should be enough,
++# but the variable is not accessible through INFORMATION_SCHEMA currently.
++# SHOW GLOBAL STATUS LIKE 'Innodb_buffer_pool_pages_flushed';
++
++DROP TABLE t1;
+--- a/mysql-test/suite/innodb/t/innodb_cmp_drop_table-master.opt
++++ b/mysql-test/suite/innodb/t/innodb_cmp_drop_table-master.opt
+@@ -1 +1 @@
+---innodb-buffer-pool-size=8M
++--innodb-buffer-pool-size=32M
+--- a/mysql-test/suite/innodb/t/innodb_cmp_drop_table.test
++++ b/mysql-test/suite/innodb/t/innodb_cmp_drop_table.test
+@@ -36,13 +36,14 @@
+
+ -- disable_query_log
+
+--- let $i = 400
++-- let $i = 4000
++begin;
+ while ($i)
+ {
+ insert into t2 values(repeat('abcdefghijklmnopqrstuvwxyz',1000));
+ dec $i;
+ }
+-
++commit;
+ -- enable_query_log
+
+ # now there should be no 8K pages in the buffer pool