Bug #45309 InnoDB does not rollback for delete and update queries if query was killed
Submitted: 3 Jun 2009 17:17 Modified: 14 Jul 2009 14:44
Reporter: Alexander Rubin Email Updates:
Status: Closed Impact on me:
None 
Category:MySQL Server: General Severity:S2 (Serious)
Version:5.1, 6.0 bzr OS:Any
Assigned to: Alexey Kopytov
Tags: delete, innodb, regression, rollback, upade
Triage: Triaged: D2 (Serious)

[3 Jun 2009 17:17] Alexander Rubin
Description:
InnoDB in MySQL 5.1 does not rollback for delete and update queries if the query was killed. Test case:

MySQL 5.1, killing DELETE query

mysql> select count(*) from articles;
+----------+
| count(*) |
+----------+
|  1048576 |
+----------+
1 row in set (5.56 sec)

mysql> delete from articles2 where id > 1000000;
Query aborted by Ctrl+C
ERROR 1317 (70100): Query execution was interrupted
mysql>  select count(*) from articles2;
+----------+
| count(*) |
+----------+
|   906447 |
+----------+
1 row in set (0.69 sec)

One more way of killing query:

mysql> show processlist;
+----+------+-----------+------+---------+------+----------+------------------------------------------+
| Id | User | Host      | db   | Command | Time | State    | Info                                     |
+----+------+-----------+------+---------+------+----------+------------------------------------------+
|  1 | root | localhost | test | Query   |    1 | updating | delete from articles2 where id > 1000000 |
|  2 | root | localhost | NULL | Query   |    0 | NULL     | show processlist                         |
+----+------+-----------+------+---------+------+----------+------------------------------------------+
2 rows in set (0.00 sec)

mysql> kill 1;
Query OK, 0 rows affected (0.00 sec)

mysql> show processlist;
+----+------+-----------+------+---------+------+-------+------------------+
| Id | User | Host      | db   | Command | Time | State | Info             |
+----+------+-----------+------+---------+------+-------+------------------+
|  2 | root | localhost | NULL | Query   |    0 | NULL  | show processlist |
|  8 | root | localhost | test | Sleep   |    5 |       | NULL             |
+----+------+-----------+------+---------+------+-------+------------------+
2 rows in set (0.00 sec)

mysql>  select count(*) from articles2;

+----------+
| count(*) |
+----------+
|   739561 |
+----------+
1 row in set (0.47 sec)

MySQL 5.1, killing UPDATE query

mysql> select count(*) from articles where section_id=100;
+----------+
| count(*) |
+----------+
|        0 |
+----------+
1 row in set (0.00 sec)

mysql> update articles set  section_id=100;
Query aborted by Ctrl+C
ERROR 1317 (70100): Query execution was interrupted
mysql> select count(*) from articles where section_id=100;
+----------+
| count(*) |
+----------+
|    65686 |
+----------+
1 row in set (0.05 sec)

This does not apply to insert into ... select from ...

To compare, MySQL 5.0:
mysql> select count(*) from articles;
+----------+
| count(*) |
+----------+
|  1048576 |
+----------+
1 row in set (0.67 sec)

mysql> delete from articles where id >100000;
Query aborted by Ctrl+C
ERROR 1317 (70100): Query execution was interrupted
mysql>
mysql> select count(*) from articles;
+----------+
| count(*) |
+----------+
|  1048576 |
+----------+
1 row in set (0.66 sec)

How to repeat:
See above.

Attaching my.cnf config for mysql 5.1 (nothing special):

skip-name-resolve
back_log = 50
max_connections = 400
max_connect_errors = 10
table_cache = 512
max_allowed_packet = 16M
binlog_cache_size = 1M
max_heap_table_size = 64M
thread_cache_size = 8
thread_concurrency = 8
ft_min_word_len = 2
default_table_type = InnoDB
thread_stack = 192K
tmp_table_size = 64M
server-id = 1
key_buffer_size = 32M
bulk_insert_buffer_size = 300M
myisam_sort_buffer_size = 300M
myisam_max_sort_file_size = 10G
myisam_max_extra_sort_file_size = 10G
myisam_repair_threads = 1

      innodb_additional_mem_pool_size = 16M
      innodb_buffer_pool_size = 1G
      innodb_data_file_path = ibdata1:10M:autoextend
      innodb_file_io_threads = 4
      innodb_thread_concurrency = 16
      innodb_flush_log_at_trx_commit = 1
      innodb_log_buffer_size = 8M
      innodb_log_file_size = 256M
      innodb_log_files_in_group = 3
      innodb_max_dirty_pages_pct = 90
      innodb_flush_method=O_DIRECT

Suggested fix:
Fix MySQL/InnoDB so it will rollback killed queries
[3 Jun 2009 20:42] Sveta Smirnova
From private discussion: autocommit is ON
[3 Jun 2009 20:54] Sveta Smirnova
Thank you for the report.

Verified as described.
[3 Jun 2009 21:04] Sveta Smirnova
test case for the testsuite

Attachment: bug45309.test (application/octet-stream, text), 1.01 KiB.

[16 Jun 2009 12:05] Marko Mäkelä
I am assigning this bug away from me, because this does not look like a storage engine bug after all. Storage engines perform DELETE and UPDATE statements row-by-row. Between rows, MySQL has the chance to observe thd->killed. InnoDB only observes thd->killed in CHECK TABLE, which is implemented by a single call to ha_innobase::check().
[23 Jun 2009 6:15] Bugs System
A patch for this bug has been committed. After review, it may
be pushed to the relevant source trees for release in the next
version. You can access the patch from:

  http://lists.mysql.com/commits/76872

2968 Alexey Kopytov	2009-06-23
      Bug #45309: InnoDB does not rollback for delete and update 
                  queries if query was killed 
       
      Since we rely on thd->is_error() to decide whether we should 
      COMMIT or ROLLBACK after a query execution, check the query 
      'killed' state and throw an error before calling 
      ha_autocommit_or_rollback(), not after. 
       
      The patch was tested manually. For reliable results, the test 
      case would have to KILL QUERY while a DELETE/UPDATE query in 
      another thread is still running. I don't see a way to achieve 
      this kind of synchronization in our test suite (no debug_sync 
      in 5.1).
     @ sql/sql_parse.cc
        Since we rely on thd->is_error() to decide whether we should 
        COMMIT or ROLLBACK after query execution, check the query 
        'killed' state and throw an error before calling 
        ha_autocommit_or_rollback(), not after.
[3 Jul 2009 14:45] Roland Bouman
Hi All!

in bug #45923(duplicate of this, 45309) Michael Izioumtchenko commented: 

"Regarding giving people chance to rollback, turning off autocommit should do it. With autocommit on, rollback on ctrl-c will still be a gamble even with the base bug fixed." 

This seems an unacceptable proposition. 

Firstly, even with autocommit off, I strongly feel the user should not be expected to rollback manually. To me, an unsuccessful query due to an abort is still an unsuccessful query - the behaviour should be no different from the statement failing due to a constraint violation (in which case an automatic rollback of the *statement* should be done, and any changes made in the same transaction prior to the failing statement should be preserved until rolled back or committed. 

Secondly, Michael seems to imply that with autocommit on, the patch committed for this bug does not guarantee to solve the problem. I mean "...will still be a gamble even with the base bug fixed..." still sounds like a bug to me - basically it means you cannot trust autocommit, ever, because you never know if you need to abort a statement. So the question is, what does this patch fix then ?

Finally, I'd like to state my worries that an aborted statement that still commits changes could break replication. I mean, I think the aborted statement won't be written to the binlog, so there will be inconsistency, no? 

Anyway - please let us know if Michaels statement is correct here. If it is, it does not seem a good idea to submit any fix until a real fix is available. (and the current workaround would be to not use autocommit)
[3 Jul 2009 15:06] Mikhail Izioumtchenko
I imply nothing at all on the patch in question.
[3 Jul 2009 19:36] Davi Arnaut
By the time someone hits control-c (or attempt to kill the query), the query might have already been committed. This is the gamble because the commit happens automatically at the end of the query and the response might still be traveling back once the client hits the control-c. Kill is sent in a separate connection.

What the patch tries to accomplish is to check right before autocommit if the thread/query is killed. If not, commit otherwise rollback.
[4 Jul 2009 8:06] Konstantin Osipov
It's a regression that I introduced in the fix for Bug#12713. Now it's fixed.
I don't think that there is much to discuss about it :).
[6 Jul 2009 14:14] Mikhail Izioumtchenko
Roland, the patch will fix the perceived partial commit problem, based on the patch description in the base bug:

check the query 
      'killed' state and throw an error before calling 
      ha_autocommit_or_rollback(), not after.
[6 Jul 2009 20:27] Roland Bouman
Hi! 

ok all thanks for explaining. I'm sorry I was just a bit alarmed about what "gamble" meant. 

Thanks again.
[8 Jul 2009 13:30] Bugs System
Pushed into 5.1.37 (revid:joro@sun.com-20090708131116-kyz8iotbum8w9yic) (version source revid:alexey.kopytov@sun.com-20090626093256-tp8e2e2ed7xxn1s5) (merge vers: 5.1.37) (pib:11)
[9 Jul 2009 7:37] Bugs System
Pushed into 5.1.37 (revid:joro@sun.com-20090708131116-kyz8iotbum8w9yic) (version source revid:alexey.kopytov@sun.com-20090626093256-tp8e2e2ed7xxn1s5) (merge vers: 5.1.37) (pib:11)
[10 Jul 2009 11:21] Bugs System
Pushed into 5.4.4-alpha (revid:anozdrin@bk-internal.mysql.com-20090710111017-bnh2cau84ug1hvei) (version source revid:alexey.kopytov@sun.com-20090626093418-u8lihel5p2vnyfmk) (merge vers: 5.4.4-alpha) (pib:11)
[14 Jul 2009 14:44] Paul Dubois
Noted in 5.1.37, 5.4.4 changelogs.

If autocommit was enabled, InnoDB did not roll back DELETE or UPDATE
statements if the statement was killed.
[12 Aug 2009 22:17] Paul Dubois
Noted in 5.4.2 changelog because next 5.4 version will be 5.4.2 and not 5.4.4.
[14 Aug 2009 23:07] Paul Dubois
Ignore previous comment about 5.4.2.
[26 Aug 2009 13:46] Bugs System
Pushed into 5.1.37-ndb-7.0.8 (revid:jonas@mysql.com-20090826132541-yablppc59e3yb54l) (version source revid:jonas@mysql.com-20090826132541-yablppc59e3yb54l) (merge vers: 5.1.37-ndb-7.0.8) (pib:11)
[26 Aug 2009 13:46] Bugs System
Pushed into 5.1.37-ndb-6.3.27 (revid:jonas@mysql.com-20090826105955-bkj027t47gfbamnc) (version source revid:jonas@mysql.com-20090826105955-bkj027t47gfbamnc) (merge vers: 5.1.37-ndb-6.3.27) (pib:11)
[26 Aug 2009 13:48] Bugs System
Pushed into 5.1.37-ndb-6.2.19 (revid:jonas@mysql.com-20090825194404-37rtosk049t9koc4) (version source revid:jonas@mysql.com-20090825194404-37rtosk049t9koc4) (merge vers: 5.1.37-ndb-6.2.19) (pib:11)
[27 Aug 2009 16:33] Bugs System
Pushed into 5.1.35-ndb-7.1.0 (revid:magnus.blaudd@sun.com-20090827163030-6o3kk6r2oua159hr) (version source revid:jonas@mysql.com-20090826132541-yablppc59e3yb54l) (merge vers: 5.1.37-ndb-7.0.8) (pib:11)
[7 Oct 2009 18:35] Paul Dubois
The 5.4 fix has been pushed to 5.4.2.
[30 Jan 19:11] Sachin Tiwari
I know it's very old bug and my question is probably not relevant but shouldn't this issue (bug) be caught in the testing phase itself as it seems to be very basic test scenario that must exist and pass.