Skip to content

Commit

Permalink
PS-9222 Include reordered fields when calculating mlog buffer size
Browse files Browse the repository at this point in the history
https://perconadev.atlassian.net/browse/PS-9222

Problem
=======
Upstream commit e6e13a8 tries to fix an issue that doesn't log the
fields that changes orders when using ALGORITHM=instant, by checking if
the fields is also reordered, then adding the columns into the list.
However when calculating the size of the buffer this fix doesn't take
account the extra fields that may be logged, and causing the assertion
on the buffer size failed eventually.

Solution
========
To calculate the buffer size correctly, we move the logic of finding
reordered fiedls before buffer size calculation, then count the number
of fields with the same logic when deciding if a field needs to be
logged.
  • Loading branch information
keeper authored and VarunNagaraju committed Jun 19, 2024
1 parent 2d41d23 commit 150cc7d
Show file tree
Hide file tree
Showing 3 changed files with 88 additions and 29 deletions.
14 changes: 14 additions & 0 deletions mysql-test/suite/innodb/r/instant_alter_stored_column_order.result
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# PS-9222 - Testing if ALGORITHM=instant crashes server
CREATE TABLE t1 (c1 TINYTEXT COLLATE ascii_bin NOT NULL, c2 DATETIME(3) NOT NULL, c3 TEXT, PRIMARY KEY (c1(30)));
INSERT INTO t1 (c1, c2, c3) VALUE ('k1','2021-12-21','something');
INSERT INTO t1 (c1, c2, c3) VALUE ('k3','2021-12-21','something else');
ALTER TABLE t1 ADD COLUMN c4 VARCHAR(18) NOT NULL, algorithm=instant;
UPDATE t1 SET c4 = 'value' WHERE c1 = 'k1';
# Restart the server and reload the table to see if tables are corrupted.
# Kill and restart
# Run a select to confirm that the database started up successfully
SELECT * FROM t1;
c1 c2 c3 c4
k1 2021-12-21 00:00:00.000 something value
k3 2021-12-21 00:00:00.000 something else
DROP TABLE t1;
31 changes: 31 additions & 0 deletions mysql-test/suite/innodb/t/instant_alter_stored_column_order.test
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
--disable_query_log
--disable_warnings
DROP DATABASE IF EXISTS instant_alter_stored_column_order;
CREATE DATABASE instant_alter_stored_column_order;
USE instant_alter_stored_column_order;
--enable_warnings
--enable_query_log

# PS-9222 Testing regression introduced by e6e13a8f, which causes engine crashed

--echo # PS-9222 - Testing if ALGORITHM=instant crashes server
CREATE TABLE t1 (c1 TINYTEXT COLLATE ascii_bin NOT NULL, c2 DATETIME(3) NOT NULL, c3 TEXT, PRIMARY KEY (c1(30)));
INSERT INTO t1 (c1, c2, c3) VALUE ('k1','2021-12-21','something');
INSERT INTO t1 (c1, c2, c3) VALUE ('k3','2021-12-21','something else');

ALTER TABLE t1 ADD COLUMN c4 VARCHAR(18) NOT NULL, algorithm=instant;
UPDATE t1 SET c4 = 'value' WHERE c1 = 'k1';

--echo # Restart the server and reload the table to see if tables are corrupted.
--source include/kill_and_restart_mysqld.inc

-- echo # Run a select to confirm that the database started up successfully
SELECT * FROM t1;
DROP TABLE t1;

# cleanup
--disable_query_log
--disable_warnings
DROP DATABASE instant_alter_stored_column_order;
--enable_warnings
--enable_query_log
72 changes: 43 additions & 29 deletions storage/innobase/mtr/mtr0log.cc
Original file line number Diff line number Diff line change
Expand Up @@ -515,18 +515,28 @@ constexpr size_t inst_col_info_size = 6;
@param[in] is_comp true if COMP
@param[in] is_versioned if table has row versions
@param[in] is_instant true if table has INSTANT cols
@param[in] changed_order array indicating fields changed position
@param[out] size_needed total size needed on REDO LOG */
static void log_index_get_size_needed(const dict_index_t *index, size_t size,
uint16_t n, bool is_comp,
bool is_versioned, bool is_instant,
const bool *changed_order,
size_t &size_needed) {
auto size_for_versioned_fields = [](const dict_index_t *ind) {
auto size_for_versioned_fields = [n, changed_order](const dict_index_t *ind) {
size_t _size = 0;
/* 2 bytes for number of columns with version */
_size += 2;

size_t n_versioned_fields = ind->table->get_n_instant_add_cols() +
ind->table->get_n_instant_drop_cols();
size_t n_versioned_fields = 0;
for (size_t i = 0; i < n; i++) {
dict_field_t *field = ind->get_field(i);
const dict_col_t *col = field->col;
if (col->is_instant_added() || col->is_instant_dropped() ||
changed_order[i]) {
n_versioned_fields += 1;
}
}

ut_ad(n_versioned_fields != 0);

_size += n_versioned_fields * inst_col_info_size;
Expand Down Expand Up @@ -644,6 +654,7 @@ static bool close_and_reopen_log(byte *&log_ptr, const byte *&log_start,
const byte *&log_end, mtr_t *&mtr,
size_t &alloc, size_t &total) {
mlog_close(mtr, log_ptr);

ut_a(total > (ulint)(log_ptr - log_start));
total -= log_ptr - log_start;
alloc = total;
Expand Down Expand Up @@ -699,7 +710,7 @@ static bool log_index_fields(const dict_index_t *index, uint16_t n,

if (is_versioned) {
if (col->is_instant_added() || col->is_instant_dropped() ||
changed_order[i]) {
changed_order[i]) { // e6e13a8: This change causes the regression
f.push_back(field);
}
}
Expand Down Expand Up @@ -799,9 +810,36 @@ bool mlog_open_and_write_index(mtr_t *mtr, const byte *rec,
n = DICT_INDEX_SPATIAL_NODEPTR_SIZE;
}

/* Ordinal position of an existing field can't be changed with INSTANT
algorithm. But when it is combined with ADD/DROP COLUMN, ordinal position
of a filed can be changed. This bool array of size #fields in index,
represents if ordinal position of an existing filed is changed. */

// Move the logic to find columns that changed order before calculating
// the buffer size
bool *fields_with_changed_order = nullptr;
if (is_versioned) {
fields_with_changed_order = new bool[n];
memset(fields_with_changed_order, false, (sizeof(bool) * n));

uint16_t phy_pos = 0;
for (size_t i = 0; i < n; i++) {
dict_field_t *field = index->get_field(i);
const dict_col_t *col = field->col;

if (col->is_instant_added() || col->is_instant_dropped()) {
continue;
} else if (col->get_phy_pos() >= phy_pos) {
phy_pos = col->get_phy_pos();
} else {
fields_with_changed_order[i] = true;
}
}
}

size_t size_needed = 0;
log_index_get_size_needed(index, size, n, is_comp, is_versioned, is_instant,
size_needed);
fields_with_changed_order, size_needed);
size_t total = size_needed;
size_t alloc = total;
if (alloc > mtr_buf_t::MAX_DATA_SIZE) {
Expand Down Expand Up @@ -846,30 +884,6 @@ bool mlog_open_and_write_index(mtr_t *mtr, const byte *rec,
return true;
};

/* Ordinal position of an existing field can't be changed with INSTANT
algorithm. But when it is combined with ADD/DROP COLUMN, ordinal position
of a filed can be changed. This bool array of size #fields in index,
represents if ordinal position of an existing filed is changed. */
bool *fields_with_changed_order = nullptr;
if (is_versioned) {
fields_with_changed_order = new bool[n];
memset(fields_with_changed_order, false, (sizeof(bool) * n));

uint16_t phy_pos = 0;
for (size_t i = 0; i < n; i++) {
dict_field_t *field = index->get_field(i);
const dict_col_t *col = field->col;

if (col->is_instant_added() || col->is_instant_dropped()) {
continue;
} else if (col->get_phy_pos() >= phy_pos) {
phy_pos = col->get_phy_pos();
} else {
fields_with_changed_order[i] = true;
}
}
}

if (is_comp) {
/* Write fields info. */
if (!log_index_fields(index, n, is_versioned, instant_fields_to_log,
Expand Down

0 comments on commit 150cc7d

Please sign in to comment.