← Back to team overview

maria-developers team mailing list archive

Re: [Commits] 27af988: MDEV-11479 Improved wsrep_dirty_reads

 

Hi Vicentiu!

Please review the updated patch.

On Thu, Dec 8, 2016 at 4:42 PM, Vicențiu Ciorbaru <vicentiu@xxxxxxxxxxx> wrote:
> Hi Sachin!
>
> Comments inline:
>
> On Sun, 4 Dec 2016 at 08:36 <sachin.setiya@xxxxxxxxxxx> wrote:
>>
>> revision-id: 27af98805c27c8e0c41dc8c19bf80aefff4c6d3d
>> (mariadb-galera-5.5.53-3-g27af988)
>> parent(s): 72fd15f7c31aa3e3705ae1b005a3247a985c5bb3
>> author: SachinSetiya
>> committer: SachinSetiya
>
>
> NIT: To get these to display correctly you should have a space between
> Sachin and Setiya.
>
Done :)
>>
>> timestamp: 2016-12-04 12:05:05 +0530
>> message:
>>
>> MDEV-11479 Improved wsrep_dirty_reads
>>
>>     Tasks:-
>>         Changes in wsrep_dirty_reads variable
>>         1.) Global + Session scope (Current: session-only)
>>         2.) Allow all commands that do not change data (besides SELECT)
>>         3.) Allow prepared Statements that do not change data
>>         4.) Works with wsrep_sync_wait enabled
>
>
> You also seemed to have made the variable available as a command line
> option. Please specify that as well.
>
Done.
>>
>> ---
>>  .../suite/galera/r/galera_var_dirty_reads.result   | 29
>> +++++++++++++++++++---
>>  .../suite/galera/t/galera_var_dirty_reads.test     | 25
>> ++++++++++++++++++-
>>  .../sys_vars/r/wsrep_dirty_reads_basic.result      | 19 ++++++++++++--
>>  .../suite/sys_vars/t/wsrep_dirty_reads_basic.test  | 13 ++++++++--
>>  sql/sql_parse.cc                                   | 18 ++++++++++----
>>  sql/sys_vars.cc                                    |  4 +--
>>  sql/wsrep_mysqld.cc                                |  3 ++-
>>  sql/wsrep_mysqld.h                                 |  1 +
>>  8 files changed, 96 insertions(+), 16 deletions(-)
>>
>> diff --git a/mysql-test/suite/galera/r/galera_var_dirty_reads.result
>> b/mysql-test/suite/galera/r/galera_var_dirty_reads.result
>> index 6a2aa1e..d009c1b 100644
>> --- a/mysql-test/suite/galera/r/galera_var_dirty_reads.result
>> +++ b/mysql-test/suite/galera/r/galera_var_dirty_reads.result
>> @@ -28,9 +28,32 @@ SELECT @@max_allowed_packet;
>>  SELECT 2+2 from DUAL;
>>  2+2
>>  4
>> -SELECT sysdate() from DUAL;
>> -sysdate()
>> -2016-10-28 23:13:06
>> +SET @@session.wsrep_dirty_reads=ON;
>> +SELECT * FROM t1;
>> +i
>> +1
>> +prepare stmt_show from 'select 1';
>> +prepare stmt_select from 'select * from t1';
>> +prepare stmt_insert from 'insert into t1 values(1)';
>> +execute stmt_show;
>> +1
>> +1
>> +execute stmt_select;
>> +i
>> +1
>> +execute stmt_insert;
>> +ERROR 08S01: WSREP has not yet prepared node for application use
>> +select * from t1;
>> +i
>> +1
>> +execute stmt_show;
>> +1
>> +1
>> +execute stmt_select;
>> +i
>> +1
>> +execute stmt_insert;
>> +ERROR 08S01: WSREP has not yet prepared node for application use
>>  SELECT * FROM t1;
>>  i
>>  1
>
>
> You're not really testing the global aspect of the variable here. You're
> only testing the sesssion behaviour of it. Also, test with it both ON and
> OFF (when setting it globally). Here's a sample that tests both session and
> global behaviour:
>
> --enable_connect_log
>
> SELECT @@expensive_subquery_limit;
> SET @@session.expensive_subquery_limit=300;
> SELECT @@expensive_subquery_limit;
>
> create user user1;
> create user user2;
> --connect (con1, localhost, user1,,)
> SELECT @@expensive_subquery_limit;
> SET @@session.expensive_subquery_limit=300;
> SELECT @@expensive_subquery_limit;
>
> connection default;
>
> SELECT @@expensive_subquery_limit;
> SET @@global.expensive_subquery_limit=400;
> SELECT @@expensive_subquery_limit;
>
> --connect (con2, localhost, user1,,)
> SELECT @@expensive_subquery_limit;
>
>
> connection default;
> drop user user1;
> drop user user2;
>
> ************************************
>
> SELECT @@expensive_subquery_limit;
> @@expensive_subquery_limit
> 100
> SET @@session.expensive_subquery_limit=300;
> SELECT @@expensive_subquery_limit;
> @@expensive_subquery_limit
> 300
> create user user1;
> create user user2;
> connect  con1, localhost, user1,,;
> SELECT @@expensive_subquery_limit;
> @@expensive_subquery_limit
> 100
> SET @@session.expensive_subquery_limit=300;
> SELECT @@expensive_subquery_limit;
> @@expensive_subquery_limit
> 300
> connection default;
> SELECT @@expensive_subquery_limit;
> @@expensive_subquery_limit
> 300
> SET @@global.expensive_subquery_limit=400;
> SELECT @@expensive_subquery_limit;
> @@expensive_subquery_limit
> 300
> connect  con2, localhost, user1,,;
> SELECT @@expensive_subquery_limit;
> @@expensive_subquery_limit
> 400
> connection default;
> drop user user1;
> drop user user2;
>
> Notice how the last connection has the default value 400 as that's the
> global value. For your use case, you'd also have to execute the prepared
> statements to see if it works (execute select_stmt).
>
Done.
>
>>
>> diff --git a/mysql-test/suite/galera/t/galera_var_dirty_reads.test
>> b/mysql-test/suite/galera/t/galera_var_dirty_reads.test
>> index dd7bc88..44931aa 100644
>> --- a/mysql-test/suite/galera/t/galera_var_dirty_reads.test
>> +++ b/mysql-test/suite/galera/t/galera_var_dirty_reads.test
>> @@ -44,7 +44,30 @@ SET @@session.wsrep_dirty_reads=OFF;
>>  SELECT 2;
>>  SELECT @@max_allowed_packet;
>>  SELECT 2+2 from DUAL;
>> -SELECT sysdate() from DUAL;
>> +
>> +#Query which does not change data should be allowed MDEV-11479
>> +SET @@session.wsrep_dirty_reads=ON;
>> +SELECT * FROM t1;
>> +
>> +#Prepared statement creation should be allowed MDEV-11479
>> +prepare stmt_show from 'select 1';
>> +prepare stmt_select from 'select * from t1';
>> +prepare stmt_insert from 'insert into t1 values(1)';
>> +
>> +#Only prepare statement which does not change data should be allowed
>> +execute stmt_show;
>> +execute stmt_select;
>> +--error ER_UNKNOWN_COM_ERROR
>> +execute stmt_insert;
>>
>> +
>> +#wsrep_dirty_read should work when wsrep_sync_wait is 1 or non zero
>> +#because we already are disconnected , So It does not make any sense
>> +#to wait for other nodes
>
>
> I don't think you're testing this correctly. See my comment on the if
> statement which is supposed to govern this.
>
>>
>> +select * from t1;
>> +execute stmt_show;
>> +execute stmt_select;
>> +--error ER_UNKNOWN_COM_ERROR
>> +execute stmt_insert;
>>
>>  --disable_query_log
>>  --eval SET @@global.wsrep_cluster_address =
>> '$wsrep_cluster_address_saved'
>> diff --git a/mysql-test/suite/sys_vars/r/wsrep_dirty_reads_basic.result
>> b/mysql-test/suite/sys_vars/r/wsrep_dirty_reads_basic.result
>> index d2a62d6..649ac83 100644
>> --- a/mysql-test/suite/sys_vars/r/wsrep_dirty_reads_basic.result
>> +++ b/mysql-test/suite/sys_vars/r/wsrep_dirty_reads_basic.result
>> @@ -5,12 +5,13 @@
>>  SET @wsrep_dirty_reads_session_saved = @@session.wsrep_dirty_reads;
>>  # default
>>  SELECT @@global.wsrep_dirty_reads;
>> -ERROR HY000: Variable 'wsrep_dirty_reads' is a SESSION variable
>> +@@global.wsrep_dirty_reads
>> +0
>>  SELECT @@session.wsrep_dirty_reads;
>>  @@session.wsrep_dirty_reads
>>  0
>>
>> -# scope and valid values
>> +# valid values for session
>>  SET @@session.wsrep_dirty_reads=OFF;
>>  SELECT @@session.wsrep_dirty_reads;
>>  @@session.wsrep_dirty_reads
>> @@ -24,6 +25,20 @@ SELECT @@session.wsrep_dirty_reads;
>>  @@session.wsrep_dirty_reads
>>  0
>>
>> +# valid values for global
>> +SET @@global.wsrep_dirty_reads=OFF;
>> +SELECT @@global.wsrep_dirty_reads;
>> +@@global.wsrep_dirty_reads
>> +0
>> +SET @@global.wsrep_dirty_reads=ON;
>> +SELECT @@global.wsrep_dirty_reads;
>> +@@global.wsrep_dirty_reads
>> +1
>> +SET @@global.wsrep_dirty_reads=default;
>> +SELECT @@global.wsrep_dirty_reads;
>> +@@global.wsrep_dirty_reads
>> +0
>> +
>>  # invalid values
>>  SET @@session.wsrep_dirty_reads=NULL;
>>  ERROR 42000: Variable 'wsrep_dirty_reads' can't be set to the value of
>> 'NULL'
>
>
> Test invalid values for global scope as well. It should not change obviously
> but since we're writing tests, might as well do that too.
>
Done.
>>
>> diff --git a/mysql-test/suite/sys_vars/t/wsrep_dirty_reads_basic.test
>> b/mysql-test/suite/sys_vars/t/wsrep_dirty_reads_basic.test
>> index a47524f..000c4c4 100644
>> --- a/mysql-test/suite/sys_vars/t/wsrep_dirty_reads_basic.test
>> +++ b/mysql-test/suite/sys_vars/t/wsrep_dirty_reads_basic.test
>> @@ -8,12 +8,12 @@
>>  SET @wsrep_dirty_reads_session_saved = @@session.wsrep_dirty_reads;
>>
>>  --echo # default
>> ---error ER_INCORRECT_GLOBAL_LOCAL_VAR
>> +
>>  SELECT @@global.wsrep_dirty_reads;
>>  SELECT @@session.wsrep_dirty_reads;
>>
>>  --echo
>> ---echo # scope and valid values
>> +--echo # valid values for session
>>  SET @@session.wsrep_dirty_reads=OFF;
>>  SELECT @@session.wsrep_dirty_reads;
>>  SET @@session.wsrep_dirty_reads=ON;
>> @@ -22,6 +22,15 @@ SET @@session.wsrep_dirty_reads=default;
>>  SELECT @@session.wsrep_dirty_reads;
>>
>>  --echo
>> +--echo # valid values for global
>> +SET @@global.wsrep_dirty_reads=OFF;
>> +SELECT @@global.wsrep_dirty_reads;
>> +SET @@global.wsrep_dirty_reads=ON;
>> +SELECT @@global.wsrep_dirty_reads;
>> +SET @@global.wsrep_dirty_reads=default;
>> +SELECT @@global.wsrep_dirty_reads;
>> +
>> +--echo
>>  --echo # invalid values
>>  --error ER_WRONG_VALUE_FOR_VAR
>>  SET @@session.wsrep_dirty_reads=NULL;
>> diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc
>> index eb4f714..a56d694 100644
>> --- a/sql/sql_parse.cc
>> +++ b/sql/sql_parse.cc
>> @@ -2393,10 +2393,11 @@ mysql_execute_command(THD *thd)
>>      */
>>      if (thd->variables.wsrep_on && !thd->wsrep_applier && !wsrep_ready &&
>>          lex->sql_command != SQLCOM_SET_OPTION &&
>> -        !(thd->variables.wsrep_dirty_reads &&
>> -          lex->sql_command == SQLCOM_SELECT) &&
>> -        !(lex->sql_command == SQLCOM_SELECT &&
>> -          !all_tables)                      &&
>> +        !(thd->variables.wsrep_dirty_reads    &&
>> +        (sql_command_flags[lex->sql_command] &
>> +        CF_CHANGES_DATA) == 0)                &&
>
>
> Put this line on one line only, the single & is confusing, or better, turn
> it into a small inline function, such as command_changes_data()
Okay.
>
>>
>> +        !(lex->sql_command == SQLCOM_SELECT   &&
>> +          !all_tables)                        &&
>
> Can't tell for certain but this condition might not be necessary if you have
> the previous one.
This is required by https://jira.mariadb.org/browse/MDEV-11016
previous condition is only applied when wsrep_dirty reads is true,
this is applied independent of wsrep_dirty_reads
 What happens when you have a stored procedure that inserts
> into a table? Does the CF_CHANGES_DATA flag filter out the query or not?
> ex:
> select function_with_side_efect(1);
> This seems like a good test case that I'm not sure is tested for this
> variable anyway. Please try it.
This wont work, because  CREATE PROCEDURE , does changes the data.
sql_command_flags[SQLCOM_CREATE_PROCEDURE]=  CF_CHANGES_DATA |
CF_AUTO_COMMIT_TRANS;
>
>
>>
>>          !wsrep_is_show_query(lex->sql_command))
>>      {
>>  #if DIRTY_HACK
>> @@ -2512,7 +2513,14 @@ mysql_execute_command(THD *thd)
>>    case SQLCOM_SHOW_INDEX_STATS:
>>    case SQLCOM_SELECT:
>>  #ifdef WITH_WSREP
>> -    if (WSREP_CLIENT(thd) && wsrep_sync_wait(thd)) goto error;
>> +    /*
>> +      wsrep_dirty_read should work when wsrep_sync_wait is 1 or  non-zero
>> +      because we already are disconnected. So, it does not make any sense
>> +      to wait for other nodes
>> +    */
>>
>  This here is not correct. This will not execute the select if
> wsrep_dirty_reads is true and wsrep_ready is true and wsrep_sync_wait(thd)
> is non-zero.
This is expected. If wsrep_ready is true, that means cluster is online and if
wsrep_sync_wait returns some non zero value then we have to goto error.
> if (WSREP_CLIENT(thd) && thd->variables.wsrep_dirty_reads(wsrep_ready
>>
>> +    if (WSREP_CLIENT(thd) && (wsrep_ready ||
>> !thd->variables.wsrep_dirty_reads)
>> +        && wsrep_sync_wait(thd))
>> +        goto error
>>
Actually original condition was

if (WSREP_CLIENT(thd))
   {
     if (!wsrep_ready && thd->variables.wsrep_dirty_reads)
       ;
     else if(wsrep_sync_wait(thd))
       goto error;
   }

which I (with the help of wlad ) reduced it to above IF.
>>    case SQLCOM_SHOW_VARIABLES:
>>    case SQLCOM_SHOW_CHARSETS:
>>    case SQLCOM_SHOW_COLLATIONS:
>> diff --git a/sql/sys_vars.cc b/sql/sys_vars.cc
>> index bdcedff..0d864f4 100644
>> --- a/sql/sys_vars.cc
>> +++ b/sql/sys_vars.cc
>> @@ -3967,8 +3967,8 @@ static Sys_var_mybool Sys_wsrep_restart_slave(
>>
>>  static Sys_var_mybool Sys_wsrep_dirty_reads(
>>         "wsrep_dirty_reads", "Do not reject SELECT queries even when the
>> node "
>> -       "is not ready.", SESSION_ONLY(wsrep_dirty_reads), NO_CMD_LINE,
>> -       DEFAULT(FALSE), NO_MUTEX_GUARD, NOT_IN_BINLOG);
>> +       "is not ready.", SESSION_VAR(wsrep_dirty_reads),
>>
>> +       CMD_LINE(OPT_ARG), DEFAULT(FALSE), NO_MUTEX_GUARD, NOT_IN_BINLOG);
>
>
> We might need a guard here since global variables can be changed from
> multiple places. Not 100% sure about this but that's my intuition. Need to
> look this up.

I am not sure if I get this because global variables are guarded by
 LOCK_global_system_variables mutex.
/*
  the define below means that there's no *second* mutex guard,
  LOCK_global_system_variables always guards all system variables
*/
#define NO_MUTEX_GUARD ((PolyLock*)0)

And auto increment uses the same approach.

static Sys_var_ulong Sys_auto_increment_increment(
       "auto_increment_increment",
       "Auto-increment columns are incremented by this",
       SESSION_VAR(auto_increment_increment),
       CMD_LINE(OPT_ARG),
       VALID_RANGE(1, 65535), DEFAULT(1), BLOCK_SIZE(1),
       NO_MUTEX_GUARD, IN_BINLOG)
>
>>
>>  #endif /* WITH_WSREP */
>>
>> diff --git a/sql/wsrep_mysqld.cc b/sql/wsrep_mysqld.cc
>> index 48114fc..f21ff92 100644
>> --- a/sql/wsrep_mysqld.cc
>> +++ b/sql/wsrep_mysqld.cc
>> @@ -65,7 +65,8 @@ my_bool wsrep_restart_slave_activated  = 0; // node has
>> dropped, and slave
>>                                              // restart will be needed
>>  my_bool wsrep_slave_UK_checks          = 0; // slave thread does UK
>> checks
>>  my_bool wsrep_slave_FK_checks          = 0; // slave thread does FK
>> checks
>> -
>> +bool wsrep_dirty_reads                 = 0; // is dirty reads allowed for
>> +                                            // disconnectd/non major node
>
>
> Typo: disconnectd -> disconnected.
> I really hate how the whole file is set up with TRUE/FALSE/0 for either bool
> or my_bool. It's so inconsistent. I'd prefer 'true' here to complicate stuff
> further. :) To be "consistent" I'd make it a my_bool, but we should clean up
> all of this mess at one point.
>
Done, I value changed to false.
>>
>>  /*
>>    Set during the creation of first wsrep applier and rollback threads.
>>    Since these threads are critical, abort if the thread creation fails.
>> diff --git a/sql/wsrep_mysqld.h b/sql/wsrep_mysqld.h
>> index 3fb5189..9b9179a 100644
>> --- a/sql/wsrep_mysqld.h
>> +++ b/sql/wsrep_mysqld.h
>> @@ -85,6 +85,7 @@ extern ulong       wsrep_max_ws_size;
>>  extern ulong       wsrep_max_ws_rows;
>>  extern const char* wsrep_notify_cmd;
>>  extern my_bool     wsrep_certify_nonPK;
>> +extern bool        wsrep_dirty_reads;
>
>
> Remember to update the type here too.
Done.
>
>>
>>  extern long        wsrep_max_protocol_version;
>>  extern long        wsrep_protocol_version;
>>  extern ulong       wsrep_forced_binlog_format;
>
>



-- 
Regards
Sachin Setiya
Software Engineer at  MariaDB
commit 7e1ce93bf5887accf2d8ff7dc1daa5310cd68238
Author: SachinSetiya <sachinsetia1001@xxxxxxxxx>
Date:   Sat Dec 3 17:28:12 2016 +0530

    MDEV-11479 Improved wsrep_dirty_reads
    
    Tasks:-
        Changes in wsrep_dirty_reads variable
        1.) Global + Session scope (Current: session-only)
        2.) Can be Set using command line.
        3.) Allow all commands that do not change data (besides SELECT)
        4.) Allow prepared Statements that do not change data
        5.) Works with wsrep_sync_wait enabled

diff --git a/mysql-test/suite/galera/r/galera_var_dirty_reads.result b/mysql-test/suite/galera/r/galera_var_dirty_reads.result
index 6a2aa1e..cd132a0 100644
--- a/mysql-test/suite/galera/r/galera_var_dirty_reads.result
+++ b/mysql-test/suite/galera/r/galera_var_dirty_reads.result
@@ -3,6 +3,10 @@ INSERT INTO t1 VALUES(1);
 SELECT * FROM t1;
 i
 1
+create user user1;
+grant all privileges on *.* to user1;
+create user user2;
+grant all privileges on *.* to user2;
 SET @@global.wsrep_cluster_address = '';
 SET @@session.wsrep_dirty_reads=OFF;
 SET SESSION wsrep_sync_wait=0;
@@ -28,11 +32,81 @@ SELECT @@max_allowed_packet;
 SELECT 2+2 from DUAL;
 2+2
 4
-SELECT sysdate() from DUAL;
-sysdate()
-2016-10-28 23:13:06
+SET @@session.wsrep_dirty_reads=ON;
+SELECT * FROM t1;
+i
+1
+connect  con1, localhost, user1,,test,$NODE_MYPORT_2,$NODE_MYSOCK_2;
+SET SESSION wsrep_sync_wait=0;
+select @@session.wsrep_dirty_reads;
+@@session.wsrep_dirty_reads
+0
+set session wsrep_dirty_reads=1;
+prepare stmt_show from 'select 1';
+prepare stmt_select from 'select * from t1';
+prepare stmt_insert from 'insert into t1 values(1)';
+set session wsrep_dirty_reads=0;
+execute stmt_show;
+ERROR 08S01: WSREP has not yet prepared node for application use
+execute stmt_select;
+ERROR 08S01: WSREP has not yet prepared node for application use
+execute stmt_insert;
+ERROR 08S01: WSREP has not yet prepared node for application use
+SET wsrep_dirty_reads=ON;
+select @@session.wsrep_dirty_reads;
+@@session.wsrep_dirty_reads
+1
+execute stmt_show;
+1
+1
+execute stmt_select;
+i
+1
+execute stmt_insert;
+ERROR 08S01: WSREP has not yet prepared node for application use
+SET @@global.wsrep_dirty_reads=ON;
+connect  con2, localhost, user2,,test,$NODE_MYPORT_2,$NODE_MYSOCK_2;
+select @@session.wsrep_dirty_reads;
+@@session.wsrep_dirty_reads
+1
+prepare stmt_show from 'select 1';
+prepare stmt_select from 'select * from t1';
+prepare stmt_insert from 'insert into t1 values(1)';
+execute stmt_show;
+1
+1
+execute stmt_select;
+i
+1
+execute stmt_insert;
+ERROR 08S01: WSREP has not yet prepared node for application use
+SET SESSION wsrep_sync_wait=1;
+execute stmt_show;
+1
+1
+execute stmt_select;
+i
+1
+execute stmt_insert;
+ERROR 08S01: WSREP has not yet prepared node for application use
+SET SESSION wsrep_sync_wait=7;
+execute stmt_show;
+1
+1
+execute stmt_select;
+i
+1
+execute stmt_insert;
+ERROR 08S01: WSREP has not yet prepared node for application use
+connection node_2;
+SET @@global.wsrep_dirty_reads=OFF;
+connection node_1;
 SELECT * FROM t1;
 i
 1
 DROP TABLE t1;
+drop user user1;
+drop user user2;
+disconnect node_2;
+disconnect node_1;
 # End of test
diff --git a/mysql-test/suite/galera/t/galera_var_dirty_reads.test b/mysql-test/suite/galera/t/galera_var_dirty_reads.test
index 0c81779..9891849 100644
--- a/mysql-test/suite/galera/t/galera_var_dirty_reads.test
+++ b/mysql-test/suite/galera/t/galera_var_dirty_reads.test
@@ -17,6 +17,11 @@ CREATE TABLE t1(i INT) ENGINE=INNODB;
 INSERT INTO t1 VALUES(1);
 SELECT * FROM t1;
 
+create user user1;
+grant all privileges on *.* to user1;
+create user user2;
+grant all privileges on *.* to user2;
+
 SET @@global.wsrep_cluster_address = '';
 SET @@session.wsrep_dirty_reads=OFF;
 
@@ -41,7 +46,72 @@ SET @@session.wsrep_dirty_reads=OFF;
 SELECT 2;
 SELECT @@max_allowed_packet;
 SELECT 2+2 from DUAL;
-SELECT sysdate() from DUAL;
+
+#Query which does not change data should be allowed MDEV-11479
+SET @@session.wsrep_dirty_reads=ON;
+SELECT * FROM t1;
+
+--enable_connect_log
+--connect (con1, localhost, user1,,test,$NODE_MYPORT_2,$NODE_MYSOCK_2)
+#Just test the session behavior
+SET SESSION wsrep_sync_wait=0;
+select @@session.wsrep_dirty_reads;
+
+set session wsrep_dirty_reads=1;
+#Prepared statement creation should be allowed MDEV-11479
+prepare stmt_show from 'select 1';
+prepare stmt_select from 'select * from t1';
+prepare stmt_insert from 'insert into t1 values(1)';
+set session wsrep_dirty_reads=0;
+
+#No Preapare stmt/proceure will be allowed
+--error ER_UNKNOWN_COM_ERROR
+execute stmt_show;
+--error ER_UNKNOWN_COM_ERROR
+execute stmt_select;
+--error ER_UNKNOWN_COM_ERROR
+execute stmt_insert;
+
+SET wsrep_dirty_reads=ON;
+select @@session.wsrep_dirty_reads;
+#Only prepare statement which does not change data should be allowed
+execute stmt_show;
+execute stmt_select;
+--error ER_UNKNOWN_COM_ERROR
+execute stmt_insert;
+SET @@global.wsrep_dirty_reads=ON;
+
+--connect (con2, localhost, user2,,test,$NODE_MYPORT_2,$NODE_MYSOCK_2)
+#Just test the session behavior
+select @@session.wsrep_dirty_reads;
+
+prepare stmt_show from 'select 1';
+prepare stmt_select from 'select * from t1';
+prepare stmt_insert from 'insert into t1 values(1)';
+
+#Only prepare statement which does not change data should be allowed
+execute stmt_show;
+execute stmt_select;
+--error ER_UNKNOWN_COM_ERROR
+execute stmt_insert;
+
+#wsrep_dirty_read should work when wsrep_sync_wait is 1 or non zero
+#because we already are disconnected , So It does not make any sense
+#to wait for other nodes
+SET SESSION wsrep_sync_wait=1;
+execute stmt_show;
+execute stmt_select;
+--error ER_UNKNOWN_COM_ERROR
+execute stmt_insert;
+
+SET SESSION wsrep_sync_wait=7;
+execute stmt_show;
+execute stmt_select;
+--error ER_UNKNOWN_COM_ERROR
+execute stmt_insert;
+
+--connection node_2
+SET @@global.wsrep_dirty_reads=OFF;
 
 --disable_query_log
 --eval SET @@global.wsrep_cluster_address = '$wsrep_cluster_address_saved'
@@ -52,6 +122,8 @@ SELECT sysdate() from DUAL;
 SELECT * FROM t1;
 # Cleanup
 DROP TABLE t1;
+drop user user1;
+drop user user2;
 
 # Restore original auto_increment_offset values.
 --source include/auto_increment_offset_restore.inc
diff --git a/mysql-test/suite/sys_vars/r/wsrep_dirty_reads_basic.result b/mysql-test/suite/sys_vars/r/wsrep_dirty_reads_basic.result
index d2a62d6..1968103 100644
--- a/mysql-test/suite/sys_vars/r/wsrep_dirty_reads_basic.result
+++ b/mysql-test/suite/sys_vars/r/wsrep_dirty_reads_basic.result
@@ -5,12 +5,13 @@
 SET @wsrep_dirty_reads_session_saved = @@session.wsrep_dirty_reads;
 # default
 SELECT @@global.wsrep_dirty_reads;
-ERROR HY000: Variable 'wsrep_dirty_reads' is a SESSION variable
+@@global.wsrep_dirty_reads
+0
 SELECT @@session.wsrep_dirty_reads;
 @@session.wsrep_dirty_reads
 0
 
-# scope and valid values
+# valid values for session
 SET @@session.wsrep_dirty_reads=OFF;
 SELECT @@session.wsrep_dirty_reads;
 @@session.wsrep_dirty_reads
@@ -24,11 +25,29 @@ SELECT @@session.wsrep_dirty_reads;
 @@session.wsrep_dirty_reads
 0
 
+# valid values for global
+SET @@global.wsrep_dirty_reads=OFF;
+SELECT @@global.wsrep_dirty_reads;
+@@global.wsrep_dirty_reads
+0
+SET @@global.wsrep_dirty_reads=ON;
+SELECT @@global.wsrep_dirty_reads;
+@@global.wsrep_dirty_reads
+1
+SET @@global.wsrep_dirty_reads=default;
+SELECT @@global.wsrep_dirty_reads;
+@@global.wsrep_dirty_reads
+0
+
 # invalid values
 SET @@session.wsrep_dirty_reads=NULL;
 ERROR 42000: Variable 'wsrep_dirty_reads' can't be set to the value of 'NULL'
 SET @@session.wsrep_dirty_reads='junk';
 ERROR 42000: Variable 'wsrep_dirty_reads' can't be set to the value of 'junk'
+SET @@global.wsrep_dirty_reads=NULL;
+ERROR 42000: Variable 'wsrep_dirty_reads' can't be set to the value of 'NULL'
+SET @@global.wsrep_dirty_reads='junk';
+ERROR 42000: Variable 'wsrep_dirty_reads' can't be set to the value of 'junk'
 
 # restore the initial values
 SET @@session.wsrep_dirty_reads = @wsrep_dirty_reads_session_saved;
diff --git a/mysql-test/suite/sys_vars/t/wsrep_dirty_reads_basic.test b/mysql-test/suite/sys_vars/t/wsrep_dirty_reads_basic.test
index a47524f..ffe767a 100644
--- a/mysql-test/suite/sys_vars/t/wsrep_dirty_reads_basic.test
+++ b/mysql-test/suite/sys_vars/t/wsrep_dirty_reads_basic.test
@@ -8,12 +8,12 @@
 SET @wsrep_dirty_reads_session_saved = @@session.wsrep_dirty_reads;
 
 --echo # default
---error ER_INCORRECT_GLOBAL_LOCAL_VAR
+
 SELECT @@global.wsrep_dirty_reads;
 SELECT @@session.wsrep_dirty_reads;
 
 --echo
---echo # scope and valid values
+--echo # valid values for session
 SET @@session.wsrep_dirty_reads=OFF;
 SELECT @@session.wsrep_dirty_reads;
 SET @@session.wsrep_dirty_reads=ON;
@@ -22,11 +22,24 @@ SET @@session.wsrep_dirty_reads=default;
 SELECT @@session.wsrep_dirty_reads;
 
 --echo
+--echo # valid values for global
+SET @@global.wsrep_dirty_reads=OFF;
+SELECT @@global.wsrep_dirty_reads;
+SET @@global.wsrep_dirty_reads=ON;
+SELECT @@global.wsrep_dirty_reads;
+SET @@global.wsrep_dirty_reads=default;
+SELECT @@global.wsrep_dirty_reads;
+
+--echo
 --echo # invalid values
 --error ER_WRONG_VALUE_FOR_VAR
 SET @@session.wsrep_dirty_reads=NULL;
 --error ER_WRONG_VALUE_FOR_VAR
 SET @@session.wsrep_dirty_reads='junk';
+--error ER_WRONG_VALUE_FOR_VAR
+SET @@global.wsrep_dirty_reads=NULL;
+--error ER_WRONG_VALUE_FOR_VAR
+SET @@global.wsrep_dirty_reads='junk';
 
 --echo
 --echo # restore the initial values
diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc
index 7d5c59f..008393f 100644
--- a/sql/sql_parse.cc
+++ b/sql/sql_parse.cc
@@ -2155,6 +2155,15 @@ int prepare_schema_table(THD *thd, LEX *lex, Table_ident *table_ident,
 }
 
 
+/*
+  See whether the command changes the data or not.
+ */
+static int inline
+command_changes_data(uint sql_command)
+{
+  return (sql_command_flags[sql_command] &
+          CF_CHANGES_DATA) != 0;
+}
 /**
   Read query from packet and store in thd->query.
   Used in COM_QUERY and COM_STMT_PREPARE.
@@ -2656,7 +2665,7 @@ mysql_execute_command(THD *thd)
     if (lex->sql_command != SQLCOM_SET_OPTION  &&
         !wsrep_is_show_query(lex->sql_command) &&
         !(thd->variables.wsrep_dirty_reads     &&
-          lex->sql_command == SQLCOM_SELECT)   &&
+       !command_changes_data(lex->sql_command))&&
         !(lex->sql_command == SQLCOM_SELECT    &&
           !all_tables)                         &&
         !wsrep_node_is_ready(thd))
@@ -2934,8 +2943,14 @@ mysql_execute_command(THD *thd)
   case SQLCOM_SHOW_FIELDS:
   case SQLCOM_SHOW_KEYS:
   case SQLCOM_SELECT:
-    if (WSREP_CLIENT(thd) && wsrep_sync_wait(thd))
-      goto error;
+   /*
+      wsrep_dirty_read should work when wsrep_sync_wait is 1 or  non-zero
+      because we already are disconnected. So, it does not make any sense
+      to wait for other nodes
+    */
+    if (WSREP_CLIENT(thd) && (wsrep_ready || !thd->variables.wsrep_dirty_reads)
+        && wsrep_sync_wait(thd))
+        goto error;
 
   case SQLCOM_SHOW_PLUGINS:
   case SQLCOM_SHOW_VARIABLES:
diff --git a/sql/sys_vars.cc b/sql/sys_vars.cc
index 0518e43..42fd46c 100644
--- a/sql/sys_vars.cc
+++ b/sql/sys_vars.cc
@@ -4977,8 +4977,8 @@ static Sys_var_mybool Sys_wsrep_restart_slave(
 
 static Sys_var_mybool Sys_wsrep_dirty_reads(
        "wsrep_dirty_reads", "Do not reject SELECT queries even when the node "
-       "is not ready.", SESSION_ONLY(wsrep_dirty_reads), NO_CMD_LINE,
-       DEFAULT(FALSE), NO_MUTEX_GUARD, NOT_IN_BINLOG);
+       "is not ready.", SESSION_VAR(wsrep_dirty_reads),
+       CMD_LINE(OPT_ARG), DEFAULT(FALSE), NO_MUTEX_GUARD, NOT_IN_BINLOG);
 
 static Sys_var_uint Sys_wsrep_gtid_domain_id(
        "wsrep_gtid_domain_id", "When wsrep_gtid_mode is set, this value is "
diff --git a/sql/wsrep_mysqld.cc b/sql/wsrep_mysqld.cc
index 0deb19d..a6f55b4 100644
--- a/sql/wsrep_mysqld.cc
+++ b/sql/wsrep_mysqld.cc
@@ -94,6 +94,8 @@ bool wsrep_new_cluster                 = false; // Bootstrap the cluster ?
 
 // Use wsrep_gtid_domain_id for galera transactions?
 bool wsrep_gtid_mode                   = 0;
+my_bool wsrep_dirty_reads              = false; // is dirty reads allowed for
+                                                // disconnected/non major node
 // gtid_domain_id for galera transactions.
 uint32 wsrep_gtid_domain_id            = 0;
 
diff --git a/sql/wsrep_mysqld.h b/sql/wsrep_mysqld.h
index 04ccc1a..8bdfdd5 100644
--- a/sql/wsrep_mysqld.h
+++ b/sql/wsrep_mysqld.h
@@ -88,6 +88,7 @@ extern my_bool     wsrep_slave_UK_checks;
 extern ulong       wsrep_running_threads;
 extern bool        wsrep_new_cluster;
 extern bool        wsrep_gtid_mode;
+extern my_bool     wsrep_dirty_reads;
 extern uint32      wsrep_gtid_domain_id;
 
 enum enum_wsrep_OSU_method {

References