Bug #103891 Two parallel threads trigger error code '1032 Can't find record in 'table'

Submitted: 2 Jun 2021 14:13 Modified: 4 Jul 2021 7:42
Reporter: Fei Li Email Updates:
Status: Verified Impact on me:
None 
Category:MySQL Server: InnoDB storage engine Severity:S3 (Non-critical)
Version:MySQL5.6 and MySQL5.7.33 OS:Any
Assigned to: CPU Architecture:Any

[2 Jun 2021 14:13] Fei Li
Description:
# The script can reproduce
# the `can’t find record` problem in MySQL5.6 and MySQL5.7.33

import pymysql
import _thread

# You should create this database before executing the script
db = 'testdb'

# host, port, user and password
host = 'localhost'
port = 13306
user = 'root'
password = 'root'

def connect() -> pymysql.Connection:
    return pymysql.connect(host=host,
                           port=port,
                           user=user,
                           password=password,
                           db=db)

# recreate database ${db}
# then create schema for test
def initdb():
    conn = connect()
    cur = conn.cursor()
    cur.execute(f'drop table if exists t;')
    cur.execute('create table t(pkAttr0 integer, coAttr1 varchar(100), \
                coAttr2 varchar(100), coAttr3 varchar(100), coAttr4 integer, \
                coAttr5 varchar(100), coAttr6 varchar(100), primary key(pkAttr0));')

def select():
    conn = connect()
    cur = conn.cursor()
    cur.execute('set session transaction isolation level read uncommitted')
    conn.autocommit(False)
    while True:
        #Sometimes, this select triggers error:pymysql.err.0perationalError: (1032,"Can't find record in 't'")
        cur.execute('select pkAttr0, coAttr1, coAttr2, coAttr3, coAttr4, coAttr5, coAttr6 \
            from t order by pkAttr0, coAttr1, coAttr2, coAttr3, coAttr4, coAttr5, coAttr6;')
        print(cur.fetchall())
        conn.commit()

def change():
    conn = connect()
    cur = conn.cursor()
    cur.execute('set session transaction isolation level read uncommitted')
    conn.autocommit(False)

    while True:
        cur.execute('delete from t where pkAttr0 = 1;')
        cur.execute('insert into t(pkAttr0, coAttr1, coAttr2, coAttr3, coAttr4, coAttr5, coAttr6) \
            values("1", "varchar1", "varchar2", "varchar3", "2021", "varchar5", "varchar6");')
        # The problem can be reproduced whether or not to commit the transaction
        # conn.commit()

initdb()

_thread.start_new_thread(select, ())
_thread.start_new_thread(change, ())

while True:
    pass

How to repeat:
Run the above Python script to reproduce the bug.

Suggested fix:
The expected result should not throw '1032' error.
[3 Jun 2021 13:25] MySQL Verification Team
Hi Mr. Li,

Thank you for your bug report.

However, we can not accept a test case in this form, since we are not testing Python , but only our own software.

Hence, we need structure and contents of all the tables involved in these two transactions. Next, we require a set of SQL queries in each of the transactions , that would lead to the error code that you report. We shall run them in parallel, in our own environment.

We are waiting on your feedback.
[3 Jun 2021 13:29] MySQL Verification Team
Hi,

We must also point out that what you describe is absolutely expected behaviour when you use isolation level of READ_UNCOMMITTED. As our Manual says , it can lead to the unexpected results, so let us also know whether you also get this error with any other isolation level.
[4 Jul 2021 1:00] Bugs System
No feedback was provided for this bug for over a month, so it is
being suspended automatically. If you are able to provide the
information that was originally requested, please do so and change
the status of the bug back to "Open".
[4 Jul 2021 7:42] Shane Bester
Verified with a psuedorandom testcase on 5.7.36.

drop table if exists t;
create table t(pkattr0 integer, coattr1 varchar(100), coattr2 varchar(100), coattr3 varchar(100), coattr4 integer, coattr5 varchar(100), coattr6 varchar(100), primary key(pkattr0));

drop procedure if exists p;
delimiter $
create procedure p()
begin
 
  declare done int default false;
  declare v_pkattr0 integer;
  declare v_coattr1 varchar(100);
  declare v_coattr2 varchar(100);
  declare v_coattr3 varchar(100);
  declare v_coattr4 varchar(100);
  declare v_coattr5 varchar(100);
  declare v_coattr6 varchar(100);
  declare cur1 cursor for select pkattr0, coattr1, coattr2, coattr3, coattr4, coattr5, coattr6 from t order by pkattr0, coattr1, coattr2, coattr3, coattr4, coattr5, coattr6;
  -- declare continue handler for sqlexception begin end;
  declare continue handler for not found set done = true;
  repeat
     set session transaction isolation level read uncommitted;
     if rand()<0.5 then start transaction; end if;
     open cur1;
      read_loop: loop
        fetch cur1 into v_pkattr0,v_coattr1,v_coattr2,v_coattr3,v_coattr4,v_coattr5,v_coattr6;
        if done then
          leave read_loop;
        end if;
      end loop;
     close cur1;
     if rand()<0.5 then commit; end if;
     if rand()<0.5 then start transaction; end if;
     if rand()<0.5 then delete from t where pkattr0 = 1; end if;
     if rand()<0.5 then commit; end if;
     set session transaction isolation level read uncommitted;
     if rand()<0.5 then start transaction; end if;
     if rand()<0.5 then replace into t(pkattr0, coattr1, coattr2, coattr3, coattr4, coattr5, coattr6) values("1", "varchar1", "varchar2", "varchar3", "2021", "varchar5", "varchar6"); end if;
     if rand()<0.5 then commit; end if;
     
  until 1=2 end repeat;
  

end $

delimiter ;

call p();   -- in multiple threads

On launching second call p(),  first one quickly dies with:

mysql> call p();   -- in multiple threads
ERROR 1032 (HY000): Can't find record in 't'
[4 Jul 2021 7:44] Shane Bester
Current 8.0 was not affected.

Portions of this website are copyright © 2001, 2002 The PHP Group

Page generated in 0.054 sec. using MySQL 8.4.7-u6-cloud

Timestamp references displayed by the system are UTC.
Content reproduced on this site is the property of the respective copyright holders.
It is not reviewed in advance by Oracle and does not necessarily represent the opinion of Oracle or any other party.