Skip to content

Commit df0167f

Browse files
Andrzej Jarzabekdahlerlend
authored andcommitted
Bug#36319083 [InnoDB] Merge sort buffer can be too small
Symptom: In some circumstances an index cannot be created; this is configuration-dependent Root cause: Merge sort file buffer size can in some cases be calculated to be the size of IO_BLOCK_SIZE. The logic for the buffer is that subsequent records of data are added to the buffer, but when adding a row would overflow the buffer, thw contents are written to disk in multiples of IO_BLOCK_SIZE and space is freed up. If the buffer is only IO_BLOCK_SIZE, it is likely that at that point the existing contents' length is less than IO_BLOCK_SIZE, in which case nothing gets written, no space can be freed up, and the new record cannot be added. Fix: Since maximum allowed key length is 3072 and IO_BLOCK_SIZE is 4096 bytes, and given other factors contributing to buffer length, like page size, which also affects allowed key size, 2 * IO_BLOCK_SIZE is sufficient minimum length to ensure there is always IO_BLOCK_SIZE bytes in the buffer when the write happens. If this ever changes, the merge will fail gracefully. Change-Id: I7aec373cfed9e364751372cce3746eb7ad75b3b9
1 parent 5943f6c commit df0167f

File tree

2 files changed

+22
-11
lines changed

2 files changed

+22
-11
lines changed

storage/innobase/ddl/ddl0ctx.cc

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -206,7 +206,10 @@ size_t Context::merge_io_buffer_size(size_t n_buffers) const noexcept {
206206
/* We aim to do IO_BLOCK_SIZE writes all the time. */
207207
ut_a(!(io_size % IO_BLOCK_SIZE));
208208

209-
return std::max(std::max((ulong)srv_page_size, (ulong)IO_BLOCK_SIZE),
209+
/* The buffer must be at least large enough to fit one IO block plus one row.
210+
2 * IO_BLOCK_SIZE meets this criterion given limits on key length -
211+
see ha_innobase::max_supported_key_length() */
212+
return std::max(std::max((ulong)srv_page_size, (ulong)IO_BLOCK_SIZE * 2LU),
210213
(ulong)io_size);
211214
}
212215

storage/innobase/ddl/ddl0merge.cc

Lines changed: 18 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -321,20 +321,28 @@ dberr_t Merge_file_sort::Output_file::write(const mrec_t *mrec,
321321
if (unlikely(m_ptr + rec_size + need >= m_buffer.first + m_buffer.second)) {
322322
const size_t n_write = m_ptr - m_buffer.first;
323323
const auto len = ut_uint64_align_down(n_write, IO_BLOCK_SIZE);
324-
auto err = ddl::pwrite(m_file.get(), m_buffer.first, len, m_offset);
324+
if (len != 0) {
325+
auto err = ddl::pwrite(m_file.get(), m_buffer.first, len, m_offset);
325326

326-
if (err != DB_SUCCESS) {
327-
return err;
328-
}
327+
if (err != DB_SUCCESS) {
328+
return err;
329+
}
329330

330-
ut_a(n_write >= len);
331-
const auto n_move = n_write - len;
331+
ut_a(n_write >= len);
332+
const auto n_move = n_write - len;
332333

333-
m_ptr = m_buffer.first;
334-
memmove(m_ptr, m_ptr + len, n_move);
335-
m_ptr += n_move;
334+
m_ptr = m_buffer.first;
335+
memmove(m_ptr, m_ptr + len, n_move);
336+
m_ptr += n_move;
336337

337-
m_offset += len;
338+
m_offset += len;
339+
}
340+
341+
if (unlikely(m_ptr + rec_size + need >= m_buffer.first + m_buffer.second)) {
342+
// Should be caught earlier
343+
ut_d(ut_error);
344+
ut_o(return DB_TOO_BIG_RECORD);
345+
}
338346
}
339347

340348
m_last_mrec = m_ptr;

0 commit comments

Comments
 (0)