Bug #40760 "set global innodb_thread_concurrency = 0;" is not safe
Submitted: 15 Nov 2008 18:12 Modified: 20 Jun 2010 1:00
Reporter: Yasufumi Kinoshita Email Updates:
Status: Closed Impact on me:
None 
Category:MySQL Server: InnoDB storage engine Severity:S3 (Non-critical)
Version:5.0.67, 5.0.70, 5.1 OS:Any
Assigned to: Inaam Rana CPU Architecture:Any

[15 Nov 2008 18:12] Yasufumi Kinoshita
Description:
During transactions are running on the other sessions,
"set global innodb_thread_concurrency = 0;" from the other value may cause error log
at the close of the session.

And at that time "set global innodb_thread_concurrency = 1;" can halt InnoDB.

How to repeat:
session-1> set global innodb_thread_concurrency = 8; /* !=0 value */

session-2> (execute long query using InnoDB table)

(during the query of session-2)
session-1> set global innodb_thread_concurrency = 0;

session-2> exit

===(The message is outputted to .err file)========================
081116  3:10:21  InnoDB: Error: Freeing a trx which is declared to be processing
InnoDB: inside InnoDB.
TRANSACTION 0 2567, not started, process no 4287, OS thread id 1141483840, thread declared inside InnoDB 53
MySQL thread id 7, query id 44 localhost root
==================================================================

In addition, at that time,

> set global innodb_thread_concurrency = 1;

Then nobody can use InnoDB tables....
[16 Nov 2008 6:16] Valeriy Kravchuk
Thank you for a problem report. Sorry, but I was not able to repeat the behaviour described neither on 5.0.70 nor on latest 5.0.74 from bzr with only two sessions whith one of them doing "select count(*) from big_innodb_table" or "update big_innodb_table set ...".

So, please, provide a more detailed test case. Do I need to use multi-core system to repeat this?
[16 Nov 2008 10:56] Yasufumi Kinoshita
Valeriy,

I think any long query is OK. And it don't need SMP.

My test case is intended to change srv_thread_concurrency between these functions at ha_innodb.cc

/**********************************************************************
Save some CPU by testing the value of srv_thread_concurrency in inline
functions. */
inline
void
innodb_srv_conc_enter_innodb(
/*=========================*/
        trx_t*  trx)    /* in: transaction handle */
{
        if (UNIV_LIKELY(!srv_thread_concurrency)) {  /* intended value == 8 */

                return;
        }

        srv_conc_enter_innodb(trx);
}

/**********************************************************************
Save some CPU by testing the value of srv_thread_concurrency in inline
functions. */
inline
void
innodb_srv_conc_exit_innodb(
/*========================*/
        trx_t*  trx)    /* in: transaction handle */
{
        if (UNIV_LIKELY(!srv_thread_concurrency)) { /* intended value == 0 */

                return;
        }

        srv_conc_exit_innodb(trx);
}

In addition, the another test case to halt is that
when benchmark (like DBT-2) is running, repeat to switch
"set global innodb_thread_concurrency = 0;" and
"set global innodb_thread_concurrency = 8;"(smaller value than the sessions of benchmark) many times.
[16 Nov 2008 11:23] Valeriy Kravchuk
Verified with 5.0.70 on our sol10-sparc-c based on last comment. I had started 10 long running transactions (INSERT ... SELECT, UPDATE, DELETE) with innodb_thread_concurrency=8 and then, from the other session, did:

set global innodb_thread_concurrency=0;
set global innodb_thread_concurrency=1;
set global innodb_thread_concurrency=4;

dozen of times. I've got in the error log eventually:

Version: '5.0.70-enterprise-gpl-log'  socket: '/tmp/vk.sock'  port: 4444  MySQL Enterprise Server (GPL)
081116 12:18:30  InnoDB: Error: Freeing a trx which is declared to be processing
InnoDB: inside InnoDB.
TRANSACTION 0 23807000, not started, OS thread id 56, thread declared inside InnoDB 49
MySQL thread id 46, query id 373 localhost root
[18 Nov 2008 16:45] Heikki Tuuri
Thank you Yasufumi. You again found a bug concerned with races.

In very old versions of InnoDB, one could not dynamically adjust thread concurrency, and the bug was not present.

Hmm... a fix might be that in the two functions below, we test if trx->declared_to_be_inside_innodb is TRUE. If that is the case, we must do the .._exit() regardless of the value of srv_thread_concurrency.

That is, in the first function below, replace:

"        if (UNIV_LIKELY(!srv_thread_concurrency)) {"

with:

"        if (UNIV_LIKELY(!srv_thread_concurrency && !trx->declared_to_be_inside_innodb )) {"

In the second function, do a similar replacement.

This solves the problem of srv_thread_concurrency > 0 at a thread ENTER and srv_thread_concurrency == 0 at a thread EXIT.

What about the inverse problem? I think that problem is already solved in the code of the second function by:

"        if (trx->declared_to_be_inside_innodb == FALSE) {

                return;
        }
"

/**********************************************************************
Save some CPU by testing the value of srv_thread_concurrency in inline
functions. */
inline
void
innodb_srv_conc_exit_innodb(
/*========================*/
        trx_t*  trx)    /* in: transaction handle */
{
        if (UNIV_LIKELY(!srv_thread_concurrency)) {

                return;
        }

        srv_conc_exit_innodb(trx);
}

/*************************************************************************
This must be called when a thread exits InnoDB in a lock wait or at the
end of an SQL statement. */

void
srv_conc_force_exit_innodb(
/*=======================*/
        trx_t*  trx)    /* in: transaction object associated with the
                        thread */
{
        srv_conc_slot_t*        slot    = NULL;

        if (UNIV_LIKELY(!srv_thread_concurrency)) {

                return;
        }

        if (trx->mysql_thd != NULL
            && thd_is_replication_slave_thread(trx->mysql_thd)) {

                return;
        }

        if (trx->declared_to_be_inside_innodb == FALSE) {

                return;
        }

Assigning this to Inaam, who is our race condition man.
[18 Nov 2008 17:12] Heikki Tuuri
This comment in srv0srv.c is wrong:
"
lint    srv_conc_n_threads      = 0;    /* number of OS threads currently
                                        inside InnoDB; it is not an error
                                        if this drops temporarily below zero
                                        because we do not demand that every
                                        thread increments this, but a thread
                                        waiting for a lock decrements this
                                        temporarily */
"

In the current code, it can never become negative!
[18 Nov 2008 17:51] Heikki Tuuri
There are invariants, which should be stated in the comment:

(1) srv_conc_n_threads is the number of threads that have trx->declared_to_be_inside_innodb == TRUE;
(2) trx->declared_to_be_inside_innodb will eventually become FALSE for every thread.
[12 Mar 2009 22:10] Paul DuBois
Noted in 5.1.32, 6.0.10 changelog.

Changing innodb_thread_concurrency at runtime could cause errors.
[17 Jul 2009 16:36] Mark Callaghan
It would be nice to get a fix for this in 5.0
[5 May 2010 15:04] Bugs System
Pushed into 5.1.47 (revid:joro@sun.com-20100505145753-ivlt4hclbrjy8eye) (version source revid:vasil.dimov@oracle.com-20100331130613-8ja7n0vh36a80457) (merge vers: 5.1.46) (pib:16)
[6 May 2010 16:53] Paul DuBois
Push resulted from incorporation of InnoDB tree. No changes pertinent to this bug.
Re-closing.
[28 May 2010 6:06] Bugs System
Pushed into mysql-next-mr (revid:alik@sun.com-20100524190136-egaq7e8zgkwb9aqi) (version source revid:vasil.dimov@oracle.com-20100331130613-8ja7n0vh36a80457) (pib:16)
[28 May 2010 6:34] Bugs System
Pushed into 6.0.14-alpha (revid:alik@sun.com-20100524190941-nuudpx60if25wsvx) (version source revid:vasil.dimov@oracle.com-20100331130613-8ja7n0vh36a80457) (merge vers: 5.1.46) (pib:16)
[28 May 2010 7:02] Bugs System
Pushed into 5.5.5-m3 (revid:alik@sun.com-20100524185725-c8k5q7v60i5nix3t) (version source revid:vasil.dimov@oracle.com-20100331130613-8ja7n0vh36a80457) (merge vers: 5.1.46) (pib:16)
[29 May 2010 23:07] Paul DuBois
Push resulted from incorporation of InnoDB tree. No changes pertinent to this bug.
Re-closing.
[15 Jun 2010 8:09] Bugs System
Pushed into 5.5.5-m3 (revid:alik@sun.com-20100615080459-smuswd9ooeywcxuc) (version source revid:mmakela@bk-internal.mysql.com-20100415070122-1nxji8ym4mao13ao) (merge vers: 5.1.47) (pib:16)
[15 Jun 2010 8:24] Bugs System
Pushed into mysql-next-mr (revid:alik@sun.com-20100615080558-cw01bzdqr1bdmmec) (version source revid:mmakela@bk-internal.mysql.com-20100415070122-1nxji8ym4mao13ao) (pib:16)
[17 Jun 2010 12:11] Bugs System
Pushed into 5.1.47-ndb-7.0.16 (revid:martin.skold@mysql.com-20100617114014-bva0dy24yyd67697) (version source revid:vasil.dimov@oracle.com-20100331130613-8ja7n0vh36a80457) (merge vers: 5.1.46) (pib:16)
[17 Jun 2010 12:58] Bugs System
Pushed into 5.1.47-ndb-6.2.19 (revid:martin.skold@mysql.com-20100617115448-idrbic6gbki37h1c) (version source revid:vasil.dimov@oracle.com-20100331130613-8ja7n0vh36a80457) (merge vers: 5.1.46) (pib:16)
[17 Jun 2010 13:38] Bugs System
Pushed into 5.1.47-ndb-6.3.35 (revid:martin.skold@mysql.com-20100617114611-61aqbb52j752y116) (version source revid:vasil.dimov@oracle.com-20100331130613-8ja7n0vh36a80457) (merge vers: 5.1.46) (pib:16)