Bug #68981 | bpage->access_time is unsigned int | ||
---|---|---|---|
Submitted: | 17 Apr 2013 7:29 | Modified: | 3 Jul 2013 16:07 |
Reporter: | Tsubasa Tanaka (OCA) | Email Updates: | |
Status: | Closed | Impact on me: | |
Category: | MySQL Server: InnoDB storage engine | Severity: | S3 (Non-critical) |
Version: | 5.5.30, 5.6.10 | OS: | Any |
Assigned to: | Paul DuBois | CPU Architecture: | Any |
[17 Apr 2013 7:29]
Tsubasa Tanaka
[17 Apr 2013 7:37]
Tsubasa Tanaka
Breakpoint 3, i_s_innodb_buffer_page_get_info (bpage=0x7f04aa1c3000, pool_id=0, pos=0, page_info=0x7f0484076f78) at /home/ttanaka/mysql-5.6.10/storage/innobase/handler/i_s.cc:5076 5076 { (gdb) p *bpage $4 = {space = 261, offset = 240, state = 3, flush_type = 0, io_fix = 0, buf_fix_count = 0, buf_pool_index = 0, zip = { data = 0x0, m_end = 0, m_nonempty = 0, n_blobs = 0, ssize = 0}, hash = 0x0, list = {prev = 0x7f04aa1c3180, next = 0x7f04aa1eaf00}, newest_modification = 0, oldest_modification = 0, LRU = {prev = 0x7f04aa1c3180, next = 0x0}, old = 1, freed_page_clock = 1507, access_time = 41213048} (gdb) whatis bpage->access_time type = unsigned int (gdb) p ut_time_ms() $2 = 1366183980583 1366183980583 is greater than 2^32.
[17 Apr 2013 8:05]
Tsubasa Tanaka
mysql56> SELECT TABLE_NAME, INDEX_NAME, ACCESS_TIME FROM INNODB_BUFFER_PAGE WHERE TABLE_NAME = '`d4`.`t1`'; Empty set (0.02 sec) mysql56> create table d4.t1 ( num serial ); select unix_timestamp(now(3)), (unix_timestamp(now(3)) * 1000) & ( pow(2, 32) - 1); Query OK, 0 rows affected (0.04 sec) +------------------------+-----------------------------------------------------+ | unix_timestamp(now(3)) | (unix_timestamp(now(3)) * 1000) & ( pow(2, 32) - 1) | +------------------------+-----------------------------------------------------+ | 1366185723.810 | 386123682 | +------------------------+-----------------------------------------------------+ 1 row in set (0.01 sec) mysql56> SELECT TABLE_NAME, INDEX_NAME, ACCESS_TIME FROM INNODB_BUFFER_PAGE WHERE TABLE_NAME = '`d4`.`t1`'; +------------+------------+-------------+ | TABLE_NAME | INDEX_NAME | ACCESS_TIME | +------------+------------+-------------+ | `d4`.`t1` | num | 386123651 | +------------+------------+-------------+ 1 row in set (0.02 sec)
[22 Apr 2013 19:19]
Mark Leith
Verified with code inspection. If one were to trust comments: unsigned access_time; /*!< time of first access, or 0 if the block was never accessed in the buffer pool. Protected by block mutex */ So it really is supposed to give the date/time of first access only, or 0 otherwise. Updating is only done within buf_page_set_accessed(), and only "if (!bpage->access_time)" Given the overflow, I question whether the old/young LRU management is functioning correctly in buf_page_peek_if_too_old(): if (access_time > 0 && ((ib_uint32_t) (ut_time_ms() - access_time)) >= buf_LRU_old_threshold_ms) { return(TRUE); }
[14 May 2013 15:16]
Vasil Dimov
Hello, buf_page_t::access_time is expected to wrap around. It is measured in milliseconds since epoch and is stored in 32 bit unsigned integer. The documentation http://dev.mysql.com/doc/refman/5.6/en/innodb-buffer-page-table.html says "Time of first access" which is misleading - the exact specification is "The least significant 32 bits from the number of milliseconds since epoch of the first access". If that is too cryptic, the doc should say "an abstract number used to judge the first access time". About the check in buf_page_peek_if_too_old() (notice that ut_time_ms() returns ulint which is 64 bit on 64 bit systems (no wrap) and 32 bit on 32 bit systems (will wrap)): 179 unsigned access_time = buf_page_is_accessed(bpage); 180 181 if (access_time > 0 182 && ((ib_uint32_t) (ut_time_ms() - access_time)) 183 >= buf_LRU_old_threshold_ms) { 184 return(TRUE); 185 } In either 64 or 32 bit systems it works correctly most of the time. But there is an edge case where it would fail to provide the expected result. The thing is that 2^32 milliseconds pass in about 50 days. So if the difference between ut_time_ms() and access_time is more than 50 days, then it will not work correctly. Here is an example: (gdb) p (unsigned)(1372832535000 - (unsigned)1368537567000) $1 = 704 In this case it would assume that the page was first accessed 0.704 seconds ago, but: 1368537567000 milliseconds since epoch correspond to Tue May 14 13:19:27 UTC 2013 and 1372832535000 milliseconds since epoch correspond to Wed Jul 3 06:22:15 UTC 2013, i.e. the page was first accessed about 50 days ago.
[15 May 2013 15:45]
Vasil Dimov
It is not worth increasing the size of buf_page_t with 32 bits (change access_time from 32 bit to 64 bit) in order to fix the issue with buf_page_peek_if_too_old() misbehaving for some 50+ days old pages. It could sometimes wrongly return FALSE when it should return TRUE - if the page's age is in the interval [50 days, 50 days + buf_LRU_old_threshold_ms).
[15 May 2013 15:46]
Vasil Dimov
The documentation at http://dev.mysql.com/doc/refman/5.6/en/innodb-buffer-page-table.html should be changed from "Time of first access" to "An abstract number used to judge the first access time of the page".
[20 May 2013 16:50]
MySQL Verification Team
reminds me of http://bugs.mysql.com/bug.php?id=52111
[3 Jul 2013 16:07]
Paul DuBois
Thank you for your bug report. This issue has been addressed in the documentation. The updated documentation will appear on our website shortly, and will be included in the next release of the relevant products.