developers
Threads by month
- ----- 2024 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2023 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2022 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2021 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2020 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2019 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2018 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2017 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2016 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2015 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2014 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2013 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2012 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2011 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2010 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2009 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
February 2010
- 24 participants
- 291 discussions
[Maria-developers] Rev 2753: Subquery backport: Update test results (checked) in file:///home/psergey/dev/maria-5.3-subqueries-r3/
by Sergey Petrunya 08 Feb '10
by Sergey Petrunya 08 Feb '10
08 Feb '10
At file:///home/psergey/dev/maria-5.3-subqueries-r3/
------------------------------------------------------------
revno: 2753
revision-id: psergey(a)askmonty.org-20100208130923-g38q4uiyu90g60w9
parent: psergey(a)askmonty.org-20100208125530-l0o01n6lxk95cn78
committer: Sergey Petrunya <psergey(a)askmonty.org>
branch nick: maria-5.3-subqueries-r3
timestamp: Mon 2010-02-08 15:09:23 +0200
message:
Subquery backport: Update test results (checked)
=== modified file 'mysql-test/r/explain.result'
--- a/mysql-test/r/explain.result 2009-12-15 07:16:46 +0000
+++ b/mysql-test/r/explain.result 2010-02-08 13:09:23 +0000
@@ -171,7 +171,7 @@
EXPLAIN SELECT OUTR.dt FROM t1 AS OUTR WHERE OUTR.dt IN (SELECT INNR.dt FROM t2 AS INNR WHERE OUTR.dt IS NULL );
id select_type table type possible_keys key key_len ref rows Extra
1 PRIMARY OUTR ALL NULL NULL NULL NULL 2 Using where
-2 DEPENDENT SUBQUERY INNR ALL NULL NULL NULL NULL 2 Using where
+1 PRIMARY INNR ALL NULL NULL NULL NULL 2 Using where; FirstMatch(OUTR)
flush tables;
SELECT OUTR.dt FROM t1 AS OUTR WHERE OUTR.dt IN (SELECT INNR.dt FROM t2 AS INNR WHERE OUTR.dt IS NULL );
dt
@@ -179,7 +179,7 @@
EXPLAIN SELECT OUTR.dt FROM t1 AS OUTR WHERE OUTR.dt IN ( SELECT INNR.dt FROM t2 AS INNR WHERE OUTR.t < '2005-11-13 7:41:31' );
id select_type table type possible_keys key key_len ref rows Extra
1 PRIMARY OUTR ALL NULL NULL NULL NULL 2 Using where
-2 DEPENDENT SUBQUERY INNR ALL NULL NULL NULL NULL 2 Using where
+1 PRIMARY INNR ALL NULL NULL NULL NULL 2 Using where; FirstMatch(OUTR)
flush tables;
SELECT OUTR.dt FROM t1 AS OUTR WHERE OUTR.dt IN ( SELECT INNR.dt FROM t2 AS INNR WHERE OUTR.t < '2005-11-13 7:41:31' );
dt
=== modified file 'mysql-test/r/group_min_max.result'
--- a/mysql-test/r/group_min_max.result 2009-08-30 07:03:37 +0000
+++ b/mysql-test/r/group_min_max.result 2010-02-08 13:09:23 +0000
@@ -2256,7 +2256,7 @@
a IN (SELECT max(b) FROM t1 GROUP BY a HAVING a < 2);
id select_type table type possible_keys key key_len ref rows Extra
1 PRIMARY t1_outer index NULL a 10 NULL 15 Using where; Using index
-2 DEPENDENT SUBQUERY t1 index NULL a 10 NULL 1 Using index
+2 SUBQUERY t1 range NULL a 5 NULL 8 Using index for group-by
EXPLAIN SELECT 1 FROM t1 AS t1_outer GROUP BY a HAVING
a > (SELECT max(b) FROM t1 GROUP BY a HAVING a < 2);
id select_type table type possible_keys key key_len ref rows Extra
=== modified file 'mysql-test/r/subselect3_jcl6.result'
--- a/mysql-test/r/subselect3_jcl6.result 2010-01-17 14:51:10 +0000
+++ b/mysql-test/r/subselect3_jcl6.result 2010-02-08 13:09:23 +0000
@@ -1140,7 +1140,7 @@
flush status;
select count(*) from t0 A, t0 B, t0 C, t0 D where D.a in (select a from t1 E);
count(*)
-4999
+5000
show status like 'Created_tmp_disk_tables';
Variable_name Value
Created_tmp_disk_tables 1
1
0
[Maria-developers] Rev 2740: options for CREATE TABLE (MWL#43) (version after first review). in file:///home/bell/maria/bzr/work-maria-5.2-createoptions/
by sanja@askmonty.org 05 Feb '10
by sanja@askmonty.org 05 Feb '10
05 Feb '10
At file:///home/bell/maria/bzr/work-maria-5.2-createoptions/
------------------------------------------------------------
revno: 2740
revision-id: sanja(a)askmonty.org-20100205170316-gg4nio1p81cpmjop
parent: knielsen(a)knielsen-hq.org-20100201190519-b9uktnn90rwwiile
committer: sanja(a)askmonty.org
branch nick: work-maria-5.2-createoptions
timestamp: Fri 2010-02-05 19:03:16 +0200
message:
options for CREATE TABLE (MWL#43) (version after first review).
Diff too large for email (1280 lines, the limit is 1000).
2
1
[Maria-developers] Updated (by Serg): Store in binlog text of statements that caused RBR events (47)
by worklog-noreply@askmonty.org 05 Feb '10
by worklog-noreply@askmonty.org 05 Feb '10
05 Feb '10
-----------------------------------------------------------------------
WORKLOG TASK
-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
TASK...........: Store in binlog text of statements that caused RBR events
CREATION DATE..: Sat, 15 Aug 2009, 23:48
SUPERVISOR.....: Monty
IMPLEMENTOR....:
COPIES TO......: Knielsen, Serg
CATEGORY.......: Server-Sprint
TASK ID........: 47 (http://askmonty.org/worklog/?tid=47)
VERSION........: Server-9.x
STATUS.........: In-Progress
PRIORITY.......: 60
WORKED HOURS...: 20
ESTIMATE.......: 35 (hours remain)
ORIG. ESTIMATE.: 35
PROGRESS NOTES:
-=-=(Serg - Fri, 05 Feb 2010, 14:04)=-=-
Observers changed: Knielsen,Serg
-=-=(Guest - Fri, 05 Feb 2010, 13:40)=-=-
Category updated.
--- /tmp/wklog.47.old.9197 2010-02-05 13:40:36.000000000 +0200
+++ /tmp/wklog.47.new.9197 2010-02-05 13:40:36.000000000 +0200
@@ -1 +1 @@
-Server-RawIdeaBin
+Server-Sprint
-=-=(Guest - Fri, 05 Feb 2010, 13:40)=-=-
Status updated.
--- /tmp/wklog.47.old.9197 2010-02-05 13:40:36.000000000 +0200
+++ /tmp/wklog.47.new.9197 2010-02-05 13:40:36.000000000 +0200
@@ -1 +1 @@
-Un-Assigned
+In-Progress
-=-=(Alexi - Thu, 04 Feb 2010, 09:54)=-=-
Low Level Design modified.
--- /tmp/wklog.47.old.16174 2010-02-04 09:54:13.000000000 +0200
+++ /tmp/wklog.47.new.16174 2010-02-04 09:54:13.000000000 +0200
@@ -171,35 +171,20 @@
5. How slave IO thread requests Annotate_rows events
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-When requesting an event, the slave should inform the master whether
-it should send Annotate_rows events or not. To that end we add a new
-BINLOG_SEND_ANNOTATE_ROWS_EVENT flag used when requesting an event:
+If the replicate-annotate-rows-events option is not set on a slave, there
+is no need for master to send Annotate_rows events to this slave. The slave
+(or mysqlbinlog in remote case), before requesting binlog dump via the
+COM_BINLOG_DUMP command, informs the master whether it should send these
+events by executing the newly added COM_BINLOG_DUMP_OPTIONS_EXT server
+command:
+
+ case COM_BINLOG_DUMP_OPTIONS_EXT:
+ thd->binlog_dump_flags_ext= packet[0];
+ my_ok(thd);
+ break;
- #define BINLOG_DUMP_NON_BLOCK 1
- #define BINLOG_SEND_ANNOTATE_ROWS_EVENT 2
-
- pthread_handler_t handle_slave_io(void *arg)
- { ...
- request_dump(mysql, ...);
- ...
- }
-
- int request_dump(MYSQL* mysql, ...)
- { ...
- if (opt_log_slave_updates &&
- mi->io_thd->variables.binlog_annotate_rows_events)
- binlog_flags|= BINLOG_SEND_ANNOTATE_ROWS_EVENT;
- ...
- int2store(buf + 4, binlog_flags);
- ...
- simple_command(mysql, COM_BINLOG_DUMP, buf, ...);
- ...
- }
-
-NOTE. mysqlbinlog, when remotely requesting BINLOG_DUMP by calling the
-simple_command() function, should also use this flag if it wants (in case
-of the --print-annotate-rows-events option set) to recieve Annotate_rows
-events.
+Note. We add this new command and don't use COM_BINLOG_DUMP to avoid possible
+conflicts with MySQL/Sun.
6. How master executes the request
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -338,10 +323,4 @@
to internals(a)lists.mysql.com about this and suggesting to reserve the
event number.
-Also we should notice the introduction of the BINLOG_SEND_ANNOTATE_ROWS_EVENT
-flag taking into account that MySQL/Sun may also introduce a flag with the
-same value to be used in the request_dump-mysql_binlog_send interface.
-But this is mainly the question of merging: if a conflict concerning this
-flag occur, we may simply change the BINLOG_SEND_ANNOTATE_ROWS_EVENT value
-(this does not require additional changes in the code).
-=-=(Alexi - Sun, 20 Dec 2009, 16:00)=-=-
Low Level Design modified.
--- /tmp/wklog.47.old.19667 2009-12-20 14:00:56.000000000 +0000
+++ /tmp/wklog.47.new.19667 2009-12-20 14:00:56.000000000 +0000
@@ -196,6 +196,11 @@
...
}
+NOTE. mysqlbinlog, when remotely requesting BINLOG_DUMP by calling the
+simple_command() function, should also use this flag if it wants (in case
+of the --print-annotate-rows-events option set) to recieve Annotate_rows
+events.
+
6. How master executes the request
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -212,8 +217,7 @@
Log_event::read_log_event(&log, packet, ...);
...
if ((*packet)[EVENT_TYPE_OFFSET + 1] != ANNOTATE_ROWS_EVENT ||
- flags & BINLOG_SEND_ANNOTATE_ROWS_EVENT ||
- thd->server_id == 0 /* slave == mysqlbinlog */ )
+ flags & BINLOG_SEND_ANNOTATE_ROWS_EVENT)
{
my_net_write(net, packet->ptr(), packet->length());
}
-=-=(Alexi - Sun, 20 Dec 2009, 13:14)=-=-
Low Level Design modified.
--- /tmp/wklog.47.old.11350 2009-12-20 13:14:04.000000000 +0200
+++ /tmp/wklog.47.new.11350 2009-12-20 13:14:04.000000000 +0200
@@ -282,23 +282,18 @@
Annotate_rows_log_event* m_annotate_event;
};
-When the saved Annotate_rows object may be deleted? When all corresponding
-Rows events will be processed, i.e. before processing the first non-Rows
-event (note that Annotate_rows object resides in the binary log *after*
-the (possible) 'BEGIN' Query event which accompanies the rows events; note
-also that this deletion is adjusted with the case when some or all
-corresponding Rows events are filtered out by replicate filter rules):
+The saved Annotate_rows object should be deleted when all corresponding
+Rows events will be processed:
int exec_relay_log_event(THD* thd, Relay_log_info* rli)
{ ...
Log_event *ev= next_event(rli);
...
- if (rli->get_annotate_event() && !IS_RBR_EVENT_TYPE(ev->get_type_code()))
- rli->free_annotate_event();
-
apply_event_and_update_pos(ev, ...);
- if (ev->get_type_code() == ANNOTATE_ROWS_EVENT)
+ if (rli->get_annotate_event() && is_last_rows_event(ev))
+ rli->free_annotate_event();
+ else if (ev->get_type_code() == ANNOTATE_ROWS_EVENT)
rli->set_annotate_event((Annotate_rows_log_event*) ev);
else if (ev->get_type_code() != FORMAT_DESCRIPTION_EVENT)
delete ev;
@@ -307,10 +302,21 @@
where
- #define IS_RBR_EVENT_TYPE(type) ( (type) == TABLE_MAP_EVENT || \
- (type) == WRITE_ROWS_EVENT || \
+ bool is_last_rows_event(Log_event* ev)
+ {
+ Log_event_type type= ev->get_type_code();
+ if (IS_ROWS_EVENT_TYPE(type))
+ {
+ Rows_log_event* rows= (Rows_log_event*)ev;
+ return rows->get_flags(Rows_log_event::STMT_END_F);
+ }
+
+ return 0;
+ }
+
+ #define IS_ROWS_EVENT_TYPE(type) ((type) == WRITE_ROWS_EVENT || \
(type) == UPDATE_ROWS_EVENT || \
- (type) == DELETE_ROWS_EVENT )
+ (type) == DELETE_ROWS_EVENT)
8. General remarks
~~~~~~~~~~~~~~~~~~
-=-=(Alexi - Sun, 20 Dec 2009, 09:29)=-=-
Low Level Design modified.
--- /tmp/wklog.47.old.32726 2009-12-20 07:29:56.000000000 +0000
+++ /tmp/wklog.47.new.32726 2009-12-20 07:29:56.000000000 +0000
@@ -56,7 +56,7 @@
0000006A | 00 | 0 - RESERVED [27]
..... ...
00000081 | 00 | 0 - RESERVED [50]
- 00000082 | 00 | 0 - ANNOTATE_RBR_EVENT [51]
+ 00000082 | 00 | 0 - ANNOTATE_ROWS_EVENT [51]
************************
2. Outline of Annotate_rows event behavior
-=-=(Alexi - Sat, 19 Dec 2009, 16:10)=-=-
Low Level Design modified.
--- /tmp/wklog.47.old.16051 2009-12-19 16:10:48.000000000 +0200
+++ /tmp/wklog.47.new.16051 2009-12-19 16:10:48.000000000 +0200
@@ -253,7 +253,7 @@
thd query to that of the described by the event, i.e. to the query which
caused the subsequent Rows events (see "How Master writes Annotate_rows
events to the binary log" to follow what happens further when the subsequent
-Rows events is applied):
+Rows events are applied):
int Annotate_rows_log_event::do_apply_event(...)
{
-=-=(Alexi - Sat, 19 Dec 2009, 16:02)=-=-
High-Level Specification modified.
--- /tmp/wklog.47.old.15695 2009-12-19 16:02:33.000000000 +0200
+++ /tmp/wklog.47.new.15695 2009-12-19 16:02:33.000000000 +0200
@@ -12,7 +12,7 @@
post-header and contains the query text in its data part. Example:
************************
- ANNOTATE_RBR_EVENT
+ ANNOTATE_ROWS_EVENT
************************
00000220 | B6 A0 2C 4B | time_when = 1261215926
00000224 | 33 | event_type = 51
-=-=(Alexi - Sat, 19 Dec 2009, 15:58)=-=-
Low Level Design modified.
--- /tmp/wklog.47.old.15437 2009-12-19 13:58:12.000000000 +0000
+++ /tmp/wklog.47.new.15437 2009-12-19 13:58:12.000000000 +0000
@@ -1 +1,337 @@
+Content
+~~~~~~~
+ 1. Annotate_rows event number
+ 2. Outline of Annotate_rows event behavior
+ 3. How Master writes Annotate_rows events to the binary log
+ 4. How slave treats replicate-annotate-rows-events option
+ 5. How slave IO thread requests Annotate_rows events
+ 6. How master executes the request
+ 7. How slave SQL thread processes Annotate_rows events
+ 8. General remarks
+
+1. Annotate_rows event number
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+To avoid possible event numbers conflict with MySQL/Sun, we leave a gap
+between the last MySQL event number and the Annotate_rows event number:
+
+ enum Log_event_type
+ { ...
+ INCIDENT_EVENT= 26,
+ // New MySQL event numbers are to be added here
+ MYSQL_EVENTS_END,
+
+ MARIA_EVENTS_BEGIN= 51,
+ // New Maria event numbers start from here
+ ANNOTATE_ROWS_EVENT= 51,
+
+ ENUM_END_EVENT
+ };
+
+together with the corresponding extension of 'post_header_len' array in the
+Format description event. (This extension does not affect the compatibility
+of the binary log). Here is how Format description event looks like with
+this extension:
+
+ ************************
+ FORMAT_DESCRIPTION_EVENT
+ ************************
+ 00000004 | A1 A0 2C 4B | time_when = 1261215905
+ 00000008 | 0F | event_type = 15
+ 00000009 | 64 00 00 00 | server_id = 100
+ 0000000D | 7F 00 00 00 | event_len = 127
+ 00000011 | 83 00 00 00 | log_pos = 00000083
+ 00000015 | 01 00 | flags = LOG_EVENT_BINLOG_IN_USE_F
+ ------------------------
+ 00000017 | 04 00 | binlog_ver = 4
+ 00000019 | 35 2E 32 2E | server_ver = 5.2.0-MariaDB-alpha-debug-log
+ ..... ...
+ 0000004B | A1 A0 2C 4B | time_created = 1261215905
+ 0000004F | 13 | common_header_len = 19
+ ------------------------
+ post_header_len
+ ------------------------
+ 00000050 | 38 | 56 - START_EVENT_V3 [1]
+ ..... ...
+ 00000069 | 02 | 2 - INCIDENT_EVENT [26]
+ 0000006A | 00 | 0 - RESERVED [27]
+ ..... ...
+ 00000081 | 00 | 0 - RESERVED [50]
+ 00000082 | 00 | 0 - ANNOTATE_RBR_EVENT [51]
+ ************************
+
+2. Outline of Annotate_rows event behavior
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+Each Annotate_rows_log_event object has two private members describing the
+corresponding query:
+
+ char *m_query_txt;
+ uint m_query_len;
+
+When the object is created for writing to a binary log, this query is taken
+from 'thd' (for short, below we omit the 'Annotate_rows_log_event::' prefix
+as well as other implementation details):
+
+ Annotate_rows_log_event(THD *thd)
+ {
+ m_query_txt = thd->query();
+ m_query_len = thd->query_length();
+ }
+
+When the object is read from a binary log, the query is taken from the buffer
+containing the binary log representation of the event (this buffer is allocated
+in Log_event object from which all Log events are derived):
+
+ Annotate_rows_log_event(char *buf, uint event_len,
+ Format_description_log_event *desc)
+ {
+ m_query_len = event_len - desc->common_header_len;
+ m_query_txt = buf + desc->common_header_len;
+ }
+
+The events are written to the binary log by the Log_event::write() member
+which calls virtual write_data_header() and write_data_body() members
+("data header" and "post header" are synonym in replication terminology).
+In our case, data header is empty and data body is just the query:
+
+ bool write_data_body(IO_CACHE *file)
+ {
+ return my_b_safe_write(file, (uchar*) m_query_txt, m_query_len);
+ }
+
+Printing the event is just printing the query:
+
+ void Annotate_rows_log_event::print(FILE *file, PRINT_EVENT_INFO *pinfo)
+ {
+ my_b_printf(&pinfo->head_cache, "\tQuery: `%s`\n", m_query_txt);
+ }
+
+3. How Master writes Annotate_rows events to the binary log
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+The event is written to the binary log just before the group of Table_map
+events which precede corresponding Rows events (one query may generate
+several Table map events in the binary log, but the corresponding
+Annotate_rows event must be written only once before the first Table map
+event; hence the boolean variable 'with_annotate' below):
+
+ int write_locked_table_maps(THD *thd)
+ { ...
+ bool with_annotate= thd->variables.binlog_annotate_rows_events;
+ ...
+ for (uint i= 0; i < ... <number of tables> ...; ++i)
+ { ...
+ thd->binlog_write_table_map(table, ..., with_annotate);
+ with_annotate= 0; // write Annotate_event not more than once
+ ...
+ }
+ ...
+ }
+
+ int THD::binlog_write_table_map(TABLE *table, ..., bool with_annotate)
+ { ...
+ Table_map_log_event the_event(...);
+ ...
+ if (with_annotate)
+ {
+ Annotate_rows_log_event anno(this);
+ mysql_bin_log.write(&anno);
+ }
+
+ mysql_bin_log.write(&the_event);
+ ...
+ }
+
+4. How slave treats replicate-annotate-rows-events option
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+The replicate-annotate-rows-events option is treated just as the session
+value of the binlog_annotate_rows_events variable for the slave IO and
+SQL threads. This setting is done during initialization of these threads:
+
+ pthread_handler_t handle_slave_io(void *arg)
+ {
+ THD *thd= new THD;
+ ...
+ init_slave_thread(thd, SLAVE_THD_IO);
+ ...
+ }
+
+ pthread_handler_t handle_slave_sql(void *arg)
+ {
+ THD *thd= new THD;
+ ...
+ init_slave_thread(thd, SLAVE_THD_SQL);
+ ...
+ }
+
+ int init_slave_thread(THD* thd, SLAVE_THD_TYPE thd_type)
+ { ...
+ thd->variables.binlog_annotate_rows_events=
+ opt_replicate_annotate_rows_events;
+ ...
+ }
+
+5. How slave IO thread requests Annotate_rows events
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+When requesting an event, the slave should inform the master whether
+it should send Annotate_rows events or not. To that end we add a new
+BINLOG_SEND_ANNOTATE_ROWS_EVENT flag used when requesting an event:
+
+ #define BINLOG_DUMP_NON_BLOCK 1
+ #define BINLOG_SEND_ANNOTATE_ROWS_EVENT 2
+
+ pthread_handler_t handle_slave_io(void *arg)
+ { ...
+ request_dump(mysql, ...);
+ ...
+ }
+
+ int request_dump(MYSQL* mysql, ...)
+ { ...
+ if (opt_log_slave_updates &&
+ mi->io_thd->variables.binlog_annotate_rows_events)
+ binlog_flags|= BINLOG_SEND_ANNOTATE_ROWS_EVENT;
+ ...
+ int2store(buf + 4, binlog_flags);
+ ...
+ simple_command(mysql, COM_BINLOG_DUMP, buf, ...);
+ ...
+ }
+
+6. How master executes the request
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+ case COM_BINLOG_DUMP:
+ { ...
+ flags= uint2korr(packet + 4);
+ ...
+ mysql_binlog_send(thd, ..., flags);
+ ...
+ }
+
+ void mysql_binlog_send(THD* thd, ..., ushort flags)
+ { ...
+ Log_event::read_log_event(&log, packet, ...);
+ ...
+ if ((*packet)[EVENT_TYPE_OFFSET + 1] != ANNOTATE_ROWS_EVENT ||
+ flags & BINLOG_SEND_ANNOTATE_ROWS_EVENT ||
+ thd->server_id == 0 /* slave == mysqlbinlog */ )
+ {
+ my_net_write(net, packet->ptr(), packet->length());
+ }
+ ...
+ }
+
+7. How slave SQL thread processes Annotate_rows events
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+The slave processes each recieved event by "applying" it, i.e. by
+calling the Log_event::apply_event() function which in turn calls
+the virtual do_apply_event() member specific for each type of the
+event.
+
+ int exec_relay_log_event(THD* thd, Relay_log_info* rli)
+ { ...
+ Log_event *ev = next_event(rli);
+ ...
+ apply_event_and_update_pos(ev, ...);
+
+ if (ev->get_type_code() != FORMAT_DESCRIPTION_EVENT)
+ delete ev;
+ ...
+ }
+
+ int apply_event_and_update_pos(Log_event *ev, ...)
+ { ...
+ ev->apply_event(...);
+ ...
+ }
+
+ int Log_event::apply_event(...)
+ {
+ return do_apply_event(...);
+ }
+
+What does it mean to "apply" an Annotate_rows event? It means to set current
+thd query to that of the described by the event, i.e. to the query which
+caused the subsequent Rows events (see "How Master writes Annotate_rows
+events to the binary log" to follow what happens further when the subsequent
+Rows events is applied):
+
+ int Annotate_rows_log_event::do_apply_event(...)
+ {
+ thd->set_query(m_query_txt, m_query_len);
+ }
+
+NOTE. I am not sure, but possibly current values of thd->query and
+thd->query_length should be saved before calling set_query() and to be
+restored on the Annotate_rows_log_event object deletion.
+Is it really needed ?
+
+After calling this do_apply_event() function we may not delete the
+Annotate_rows_log_event object immediatedly (see exec_relay_log_event()
+above) because thd->query now points to the string inside this object.
+We may keep the pointer to this object in the Relay_log_info:
+
+ class Relay_log_info
+ {
+ public:
+ ...
+ void set_annotate_event(Annotate_rows_log_event*);
+ Annotate_rows_log_event* get_annotate_event();
+ void free_annotate_event();
+ ...
+ private:
+ Annotate_rows_log_event* m_annotate_event;
+ };
+
+When the saved Annotate_rows object may be deleted? When all corresponding
+Rows events will be processed, i.e. before processing the first non-Rows
+event (note that Annotate_rows object resides in the binary log *after*
+the (possible) 'BEGIN' Query event which accompanies the rows events; note
+also that this deletion is adjusted with the case when some or all
+corresponding Rows events are filtered out by replicate filter rules):
+
+ int exec_relay_log_event(THD* thd, Relay_log_info* rli)
+ { ...
+ Log_event *ev= next_event(rli);
+ ...
+ if (rli->get_annotate_event() && !IS_RBR_EVENT_TYPE(ev->get_type_code()))
+ rli->free_annotate_event();
+
+ apply_event_and_update_pos(ev, ...);
+
+ if (ev->get_type_code() == ANNOTATE_ROWS_EVENT)
+ rli->set_annotate_event((Annotate_rows_log_event*) ev);
+ else if (ev->get_type_code() != FORMAT_DESCRIPTION_EVENT)
+ delete ev;
+ ...
+ }
+
+where
+
+ #define IS_RBR_EVENT_TYPE(type) ( (type) == TABLE_MAP_EVENT || \
+ (type) == WRITE_ROWS_EVENT || \
+ (type) == UPDATE_ROWS_EVENT || \
+ (type) == DELETE_ROWS_EVENT )
+
+8. General remarks
+~~~~~~~~~~~~~~~~~~
+Kristian noticed that introducing new log event type should be coordinated
+somehow with MySQL/Sun:
+
+ Kristian: The numeric code for this event must be assigned carefully.
+ It should be coordinated with MySQL/Sun, otherwise we can get into a
+ situation where MySQL uses the same numeric code for one event that
+ MariaDB uses for ANNOTATE_ROWS_EVENT, which would make merging the two
+ impossible.
+ Alex: I reserved about 20 numbers not to have possible conflicts
+ with MySQL.
+ Kristian: Still, I think it would be appropriate to send a polite email
+ to internals(a)lists.mysql.com about this and suggesting to reserve the
+ event number.
+
+Also we should notice the introduction of the BINLOG_SEND_ANNOTATE_ROWS_EVENT
+flag taking into account that MySQL/Sun may also introduce a flag with the
+same value to be used in the request_dump-mysql_binlog_send interface.
+But this is mainly the question of merging: if a conflict concerning this
+flag occur, we may simply change the BINLOG_SEND_ANNOTATE_ROWS_EVENT value
+(this does not require additional changes in the code).
------------------------------------------------------------
-=-=(View All Progress Notes, 23 total)=-=-
http://askmonty.org/worklog/index.pl?tid=47&nolimit=1
DESCRIPTION:
Store in binlog (and show in mysqlbinlog output) texts of statements that
caused RBR events
This is needed for (list from Monty):
- Easier to understand why updates happened
- Would make it easier to find out where in application things went
wrong (as you can search for exact strings)
- Allow one to filter things based on comments in the statement.
The cost of this can be that the binlog will be approximately 2x in size
(especially insert of big blob's would be a bit painful), so this should
be an optional feature.
HIGH-LEVEL SPECIFICATION:
Content
~~~~~~~
1. Annotate_rows_log_event
2. Server option: --binlog-annotate-rows-events
3. Server option: --replicate-annotate-rows-events
4. mysqlbinlog option: --print-annotate-rows-events
5. mysqlbinlog output
1. Annotate_rows_log_event [ ANNOTATE_ROWS_EVENT ]
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Describes the query which caused the corresponding rows events. Has empty
post-header and contains the query text in its data part. Example:
************************
ANNOTATE_ROWS_EVENT
************************
00000220 | B6 A0 2C 4B | time_when = 1261215926
00000224 | 33 | event_type = 51
00000225 | 64 00 00 00 | server_id = 100
00000229 | 36 00 00 00 | event_len = 54
0000022D | 56 02 00 00 | log_pos = 00000256
00000231 | 00 00 | flags = <none>
------------------------
00000233 | 49 4E 53 45 | query = "INSERT INTO t1 VALUES (1), (2), (3)"
00000237 | 52 54 20 49 |
0000023B | 4E 54 4F 20 |
0000023F | 74 31 20 56 |
00000243 | 41 4C 55 45 |
00000247 | 53 20 28 31 |
0000024B | 29 2C 20 28 |
0000024F | 32 29 2C 20 |
00000253 | 28 33 29 |
************************
In binary log, Annotate_rows event follows the (possible) 'BEGIN' Query event
and precedes the first of Table map events which accompany the corresponding
rows events. (See example in the "mysqlbinlog output" section below.)
2. Server option: --binlog-annotate-rows-events
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Tells the master to write Annotate_rows events to the binary log.
* Variable Name: binlog_annotate_rows_events
* Scope: Global & Session
* Access Type: Dynamic
* Data Type: bool
* Default Value: OFF
NOTE. Session values allows to annotate only some selected statements:
...
SET SESSION binlog_annotate_rows_events=ON;
... statements to be annotated ...
SET SESSION binlog_annotate_rows_events=OFF;
... statements not to be annotated ...
3. Server option: --replicate-annotate-rows-events
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Tells the slave to reproduce Annotate_rows events recieved from the master
in its own binary log (sensible only in pair with log-slave-updates option).
* Variable Name: replicate_annotate_rows_events
* Scope: Global
* Access Type: Read only
* Data Type: bool
* Default Value: OFF
NOTE. Why do we additionally need this 'replicate' option? Why not to make
the slave to reproduce this events when its binlog-annotate-rows-events
global value is ON? Well, because, for example, we may want to configure
the slave which should reproduce Annotate_rows events but has global
binlog-annotate-rows-events = OFF meaning this to be the default value for
the client threads (see also "How slave treats replicate-annotate-rows-events
option" in LLD part).
4. mysqlbinlog option: --print-annotate-rows-events
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
With this option, mysqlbinlog prints the content of Annotate_rows events (if
the binary log does contain them). Without this option (i.e. by default),
mysqlbinlog skips Annotate_rows events.
5. mysqlbinlog output
~~~~~~~~~~~~~~~~~~~~~
With --print-annotate-rows-events, mysqlbinlog outputs Annotate_rows events
in a form like this:
...
# at 1646
#091219 12:45:26 server id 100 end_log_pos 1714 Query thread_id=1
exec_time=0 error_code=0
SET TIMESTAMP=1261215926/*!*/;
BEGIN
/*!*/;
# at 1714
# at 1812
# at 1853
# at 1894
# at 1938
#091219 12:45:26 server id 100 end_log_pos 1812 Query: `DELETE t1, t2 FROM
t1 INNER JOIN t2 INNER JOIN t3 WHERE t1.a=t2.a AND t2.a=t3.a`
#091219 12:45:26 server id 100 end_log_pos 1853 Table_map: `test`.`t1`
mapped to number 16
#091219 12:45:26 server id 100 end_log_pos 1894 Table_map: `test`.`t2`
mapped to number 17
#091219 12:45:26 server id 100 end_log_pos 1938 Delete_rows: table id 16
#091219 12:45:26 server id 100 end_log_pos 1982 Delete_rows: table id 17
flags: STMT_END_F
...
LOW-LEVEL DESIGN:
Content
~~~~~~~
1. Annotate_rows event number
2. Outline of Annotate_rows event behavior
3. How Master writes Annotate_rows events to the binary log
4. How slave treats replicate-annotate-rows-events option
5. How slave IO thread requests Annotate_rows events
6. How master executes the request
7. How slave SQL thread processes Annotate_rows events
8. General remarks
1. Annotate_rows event number
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
To avoid possible event numbers conflict with MySQL/Sun, we leave a gap
between the last MySQL event number and the Annotate_rows event number:
enum Log_event_type
{ ...
INCIDENT_EVENT= 26,
// New MySQL event numbers are to be added here
MYSQL_EVENTS_END,
MARIA_EVENTS_BEGIN= 51,
// New Maria event numbers start from here
ANNOTATE_ROWS_EVENT= 51,
ENUM_END_EVENT
};
together with the corresponding extension of 'post_header_len' array in the
Format description event. (This extension does not affect the compatibility
of the binary log). Here is how Format description event looks like with
this extension:
************************
FORMAT_DESCRIPTION_EVENT
************************
00000004 | A1 A0 2C 4B | time_when = 1261215905
00000008 | 0F | event_type = 15
00000009 | 64 00 00 00 | server_id = 100
0000000D | 7F 00 00 00 | event_len = 127
00000011 | 83 00 00 00 | log_pos = 00000083
00000015 | 01 00 | flags = LOG_EVENT_BINLOG_IN_USE_F
------------------------
00000017 | 04 00 | binlog_ver = 4
00000019 | 35 2E 32 2E | server_ver = 5.2.0-MariaDB-alpha-debug-log
..... ...
0000004B | A1 A0 2C 4B | time_created = 1261215905
0000004F | 13 | common_header_len = 19
------------------------
post_header_len
------------------------
00000050 | 38 | 56 - START_EVENT_V3 [1]
..... ...
00000069 | 02 | 2 - INCIDENT_EVENT [26]
0000006A | 00 | 0 - RESERVED [27]
..... ...
00000081 | 00 | 0 - RESERVED [50]
00000082 | 00 | 0 - ANNOTATE_ROWS_EVENT [51]
************************
2. Outline of Annotate_rows event behavior
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Each Annotate_rows_log_event object has two private members describing the
corresponding query:
char *m_query_txt;
uint m_query_len;
When the object is created for writing to a binary log, this query is taken
from 'thd' (for short, below we omit the 'Annotate_rows_log_event::' prefix
as well as other implementation details):
Annotate_rows_log_event(THD *thd)
{
m_query_txt = thd->query();
m_query_len = thd->query_length();
}
When the object is read from a binary log, the query is taken from the buffer
containing the binary log representation of the event (this buffer is allocated
in Log_event object from which all Log events are derived):
Annotate_rows_log_event(char *buf, uint event_len,
Format_description_log_event *desc)
{
m_query_len = event_len - desc->common_header_len;
m_query_txt = buf + desc->common_header_len;
}
The events are written to the binary log by the Log_event::write() member
which calls virtual write_data_header() and write_data_body() members
("data header" and "post header" are synonym in replication terminology).
In our case, data header is empty and data body is just the query:
bool write_data_body(IO_CACHE *file)
{
return my_b_safe_write(file, (uchar*) m_query_txt, m_query_len);
}
Printing the event is just printing the query:
void Annotate_rows_log_event::print(FILE *file, PRINT_EVENT_INFO *pinfo)
{
my_b_printf(&pinfo->head_cache, "\tQuery: `%s`\n", m_query_txt);
}
3. How Master writes Annotate_rows events to the binary log
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
The event is written to the binary log just before the group of Table_map
events which precede corresponding Rows events (one query may generate
several Table map events in the binary log, but the corresponding
Annotate_rows event must be written only once before the first Table map
event; hence the boolean variable 'with_annotate' below):
int write_locked_table_maps(THD *thd)
{ ...
bool with_annotate= thd->variables.binlog_annotate_rows_events;
...
for (uint i= 0; i < ... <number of tables> ...; ++i)
{ ...
thd->binlog_write_table_map(table, ..., with_annotate);
with_annotate= 0; // write Annotate_event not more than once
...
}
...
}
int THD::binlog_write_table_map(TABLE *table, ..., bool with_annotate)
{ ...
Table_map_log_event the_event(...);
...
if (with_annotate)
{
Annotate_rows_log_event anno(this);
mysql_bin_log.write(&anno);
}
mysql_bin_log.write(&the_event);
...
}
4. How slave treats replicate-annotate-rows-events option
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
The replicate-annotate-rows-events option is treated just as the session
value of the binlog_annotate_rows_events variable for the slave IO and
SQL threads. This setting is done during initialization of these threads:
pthread_handler_t handle_slave_io(void *arg)
{
THD *thd= new THD;
...
init_slave_thread(thd, SLAVE_THD_IO);
...
}
pthread_handler_t handle_slave_sql(void *arg)
{
THD *thd= new THD;
...
init_slave_thread(thd, SLAVE_THD_SQL);
...
}
int init_slave_thread(THD* thd, SLAVE_THD_TYPE thd_type)
{ ...
thd->variables.binlog_annotate_rows_events=
opt_replicate_annotate_rows_events;
...
}
5. How slave IO thread requests Annotate_rows events
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
If the replicate-annotate-rows-events option is not set on a slave, there
is no need for master to send Annotate_rows events to this slave. The slave
(or mysqlbinlog in remote case), before requesting binlog dump via the
COM_BINLOG_DUMP command, informs the master whether it should send these
events by executing the newly added COM_BINLOG_DUMP_OPTIONS_EXT server
command:
case COM_BINLOG_DUMP_OPTIONS_EXT:
thd->binlog_dump_flags_ext= packet[0];
my_ok(thd);
break;
Note. We add this new command and don't use COM_BINLOG_DUMP to avoid possible
conflicts with MySQL/Sun.
6. How master executes the request
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
case COM_BINLOG_DUMP:
{ ...
flags= uint2korr(packet + 4);
...
mysql_binlog_send(thd, ..., flags);
...
}
void mysql_binlog_send(THD* thd, ..., ushort flags)
{ ...
Log_event::read_log_event(&log, packet, ...);
...
if ((*packet)[EVENT_TYPE_OFFSET + 1] != ANNOTATE_ROWS_EVENT ||
flags & BINLOG_SEND_ANNOTATE_ROWS_EVENT)
{
my_net_write(net, packet->ptr(), packet->length());
}
...
}
7. How slave SQL thread processes Annotate_rows events
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
The slave processes each recieved event by "applying" it, i.e. by
calling the Log_event::apply_event() function which in turn calls
the virtual do_apply_event() member specific for each type of the
event.
int exec_relay_log_event(THD* thd, Relay_log_info* rli)
{ ...
Log_event *ev = next_event(rli);
...
apply_event_and_update_pos(ev, ...);
if (ev->get_type_code() != FORMAT_DESCRIPTION_EVENT)
delete ev;
...
}
int apply_event_and_update_pos(Log_event *ev, ...)
{ ...
ev->apply_event(...);
...
}
int Log_event::apply_event(...)
{
return do_apply_event(...);
}
What does it mean to "apply" an Annotate_rows event? It means to set current
thd query to that of the described by the event, i.e. to the query which
caused the subsequent Rows events (see "How Master writes Annotate_rows
events to the binary log" to follow what happens further when the subsequent
Rows events are applied):
int Annotate_rows_log_event::do_apply_event(...)
{
thd->set_query(m_query_txt, m_query_len);
}
NOTE. I am not sure, but possibly current values of thd->query and
thd->query_length should be saved before calling set_query() and to be
restored on the Annotate_rows_log_event object deletion.
Is it really needed ?
After calling this do_apply_event() function we may not delete the
Annotate_rows_log_event object immediatedly (see exec_relay_log_event()
above) because thd->query now points to the string inside this object.
We may keep the pointer to this object in the Relay_log_info:
class Relay_log_info
{
public:
...
void set_annotate_event(Annotate_rows_log_event*);
Annotate_rows_log_event* get_annotate_event();
void free_annotate_event();
...
private:
Annotate_rows_log_event* m_annotate_event;
};
The saved Annotate_rows object should be deleted when all corresponding
Rows events will be processed:
int exec_relay_log_event(THD* thd, Relay_log_info* rli)
{ ...
Log_event *ev= next_event(rli);
...
apply_event_and_update_pos(ev, ...);
if (rli->get_annotate_event() && is_last_rows_event(ev))
rli->free_annotate_event();
else if (ev->get_type_code() == ANNOTATE_ROWS_EVENT)
rli->set_annotate_event((Annotate_rows_log_event*) ev);
else if (ev->get_type_code() != FORMAT_DESCRIPTION_EVENT)
delete ev;
...
}
where
bool is_last_rows_event(Log_event* ev)
{
Log_event_type type= ev->get_type_code();
if (IS_ROWS_EVENT_TYPE(type))
{
Rows_log_event* rows= (Rows_log_event*)ev;
return rows->get_flags(Rows_log_event::STMT_END_F);
}
return 0;
}
#define IS_ROWS_EVENT_TYPE(type) ((type) == WRITE_ROWS_EVENT || \
(type) == UPDATE_ROWS_EVENT || \
(type) == DELETE_ROWS_EVENT)
8. General remarks
~~~~~~~~~~~~~~~~~~
Kristian noticed that introducing new log event type should be coordinated
somehow with MySQL/Sun:
Kristian: The numeric code for this event must be assigned carefully.
It should be coordinated with MySQL/Sun, otherwise we can get into a
situation where MySQL uses the same numeric code for one event that
MariaDB uses for ANNOTATE_ROWS_EVENT, which would make merging the two
impossible.
Alex: I reserved about 20 numbers not to have possible conflicts
with MySQL.
Kristian: Still, I think it would be appropriate to send a polite email
to internals(a)lists.mysql.com about this and suggesting to reserve the
event number.
ESTIMATED WORK TIME
ESTIMATED COMPLETION DATE
-----------------------------------------------------------------------
WorkLog (v3.5.9)
1
0
[Maria-developers] Updated (by Serg): Store in binlog text of statements that caused RBR events (47)
by worklog-noreply@askmonty.org 05 Feb '10
by worklog-noreply@askmonty.org 05 Feb '10
05 Feb '10
-----------------------------------------------------------------------
WORKLOG TASK
-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
TASK...........: Store in binlog text of statements that caused RBR events
CREATION DATE..: Sat, 15 Aug 2009, 23:48
SUPERVISOR.....: Monty
IMPLEMENTOR....:
COPIES TO......: Knielsen, Serg
CATEGORY.......: Server-Sprint
TASK ID........: 47 (http://askmonty.org/worklog/?tid=47)
VERSION........: Server-9.x
STATUS.........: In-Progress
PRIORITY.......: 60
WORKED HOURS...: 20
ESTIMATE.......: 35 (hours remain)
ORIG. ESTIMATE.: 35
PROGRESS NOTES:
-=-=(Serg - Fri, 05 Feb 2010, 14:04)=-=-
Observers changed: Knielsen,Serg
-=-=(Guest - Fri, 05 Feb 2010, 13:40)=-=-
Category updated.
--- /tmp/wklog.47.old.9197 2010-02-05 13:40:36.000000000 +0200
+++ /tmp/wklog.47.new.9197 2010-02-05 13:40:36.000000000 +0200
@@ -1 +1 @@
-Server-RawIdeaBin
+Server-Sprint
-=-=(Guest - Fri, 05 Feb 2010, 13:40)=-=-
Status updated.
--- /tmp/wklog.47.old.9197 2010-02-05 13:40:36.000000000 +0200
+++ /tmp/wklog.47.new.9197 2010-02-05 13:40:36.000000000 +0200
@@ -1 +1 @@
-Un-Assigned
+In-Progress
-=-=(Alexi - Thu, 04 Feb 2010, 09:54)=-=-
Low Level Design modified.
--- /tmp/wklog.47.old.16174 2010-02-04 09:54:13.000000000 +0200
+++ /tmp/wklog.47.new.16174 2010-02-04 09:54:13.000000000 +0200
@@ -171,35 +171,20 @@
5. How slave IO thread requests Annotate_rows events
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-When requesting an event, the slave should inform the master whether
-it should send Annotate_rows events or not. To that end we add a new
-BINLOG_SEND_ANNOTATE_ROWS_EVENT flag used when requesting an event:
+If the replicate-annotate-rows-events option is not set on a slave, there
+is no need for master to send Annotate_rows events to this slave. The slave
+(or mysqlbinlog in remote case), before requesting binlog dump via the
+COM_BINLOG_DUMP command, informs the master whether it should send these
+events by executing the newly added COM_BINLOG_DUMP_OPTIONS_EXT server
+command:
+
+ case COM_BINLOG_DUMP_OPTIONS_EXT:
+ thd->binlog_dump_flags_ext= packet[0];
+ my_ok(thd);
+ break;
- #define BINLOG_DUMP_NON_BLOCK 1
- #define BINLOG_SEND_ANNOTATE_ROWS_EVENT 2
-
- pthread_handler_t handle_slave_io(void *arg)
- { ...
- request_dump(mysql, ...);
- ...
- }
-
- int request_dump(MYSQL* mysql, ...)
- { ...
- if (opt_log_slave_updates &&
- mi->io_thd->variables.binlog_annotate_rows_events)
- binlog_flags|= BINLOG_SEND_ANNOTATE_ROWS_EVENT;
- ...
- int2store(buf + 4, binlog_flags);
- ...
- simple_command(mysql, COM_BINLOG_DUMP, buf, ...);
- ...
- }
-
-NOTE. mysqlbinlog, when remotely requesting BINLOG_DUMP by calling the
-simple_command() function, should also use this flag if it wants (in case
-of the --print-annotate-rows-events option set) to recieve Annotate_rows
-events.
+Note. We add this new command and don't use COM_BINLOG_DUMP to avoid possible
+conflicts with MySQL/Sun.
6. How master executes the request
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -338,10 +323,4 @@
to internals(a)lists.mysql.com about this and suggesting to reserve the
event number.
-Also we should notice the introduction of the BINLOG_SEND_ANNOTATE_ROWS_EVENT
-flag taking into account that MySQL/Sun may also introduce a flag with the
-same value to be used in the request_dump-mysql_binlog_send interface.
-But this is mainly the question of merging: if a conflict concerning this
-flag occur, we may simply change the BINLOG_SEND_ANNOTATE_ROWS_EVENT value
-(this does not require additional changes in the code).
-=-=(Alexi - Sun, 20 Dec 2009, 16:00)=-=-
Low Level Design modified.
--- /tmp/wklog.47.old.19667 2009-12-20 14:00:56.000000000 +0000
+++ /tmp/wklog.47.new.19667 2009-12-20 14:00:56.000000000 +0000
@@ -196,6 +196,11 @@
...
}
+NOTE. mysqlbinlog, when remotely requesting BINLOG_DUMP by calling the
+simple_command() function, should also use this flag if it wants (in case
+of the --print-annotate-rows-events option set) to recieve Annotate_rows
+events.
+
6. How master executes the request
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -212,8 +217,7 @@
Log_event::read_log_event(&log, packet, ...);
...
if ((*packet)[EVENT_TYPE_OFFSET + 1] != ANNOTATE_ROWS_EVENT ||
- flags & BINLOG_SEND_ANNOTATE_ROWS_EVENT ||
- thd->server_id == 0 /* slave == mysqlbinlog */ )
+ flags & BINLOG_SEND_ANNOTATE_ROWS_EVENT)
{
my_net_write(net, packet->ptr(), packet->length());
}
-=-=(Alexi - Sun, 20 Dec 2009, 13:14)=-=-
Low Level Design modified.
--- /tmp/wklog.47.old.11350 2009-12-20 13:14:04.000000000 +0200
+++ /tmp/wklog.47.new.11350 2009-12-20 13:14:04.000000000 +0200
@@ -282,23 +282,18 @@
Annotate_rows_log_event* m_annotate_event;
};
-When the saved Annotate_rows object may be deleted? When all corresponding
-Rows events will be processed, i.e. before processing the first non-Rows
-event (note that Annotate_rows object resides in the binary log *after*
-the (possible) 'BEGIN' Query event which accompanies the rows events; note
-also that this deletion is adjusted with the case when some or all
-corresponding Rows events are filtered out by replicate filter rules):
+The saved Annotate_rows object should be deleted when all corresponding
+Rows events will be processed:
int exec_relay_log_event(THD* thd, Relay_log_info* rli)
{ ...
Log_event *ev= next_event(rli);
...
- if (rli->get_annotate_event() && !IS_RBR_EVENT_TYPE(ev->get_type_code()))
- rli->free_annotate_event();
-
apply_event_and_update_pos(ev, ...);
- if (ev->get_type_code() == ANNOTATE_ROWS_EVENT)
+ if (rli->get_annotate_event() && is_last_rows_event(ev))
+ rli->free_annotate_event();
+ else if (ev->get_type_code() == ANNOTATE_ROWS_EVENT)
rli->set_annotate_event((Annotate_rows_log_event*) ev);
else if (ev->get_type_code() != FORMAT_DESCRIPTION_EVENT)
delete ev;
@@ -307,10 +302,21 @@
where
- #define IS_RBR_EVENT_TYPE(type) ( (type) == TABLE_MAP_EVENT || \
- (type) == WRITE_ROWS_EVENT || \
+ bool is_last_rows_event(Log_event* ev)
+ {
+ Log_event_type type= ev->get_type_code();
+ if (IS_ROWS_EVENT_TYPE(type))
+ {
+ Rows_log_event* rows= (Rows_log_event*)ev;
+ return rows->get_flags(Rows_log_event::STMT_END_F);
+ }
+
+ return 0;
+ }
+
+ #define IS_ROWS_EVENT_TYPE(type) ((type) == WRITE_ROWS_EVENT || \
(type) == UPDATE_ROWS_EVENT || \
- (type) == DELETE_ROWS_EVENT )
+ (type) == DELETE_ROWS_EVENT)
8. General remarks
~~~~~~~~~~~~~~~~~~
-=-=(Alexi - Sun, 20 Dec 2009, 09:29)=-=-
Low Level Design modified.
--- /tmp/wklog.47.old.32726 2009-12-20 07:29:56.000000000 +0000
+++ /tmp/wklog.47.new.32726 2009-12-20 07:29:56.000000000 +0000
@@ -56,7 +56,7 @@
0000006A | 00 | 0 - RESERVED [27]
..... ...
00000081 | 00 | 0 - RESERVED [50]
- 00000082 | 00 | 0 - ANNOTATE_RBR_EVENT [51]
+ 00000082 | 00 | 0 - ANNOTATE_ROWS_EVENT [51]
************************
2. Outline of Annotate_rows event behavior
-=-=(Alexi - Sat, 19 Dec 2009, 16:10)=-=-
Low Level Design modified.
--- /tmp/wklog.47.old.16051 2009-12-19 16:10:48.000000000 +0200
+++ /tmp/wklog.47.new.16051 2009-12-19 16:10:48.000000000 +0200
@@ -253,7 +253,7 @@
thd query to that of the described by the event, i.e. to the query which
caused the subsequent Rows events (see "How Master writes Annotate_rows
events to the binary log" to follow what happens further when the subsequent
-Rows events is applied):
+Rows events are applied):
int Annotate_rows_log_event::do_apply_event(...)
{
-=-=(Alexi - Sat, 19 Dec 2009, 16:02)=-=-
High-Level Specification modified.
--- /tmp/wklog.47.old.15695 2009-12-19 16:02:33.000000000 +0200
+++ /tmp/wklog.47.new.15695 2009-12-19 16:02:33.000000000 +0200
@@ -12,7 +12,7 @@
post-header and contains the query text in its data part. Example:
************************
- ANNOTATE_RBR_EVENT
+ ANNOTATE_ROWS_EVENT
************************
00000220 | B6 A0 2C 4B | time_when = 1261215926
00000224 | 33 | event_type = 51
-=-=(Alexi - Sat, 19 Dec 2009, 15:58)=-=-
Low Level Design modified.
--- /tmp/wklog.47.old.15437 2009-12-19 13:58:12.000000000 +0000
+++ /tmp/wklog.47.new.15437 2009-12-19 13:58:12.000000000 +0000
@@ -1 +1,337 @@
+Content
+~~~~~~~
+ 1. Annotate_rows event number
+ 2. Outline of Annotate_rows event behavior
+ 3. How Master writes Annotate_rows events to the binary log
+ 4. How slave treats replicate-annotate-rows-events option
+ 5. How slave IO thread requests Annotate_rows events
+ 6. How master executes the request
+ 7. How slave SQL thread processes Annotate_rows events
+ 8. General remarks
+
+1. Annotate_rows event number
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+To avoid possible event numbers conflict with MySQL/Sun, we leave a gap
+between the last MySQL event number and the Annotate_rows event number:
+
+ enum Log_event_type
+ { ...
+ INCIDENT_EVENT= 26,
+ // New MySQL event numbers are to be added here
+ MYSQL_EVENTS_END,
+
+ MARIA_EVENTS_BEGIN= 51,
+ // New Maria event numbers start from here
+ ANNOTATE_ROWS_EVENT= 51,
+
+ ENUM_END_EVENT
+ };
+
+together with the corresponding extension of 'post_header_len' array in the
+Format description event. (This extension does not affect the compatibility
+of the binary log). Here is how Format description event looks like with
+this extension:
+
+ ************************
+ FORMAT_DESCRIPTION_EVENT
+ ************************
+ 00000004 | A1 A0 2C 4B | time_when = 1261215905
+ 00000008 | 0F | event_type = 15
+ 00000009 | 64 00 00 00 | server_id = 100
+ 0000000D | 7F 00 00 00 | event_len = 127
+ 00000011 | 83 00 00 00 | log_pos = 00000083
+ 00000015 | 01 00 | flags = LOG_EVENT_BINLOG_IN_USE_F
+ ------------------------
+ 00000017 | 04 00 | binlog_ver = 4
+ 00000019 | 35 2E 32 2E | server_ver = 5.2.0-MariaDB-alpha-debug-log
+ ..... ...
+ 0000004B | A1 A0 2C 4B | time_created = 1261215905
+ 0000004F | 13 | common_header_len = 19
+ ------------------------
+ post_header_len
+ ------------------------
+ 00000050 | 38 | 56 - START_EVENT_V3 [1]
+ ..... ...
+ 00000069 | 02 | 2 - INCIDENT_EVENT [26]
+ 0000006A | 00 | 0 - RESERVED [27]
+ ..... ...
+ 00000081 | 00 | 0 - RESERVED [50]
+ 00000082 | 00 | 0 - ANNOTATE_RBR_EVENT [51]
+ ************************
+
+2. Outline of Annotate_rows event behavior
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+Each Annotate_rows_log_event object has two private members describing the
+corresponding query:
+
+ char *m_query_txt;
+ uint m_query_len;
+
+When the object is created for writing to a binary log, this query is taken
+from 'thd' (for short, below we omit the 'Annotate_rows_log_event::' prefix
+as well as other implementation details):
+
+ Annotate_rows_log_event(THD *thd)
+ {
+ m_query_txt = thd->query();
+ m_query_len = thd->query_length();
+ }
+
+When the object is read from a binary log, the query is taken from the buffer
+containing the binary log representation of the event (this buffer is allocated
+in Log_event object from which all Log events are derived):
+
+ Annotate_rows_log_event(char *buf, uint event_len,
+ Format_description_log_event *desc)
+ {
+ m_query_len = event_len - desc->common_header_len;
+ m_query_txt = buf + desc->common_header_len;
+ }
+
+The events are written to the binary log by the Log_event::write() member
+which calls virtual write_data_header() and write_data_body() members
+("data header" and "post header" are synonym in replication terminology).
+In our case, data header is empty and data body is just the query:
+
+ bool write_data_body(IO_CACHE *file)
+ {
+ return my_b_safe_write(file, (uchar*) m_query_txt, m_query_len);
+ }
+
+Printing the event is just printing the query:
+
+ void Annotate_rows_log_event::print(FILE *file, PRINT_EVENT_INFO *pinfo)
+ {
+ my_b_printf(&pinfo->head_cache, "\tQuery: `%s`\n", m_query_txt);
+ }
+
+3. How Master writes Annotate_rows events to the binary log
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+The event is written to the binary log just before the group of Table_map
+events which precede corresponding Rows events (one query may generate
+several Table map events in the binary log, but the corresponding
+Annotate_rows event must be written only once before the first Table map
+event; hence the boolean variable 'with_annotate' below):
+
+ int write_locked_table_maps(THD *thd)
+ { ...
+ bool with_annotate= thd->variables.binlog_annotate_rows_events;
+ ...
+ for (uint i= 0; i < ... <number of tables> ...; ++i)
+ { ...
+ thd->binlog_write_table_map(table, ..., with_annotate);
+ with_annotate= 0; // write Annotate_event not more than once
+ ...
+ }
+ ...
+ }
+
+ int THD::binlog_write_table_map(TABLE *table, ..., bool with_annotate)
+ { ...
+ Table_map_log_event the_event(...);
+ ...
+ if (with_annotate)
+ {
+ Annotate_rows_log_event anno(this);
+ mysql_bin_log.write(&anno);
+ }
+
+ mysql_bin_log.write(&the_event);
+ ...
+ }
+
+4. How slave treats replicate-annotate-rows-events option
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+The replicate-annotate-rows-events option is treated just as the session
+value of the binlog_annotate_rows_events variable for the slave IO and
+SQL threads. This setting is done during initialization of these threads:
+
+ pthread_handler_t handle_slave_io(void *arg)
+ {
+ THD *thd= new THD;
+ ...
+ init_slave_thread(thd, SLAVE_THD_IO);
+ ...
+ }
+
+ pthread_handler_t handle_slave_sql(void *arg)
+ {
+ THD *thd= new THD;
+ ...
+ init_slave_thread(thd, SLAVE_THD_SQL);
+ ...
+ }
+
+ int init_slave_thread(THD* thd, SLAVE_THD_TYPE thd_type)
+ { ...
+ thd->variables.binlog_annotate_rows_events=
+ opt_replicate_annotate_rows_events;
+ ...
+ }
+
+5. How slave IO thread requests Annotate_rows events
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+When requesting an event, the slave should inform the master whether
+it should send Annotate_rows events or not. To that end we add a new
+BINLOG_SEND_ANNOTATE_ROWS_EVENT flag used when requesting an event:
+
+ #define BINLOG_DUMP_NON_BLOCK 1
+ #define BINLOG_SEND_ANNOTATE_ROWS_EVENT 2
+
+ pthread_handler_t handle_slave_io(void *arg)
+ { ...
+ request_dump(mysql, ...);
+ ...
+ }
+
+ int request_dump(MYSQL* mysql, ...)
+ { ...
+ if (opt_log_slave_updates &&
+ mi->io_thd->variables.binlog_annotate_rows_events)
+ binlog_flags|= BINLOG_SEND_ANNOTATE_ROWS_EVENT;
+ ...
+ int2store(buf + 4, binlog_flags);
+ ...
+ simple_command(mysql, COM_BINLOG_DUMP, buf, ...);
+ ...
+ }
+
+6. How master executes the request
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+ case COM_BINLOG_DUMP:
+ { ...
+ flags= uint2korr(packet + 4);
+ ...
+ mysql_binlog_send(thd, ..., flags);
+ ...
+ }
+
+ void mysql_binlog_send(THD* thd, ..., ushort flags)
+ { ...
+ Log_event::read_log_event(&log, packet, ...);
+ ...
+ if ((*packet)[EVENT_TYPE_OFFSET + 1] != ANNOTATE_ROWS_EVENT ||
+ flags & BINLOG_SEND_ANNOTATE_ROWS_EVENT ||
+ thd->server_id == 0 /* slave == mysqlbinlog */ )
+ {
+ my_net_write(net, packet->ptr(), packet->length());
+ }
+ ...
+ }
+
+7. How slave SQL thread processes Annotate_rows events
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+The slave processes each recieved event by "applying" it, i.e. by
+calling the Log_event::apply_event() function which in turn calls
+the virtual do_apply_event() member specific for each type of the
+event.
+
+ int exec_relay_log_event(THD* thd, Relay_log_info* rli)
+ { ...
+ Log_event *ev = next_event(rli);
+ ...
+ apply_event_and_update_pos(ev, ...);
+
+ if (ev->get_type_code() != FORMAT_DESCRIPTION_EVENT)
+ delete ev;
+ ...
+ }
+
+ int apply_event_and_update_pos(Log_event *ev, ...)
+ { ...
+ ev->apply_event(...);
+ ...
+ }
+
+ int Log_event::apply_event(...)
+ {
+ return do_apply_event(...);
+ }
+
+What does it mean to "apply" an Annotate_rows event? It means to set current
+thd query to that of the described by the event, i.e. to the query which
+caused the subsequent Rows events (see "How Master writes Annotate_rows
+events to the binary log" to follow what happens further when the subsequent
+Rows events is applied):
+
+ int Annotate_rows_log_event::do_apply_event(...)
+ {
+ thd->set_query(m_query_txt, m_query_len);
+ }
+
+NOTE. I am not sure, but possibly current values of thd->query and
+thd->query_length should be saved before calling set_query() and to be
+restored on the Annotate_rows_log_event object deletion.
+Is it really needed ?
+
+After calling this do_apply_event() function we may not delete the
+Annotate_rows_log_event object immediatedly (see exec_relay_log_event()
+above) because thd->query now points to the string inside this object.
+We may keep the pointer to this object in the Relay_log_info:
+
+ class Relay_log_info
+ {
+ public:
+ ...
+ void set_annotate_event(Annotate_rows_log_event*);
+ Annotate_rows_log_event* get_annotate_event();
+ void free_annotate_event();
+ ...
+ private:
+ Annotate_rows_log_event* m_annotate_event;
+ };
+
+When the saved Annotate_rows object may be deleted? When all corresponding
+Rows events will be processed, i.e. before processing the first non-Rows
+event (note that Annotate_rows object resides in the binary log *after*
+the (possible) 'BEGIN' Query event which accompanies the rows events; note
+also that this deletion is adjusted with the case when some or all
+corresponding Rows events are filtered out by replicate filter rules):
+
+ int exec_relay_log_event(THD* thd, Relay_log_info* rli)
+ { ...
+ Log_event *ev= next_event(rli);
+ ...
+ if (rli->get_annotate_event() && !IS_RBR_EVENT_TYPE(ev->get_type_code()))
+ rli->free_annotate_event();
+
+ apply_event_and_update_pos(ev, ...);
+
+ if (ev->get_type_code() == ANNOTATE_ROWS_EVENT)
+ rli->set_annotate_event((Annotate_rows_log_event*) ev);
+ else if (ev->get_type_code() != FORMAT_DESCRIPTION_EVENT)
+ delete ev;
+ ...
+ }
+
+where
+
+ #define IS_RBR_EVENT_TYPE(type) ( (type) == TABLE_MAP_EVENT || \
+ (type) == WRITE_ROWS_EVENT || \
+ (type) == UPDATE_ROWS_EVENT || \
+ (type) == DELETE_ROWS_EVENT )
+
+8. General remarks
+~~~~~~~~~~~~~~~~~~
+Kristian noticed that introducing new log event type should be coordinated
+somehow with MySQL/Sun:
+
+ Kristian: The numeric code for this event must be assigned carefully.
+ It should be coordinated with MySQL/Sun, otherwise we can get into a
+ situation where MySQL uses the same numeric code for one event that
+ MariaDB uses for ANNOTATE_ROWS_EVENT, which would make merging the two
+ impossible.
+ Alex: I reserved about 20 numbers not to have possible conflicts
+ with MySQL.
+ Kristian: Still, I think it would be appropriate to send a polite email
+ to internals(a)lists.mysql.com about this and suggesting to reserve the
+ event number.
+
+Also we should notice the introduction of the BINLOG_SEND_ANNOTATE_ROWS_EVENT
+flag taking into account that MySQL/Sun may also introduce a flag with the
+same value to be used in the request_dump-mysql_binlog_send interface.
+But this is mainly the question of merging: if a conflict concerning this
+flag occur, we may simply change the BINLOG_SEND_ANNOTATE_ROWS_EVENT value
+(this does not require additional changes in the code).
------------------------------------------------------------
-=-=(View All Progress Notes, 23 total)=-=-
http://askmonty.org/worklog/index.pl?tid=47&nolimit=1
DESCRIPTION:
Store in binlog (and show in mysqlbinlog output) texts of statements that
caused RBR events
This is needed for (list from Monty):
- Easier to understand why updates happened
- Would make it easier to find out where in application things went
wrong (as you can search for exact strings)
- Allow one to filter things based on comments in the statement.
The cost of this can be that the binlog will be approximately 2x in size
(especially insert of big blob's would be a bit painful), so this should
be an optional feature.
HIGH-LEVEL SPECIFICATION:
Content
~~~~~~~
1. Annotate_rows_log_event
2. Server option: --binlog-annotate-rows-events
3. Server option: --replicate-annotate-rows-events
4. mysqlbinlog option: --print-annotate-rows-events
5. mysqlbinlog output
1. Annotate_rows_log_event [ ANNOTATE_ROWS_EVENT ]
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Describes the query which caused the corresponding rows events. Has empty
post-header and contains the query text in its data part. Example:
************************
ANNOTATE_ROWS_EVENT
************************
00000220 | B6 A0 2C 4B | time_when = 1261215926
00000224 | 33 | event_type = 51
00000225 | 64 00 00 00 | server_id = 100
00000229 | 36 00 00 00 | event_len = 54
0000022D | 56 02 00 00 | log_pos = 00000256
00000231 | 00 00 | flags = <none>
------------------------
00000233 | 49 4E 53 45 | query = "INSERT INTO t1 VALUES (1), (2), (3)"
00000237 | 52 54 20 49 |
0000023B | 4E 54 4F 20 |
0000023F | 74 31 20 56 |
00000243 | 41 4C 55 45 |
00000247 | 53 20 28 31 |
0000024B | 29 2C 20 28 |
0000024F | 32 29 2C 20 |
00000253 | 28 33 29 |
************************
In binary log, Annotate_rows event follows the (possible) 'BEGIN' Query event
and precedes the first of Table map events which accompany the corresponding
rows events. (See example in the "mysqlbinlog output" section below.)
2. Server option: --binlog-annotate-rows-events
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Tells the master to write Annotate_rows events to the binary log.
* Variable Name: binlog_annotate_rows_events
* Scope: Global & Session
* Access Type: Dynamic
* Data Type: bool
* Default Value: OFF
NOTE. Session values allows to annotate only some selected statements:
...
SET SESSION binlog_annotate_rows_events=ON;
... statements to be annotated ...
SET SESSION binlog_annotate_rows_events=OFF;
... statements not to be annotated ...
3. Server option: --replicate-annotate-rows-events
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Tells the slave to reproduce Annotate_rows events recieved from the master
in its own binary log (sensible only in pair with log-slave-updates option).
* Variable Name: replicate_annotate_rows_events
* Scope: Global
* Access Type: Read only
* Data Type: bool
* Default Value: OFF
NOTE. Why do we additionally need this 'replicate' option? Why not to make
the slave to reproduce this events when its binlog-annotate-rows-events
global value is ON? Well, because, for example, we may want to configure
the slave which should reproduce Annotate_rows events but has global
binlog-annotate-rows-events = OFF meaning this to be the default value for
the client threads (see also "How slave treats replicate-annotate-rows-events
option" in LLD part).
4. mysqlbinlog option: --print-annotate-rows-events
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
With this option, mysqlbinlog prints the content of Annotate_rows events (if
the binary log does contain them). Without this option (i.e. by default),
mysqlbinlog skips Annotate_rows events.
5. mysqlbinlog output
~~~~~~~~~~~~~~~~~~~~~
With --print-annotate-rows-events, mysqlbinlog outputs Annotate_rows events
in a form like this:
...
# at 1646
#091219 12:45:26 server id 100 end_log_pos 1714 Query thread_id=1
exec_time=0 error_code=0
SET TIMESTAMP=1261215926/*!*/;
BEGIN
/*!*/;
# at 1714
# at 1812
# at 1853
# at 1894
# at 1938
#091219 12:45:26 server id 100 end_log_pos 1812 Query: `DELETE t1, t2 FROM
t1 INNER JOIN t2 INNER JOIN t3 WHERE t1.a=t2.a AND t2.a=t3.a`
#091219 12:45:26 server id 100 end_log_pos 1853 Table_map: `test`.`t1`
mapped to number 16
#091219 12:45:26 server id 100 end_log_pos 1894 Table_map: `test`.`t2`
mapped to number 17
#091219 12:45:26 server id 100 end_log_pos 1938 Delete_rows: table id 16
#091219 12:45:26 server id 100 end_log_pos 1982 Delete_rows: table id 17
flags: STMT_END_F
...
LOW-LEVEL DESIGN:
Content
~~~~~~~
1. Annotate_rows event number
2. Outline of Annotate_rows event behavior
3. How Master writes Annotate_rows events to the binary log
4. How slave treats replicate-annotate-rows-events option
5. How slave IO thread requests Annotate_rows events
6. How master executes the request
7. How slave SQL thread processes Annotate_rows events
8. General remarks
1. Annotate_rows event number
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
To avoid possible event numbers conflict with MySQL/Sun, we leave a gap
between the last MySQL event number and the Annotate_rows event number:
enum Log_event_type
{ ...
INCIDENT_EVENT= 26,
// New MySQL event numbers are to be added here
MYSQL_EVENTS_END,
MARIA_EVENTS_BEGIN= 51,
// New Maria event numbers start from here
ANNOTATE_ROWS_EVENT= 51,
ENUM_END_EVENT
};
together with the corresponding extension of 'post_header_len' array in the
Format description event. (This extension does not affect the compatibility
of the binary log). Here is how Format description event looks like with
this extension:
************************
FORMAT_DESCRIPTION_EVENT
************************
00000004 | A1 A0 2C 4B | time_when = 1261215905
00000008 | 0F | event_type = 15
00000009 | 64 00 00 00 | server_id = 100
0000000D | 7F 00 00 00 | event_len = 127
00000011 | 83 00 00 00 | log_pos = 00000083
00000015 | 01 00 | flags = LOG_EVENT_BINLOG_IN_USE_F
------------------------
00000017 | 04 00 | binlog_ver = 4
00000019 | 35 2E 32 2E | server_ver = 5.2.0-MariaDB-alpha-debug-log
..... ...
0000004B | A1 A0 2C 4B | time_created = 1261215905
0000004F | 13 | common_header_len = 19
------------------------
post_header_len
------------------------
00000050 | 38 | 56 - START_EVENT_V3 [1]
..... ...
00000069 | 02 | 2 - INCIDENT_EVENT [26]
0000006A | 00 | 0 - RESERVED [27]
..... ...
00000081 | 00 | 0 - RESERVED [50]
00000082 | 00 | 0 - ANNOTATE_ROWS_EVENT [51]
************************
2. Outline of Annotate_rows event behavior
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Each Annotate_rows_log_event object has two private members describing the
corresponding query:
char *m_query_txt;
uint m_query_len;
When the object is created for writing to a binary log, this query is taken
from 'thd' (for short, below we omit the 'Annotate_rows_log_event::' prefix
as well as other implementation details):
Annotate_rows_log_event(THD *thd)
{
m_query_txt = thd->query();
m_query_len = thd->query_length();
}
When the object is read from a binary log, the query is taken from the buffer
containing the binary log representation of the event (this buffer is allocated
in Log_event object from which all Log events are derived):
Annotate_rows_log_event(char *buf, uint event_len,
Format_description_log_event *desc)
{
m_query_len = event_len - desc->common_header_len;
m_query_txt = buf + desc->common_header_len;
}
The events are written to the binary log by the Log_event::write() member
which calls virtual write_data_header() and write_data_body() members
("data header" and "post header" are synonym in replication terminology).
In our case, data header is empty and data body is just the query:
bool write_data_body(IO_CACHE *file)
{
return my_b_safe_write(file, (uchar*) m_query_txt, m_query_len);
}
Printing the event is just printing the query:
void Annotate_rows_log_event::print(FILE *file, PRINT_EVENT_INFO *pinfo)
{
my_b_printf(&pinfo->head_cache, "\tQuery: `%s`\n", m_query_txt);
}
3. How Master writes Annotate_rows events to the binary log
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
The event is written to the binary log just before the group of Table_map
events which precede corresponding Rows events (one query may generate
several Table map events in the binary log, but the corresponding
Annotate_rows event must be written only once before the first Table map
event; hence the boolean variable 'with_annotate' below):
int write_locked_table_maps(THD *thd)
{ ...
bool with_annotate= thd->variables.binlog_annotate_rows_events;
...
for (uint i= 0; i < ... <number of tables> ...; ++i)
{ ...
thd->binlog_write_table_map(table, ..., with_annotate);
with_annotate= 0; // write Annotate_event not more than once
...
}
...
}
int THD::binlog_write_table_map(TABLE *table, ..., bool with_annotate)
{ ...
Table_map_log_event the_event(...);
...
if (with_annotate)
{
Annotate_rows_log_event anno(this);
mysql_bin_log.write(&anno);
}
mysql_bin_log.write(&the_event);
...
}
4. How slave treats replicate-annotate-rows-events option
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
The replicate-annotate-rows-events option is treated just as the session
value of the binlog_annotate_rows_events variable for the slave IO and
SQL threads. This setting is done during initialization of these threads:
pthread_handler_t handle_slave_io(void *arg)
{
THD *thd= new THD;
...
init_slave_thread(thd, SLAVE_THD_IO);
...
}
pthread_handler_t handle_slave_sql(void *arg)
{
THD *thd= new THD;
...
init_slave_thread(thd, SLAVE_THD_SQL);
...
}
int init_slave_thread(THD* thd, SLAVE_THD_TYPE thd_type)
{ ...
thd->variables.binlog_annotate_rows_events=
opt_replicate_annotate_rows_events;
...
}
5. How slave IO thread requests Annotate_rows events
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
If the replicate-annotate-rows-events option is not set on a slave, there
is no need for master to send Annotate_rows events to this slave. The slave
(or mysqlbinlog in remote case), before requesting binlog dump via the
COM_BINLOG_DUMP command, informs the master whether it should send these
events by executing the newly added COM_BINLOG_DUMP_OPTIONS_EXT server
command:
case COM_BINLOG_DUMP_OPTIONS_EXT:
thd->binlog_dump_flags_ext= packet[0];
my_ok(thd);
break;
Note. We add this new command and don't use COM_BINLOG_DUMP to avoid possible
conflicts with MySQL/Sun.
6. How master executes the request
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
case COM_BINLOG_DUMP:
{ ...
flags= uint2korr(packet + 4);
...
mysql_binlog_send(thd, ..., flags);
...
}
void mysql_binlog_send(THD* thd, ..., ushort flags)
{ ...
Log_event::read_log_event(&log, packet, ...);
...
if ((*packet)[EVENT_TYPE_OFFSET + 1] != ANNOTATE_ROWS_EVENT ||
flags & BINLOG_SEND_ANNOTATE_ROWS_EVENT)
{
my_net_write(net, packet->ptr(), packet->length());
}
...
}
7. How slave SQL thread processes Annotate_rows events
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
The slave processes each recieved event by "applying" it, i.e. by
calling the Log_event::apply_event() function which in turn calls
the virtual do_apply_event() member specific for each type of the
event.
int exec_relay_log_event(THD* thd, Relay_log_info* rli)
{ ...
Log_event *ev = next_event(rli);
...
apply_event_and_update_pos(ev, ...);
if (ev->get_type_code() != FORMAT_DESCRIPTION_EVENT)
delete ev;
...
}
int apply_event_and_update_pos(Log_event *ev, ...)
{ ...
ev->apply_event(...);
...
}
int Log_event::apply_event(...)
{
return do_apply_event(...);
}
What does it mean to "apply" an Annotate_rows event? It means to set current
thd query to that of the described by the event, i.e. to the query which
caused the subsequent Rows events (see "How Master writes Annotate_rows
events to the binary log" to follow what happens further when the subsequent
Rows events are applied):
int Annotate_rows_log_event::do_apply_event(...)
{
thd->set_query(m_query_txt, m_query_len);
}
NOTE. I am not sure, but possibly current values of thd->query and
thd->query_length should be saved before calling set_query() and to be
restored on the Annotate_rows_log_event object deletion.
Is it really needed ?
After calling this do_apply_event() function we may not delete the
Annotate_rows_log_event object immediatedly (see exec_relay_log_event()
above) because thd->query now points to the string inside this object.
We may keep the pointer to this object in the Relay_log_info:
class Relay_log_info
{
public:
...
void set_annotate_event(Annotate_rows_log_event*);
Annotate_rows_log_event* get_annotate_event();
void free_annotate_event();
...
private:
Annotate_rows_log_event* m_annotate_event;
};
The saved Annotate_rows object should be deleted when all corresponding
Rows events will be processed:
int exec_relay_log_event(THD* thd, Relay_log_info* rli)
{ ...
Log_event *ev= next_event(rli);
...
apply_event_and_update_pos(ev, ...);
if (rli->get_annotate_event() && is_last_rows_event(ev))
rli->free_annotate_event();
else if (ev->get_type_code() == ANNOTATE_ROWS_EVENT)
rli->set_annotate_event((Annotate_rows_log_event*) ev);
else if (ev->get_type_code() != FORMAT_DESCRIPTION_EVENT)
delete ev;
...
}
where
bool is_last_rows_event(Log_event* ev)
{
Log_event_type type= ev->get_type_code();
if (IS_ROWS_EVENT_TYPE(type))
{
Rows_log_event* rows= (Rows_log_event*)ev;
return rows->get_flags(Rows_log_event::STMT_END_F);
}
return 0;
}
#define IS_ROWS_EVENT_TYPE(type) ((type) == WRITE_ROWS_EVENT || \
(type) == UPDATE_ROWS_EVENT || \
(type) == DELETE_ROWS_EVENT)
8. General remarks
~~~~~~~~~~~~~~~~~~
Kristian noticed that introducing new log event type should be coordinated
somehow with MySQL/Sun:
Kristian: The numeric code for this event must be assigned carefully.
It should be coordinated with MySQL/Sun, otherwise we can get into a
situation where MySQL uses the same numeric code for one event that
MariaDB uses for ANNOTATE_ROWS_EVENT, which would make merging the two
impossible.
Alex: I reserved about 20 numbers not to have possible conflicts
with MySQL.
Kristian: Still, I think it would be appropriate to send a polite email
to internals(a)lists.mysql.com about this and suggesting to reserve the
event number.
ESTIMATED WORK TIME
ESTIMATED COMPLETION DATE
-----------------------------------------------------------------------
WorkLog (v3.5.9)
1
0
[Maria-developers] Updated (by Serg): Store in binlog text of statements that caused RBR events (47)
by worklog-noreply@askmonty.org 05 Feb '10
by worklog-noreply@askmonty.org 05 Feb '10
05 Feb '10
-----------------------------------------------------------------------
WORKLOG TASK
-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
TASK...........: Store in binlog text of statements that caused RBR events
CREATION DATE..: Sat, 15 Aug 2009, 23:48
SUPERVISOR.....: Monty
IMPLEMENTOR....:
COPIES TO......: Knielsen, Serg
CATEGORY.......: Server-Sprint
TASK ID........: 47 (http://askmonty.org/worklog/?tid=47)
VERSION........: Server-9.x
STATUS.........: In-Progress
PRIORITY.......: 60
WORKED HOURS...: 20
ESTIMATE.......: 35 (hours remain)
ORIG. ESTIMATE.: 35
PROGRESS NOTES:
-=-=(Serg - Fri, 05 Feb 2010, 14:04)=-=-
Observers changed: Knielsen,Serg
-=-=(Guest - Fri, 05 Feb 2010, 13:40)=-=-
Category updated.
--- /tmp/wklog.47.old.9197 2010-02-05 13:40:36.000000000 +0200
+++ /tmp/wklog.47.new.9197 2010-02-05 13:40:36.000000000 +0200
@@ -1 +1 @@
-Server-RawIdeaBin
+Server-Sprint
-=-=(Guest - Fri, 05 Feb 2010, 13:40)=-=-
Status updated.
--- /tmp/wklog.47.old.9197 2010-02-05 13:40:36.000000000 +0200
+++ /tmp/wklog.47.new.9197 2010-02-05 13:40:36.000000000 +0200
@@ -1 +1 @@
-Un-Assigned
+In-Progress
-=-=(Alexi - Thu, 04 Feb 2010, 09:54)=-=-
Low Level Design modified.
--- /tmp/wklog.47.old.16174 2010-02-04 09:54:13.000000000 +0200
+++ /tmp/wklog.47.new.16174 2010-02-04 09:54:13.000000000 +0200
@@ -171,35 +171,20 @@
5. How slave IO thread requests Annotate_rows events
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-When requesting an event, the slave should inform the master whether
-it should send Annotate_rows events or not. To that end we add a new
-BINLOG_SEND_ANNOTATE_ROWS_EVENT flag used when requesting an event:
+If the replicate-annotate-rows-events option is not set on a slave, there
+is no need for master to send Annotate_rows events to this slave. The slave
+(or mysqlbinlog in remote case), before requesting binlog dump via the
+COM_BINLOG_DUMP command, informs the master whether it should send these
+events by executing the newly added COM_BINLOG_DUMP_OPTIONS_EXT server
+command:
+
+ case COM_BINLOG_DUMP_OPTIONS_EXT:
+ thd->binlog_dump_flags_ext= packet[0];
+ my_ok(thd);
+ break;
- #define BINLOG_DUMP_NON_BLOCK 1
- #define BINLOG_SEND_ANNOTATE_ROWS_EVENT 2
-
- pthread_handler_t handle_slave_io(void *arg)
- { ...
- request_dump(mysql, ...);
- ...
- }
-
- int request_dump(MYSQL* mysql, ...)
- { ...
- if (opt_log_slave_updates &&
- mi->io_thd->variables.binlog_annotate_rows_events)
- binlog_flags|= BINLOG_SEND_ANNOTATE_ROWS_EVENT;
- ...
- int2store(buf + 4, binlog_flags);
- ...
- simple_command(mysql, COM_BINLOG_DUMP, buf, ...);
- ...
- }
-
-NOTE. mysqlbinlog, when remotely requesting BINLOG_DUMP by calling the
-simple_command() function, should also use this flag if it wants (in case
-of the --print-annotate-rows-events option set) to recieve Annotate_rows
-events.
+Note. We add this new command and don't use COM_BINLOG_DUMP to avoid possible
+conflicts with MySQL/Sun.
6. How master executes the request
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -338,10 +323,4 @@
to internals(a)lists.mysql.com about this and suggesting to reserve the
event number.
-Also we should notice the introduction of the BINLOG_SEND_ANNOTATE_ROWS_EVENT
-flag taking into account that MySQL/Sun may also introduce a flag with the
-same value to be used in the request_dump-mysql_binlog_send interface.
-But this is mainly the question of merging: if a conflict concerning this
-flag occur, we may simply change the BINLOG_SEND_ANNOTATE_ROWS_EVENT value
-(this does not require additional changes in the code).
-=-=(Alexi - Sun, 20 Dec 2009, 16:00)=-=-
Low Level Design modified.
--- /tmp/wklog.47.old.19667 2009-12-20 14:00:56.000000000 +0000
+++ /tmp/wklog.47.new.19667 2009-12-20 14:00:56.000000000 +0000
@@ -196,6 +196,11 @@
...
}
+NOTE. mysqlbinlog, when remotely requesting BINLOG_DUMP by calling the
+simple_command() function, should also use this flag if it wants (in case
+of the --print-annotate-rows-events option set) to recieve Annotate_rows
+events.
+
6. How master executes the request
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -212,8 +217,7 @@
Log_event::read_log_event(&log, packet, ...);
...
if ((*packet)[EVENT_TYPE_OFFSET + 1] != ANNOTATE_ROWS_EVENT ||
- flags & BINLOG_SEND_ANNOTATE_ROWS_EVENT ||
- thd->server_id == 0 /* slave == mysqlbinlog */ )
+ flags & BINLOG_SEND_ANNOTATE_ROWS_EVENT)
{
my_net_write(net, packet->ptr(), packet->length());
}
-=-=(Alexi - Sun, 20 Dec 2009, 13:14)=-=-
Low Level Design modified.
--- /tmp/wklog.47.old.11350 2009-12-20 13:14:04.000000000 +0200
+++ /tmp/wklog.47.new.11350 2009-12-20 13:14:04.000000000 +0200
@@ -282,23 +282,18 @@
Annotate_rows_log_event* m_annotate_event;
};
-When the saved Annotate_rows object may be deleted? When all corresponding
-Rows events will be processed, i.e. before processing the first non-Rows
-event (note that Annotate_rows object resides in the binary log *after*
-the (possible) 'BEGIN' Query event which accompanies the rows events; note
-also that this deletion is adjusted with the case when some or all
-corresponding Rows events are filtered out by replicate filter rules):
+The saved Annotate_rows object should be deleted when all corresponding
+Rows events will be processed:
int exec_relay_log_event(THD* thd, Relay_log_info* rli)
{ ...
Log_event *ev= next_event(rli);
...
- if (rli->get_annotate_event() && !IS_RBR_EVENT_TYPE(ev->get_type_code()))
- rli->free_annotate_event();
-
apply_event_and_update_pos(ev, ...);
- if (ev->get_type_code() == ANNOTATE_ROWS_EVENT)
+ if (rli->get_annotate_event() && is_last_rows_event(ev))
+ rli->free_annotate_event();
+ else if (ev->get_type_code() == ANNOTATE_ROWS_EVENT)
rli->set_annotate_event((Annotate_rows_log_event*) ev);
else if (ev->get_type_code() != FORMAT_DESCRIPTION_EVENT)
delete ev;
@@ -307,10 +302,21 @@
where
- #define IS_RBR_EVENT_TYPE(type) ( (type) == TABLE_MAP_EVENT || \
- (type) == WRITE_ROWS_EVENT || \
+ bool is_last_rows_event(Log_event* ev)
+ {
+ Log_event_type type= ev->get_type_code();
+ if (IS_ROWS_EVENT_TYPE(type))
+ {
+ Rows_log_event* rows= (Rows_log_event*)ev;
+ return rows->get_flags(Rows_log_event::STMT_END_F);
+ }
+
+ return 0;
+ }
+
+ #define IS_ROWS_EVENT_TYPE(type) ((type) == WRITE_ROWS_EVENT || \
(type) == UPDATE_ROWS_EVENT || \
- (type) == DELETE_ROWS_EVENT )
+ (type) == DELETE_ROWS_EVENT)
8. General remarks
~~~~~~~~~~~~~~~~~~
-=-=(Alexi - Sun, 20 Dec 2009, 09:29)=-=-
Low Level Design modified.
--- /tmp/wklog.47.old.32726 2009-12-20 07:29:56.000000000 +0000
+++ /tmp/wklog.47.new.32726 2009-12-20 07:29:56.000000000 +0000
@@ -56,7 +56,7 @@
0000006A | 00 | 0 - RESERVED [27]
..... ...
00000081 | 00 | 0 - RESERVED [50]
- 00000082 | 00 | 0 - ANNOTATE_RBR_EVENT [51]
+ 00000082 | 00 | 0 - ANNOTATE_ROWS_EVENT [51]
************************
2. Outline of Annotate_rows event behavior
-=-=(Alexi - Sat, 19 Dec 2009, 16:10)=-=-
Low Level Design modified.
--- /tmp/wklog.47.old.16051 2009-12-19 16:10:48.000000000 +0200
+++ /tmp/wklog.47.new.16051 2009-12-19 16:10:48.000000000 +0200
@@ -253,7 +253,7 @@
thd query to that of the described by the event, i.e. to the query which
caused the subsequent Rows events (see "How Master writes Annotate_rows
events to the binary log" to follow what happens further when the subsequent
-Rows events is applied):
+Rows events are applied):
int Annotate_rows_log_event::do_apply_event(...)
{
-=-=(Alexi - Sat, 19 Dec 2009, 16:02)=-=-
High-Level Specification modified.
--- /tmp/wklog.47.old.15695 2009-12-19 16:02:33.000000000 +0200
+++ /tmp/wklog.47.new.15695 2009-12-19 16:02:33.000000000 +0200
@@ -12,7 +12,7 @@
post-header and contains the query text in its data part. Example:
************************
- ANNOTATE_RBR_EVENT
+ ANNOTATE_ROWS_EVENT
************************
00000220 | B6 A0 2C 4B | time_when = 1261215926
00000224 | 33 | event_type = 51
-=-=(Alexi - Sat, 19 Dec 2009, 15:58)=-=-
Low Level Design modified.
--- /tmp/wklog.47.old.15437 2009-12-19 13:58:12.000000000 +0000
+++ /tmp/wklog.47.new.15437 2009-12-19 13:58:12.000000000 +0000
@@ -1 +1,337 @@
+Content
+~~~~~~~
+ 1. Annotate_rows event number
+ 2. Outline of Annotate_rows event behavior
+ 3. How Master writes Annotate_rows events to the binary log
+ 4. How slave treats replicate-annotate-rows-events option
+ 5. How slave IO thread requests Annotate_rows events
+ 6. How master executes the request
+ 7. How slave SQL thread processes Annotate_rows events
+ 8. General remarks
+
+1. Annotate_rows event number
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+To avoid possible event numbers conflict with MySQL/Sun, we leave a gap
+between the last MySQL event number and the Annotate_rows event number:
+
+ enum Log_event_type
+ { ...
+ INCIDENT_EVENT= 26,
+ // New MySQL event numbers are to be added here
+ MYSQL_EVENTS_END,
+
+ MARIA_EVENTS_BEGIN= 51,
+ // New Maria event numbers start from here
+ ANNOTATE_ROWS_EVENT= 51,
+
+ ENUM_END_EVENT
+ };
+
+together with the corresponding extension of 'post_header_len' array in the
+Format description event. (This extension does not affect the compatibility
+of the binary log). Here is how Format description event looks like with
+this extension:
+
+ ************************
+ FORMAT_DESCRIPTION_EVENT
+ ************************
+ 00000004 | A1 A0 2C 4B | time_when = 1261215905
+ 00000008 | 0F | event_type = 15
+ 00000009 | 64 00 00 00 | server_id = 100
+ 0000000D | 7F 00 00 00 | event_len = 127
+ 00000011 | 83 00 00 00 | log_pos = 00000083
+ 00000015 | 01 00 | flags = LOG_EVENT_BINLOG_IN_USE_F
+ ------------------------
+ 00000017 | 04 00 | binlog_ver = 4
+ 00000019 | 35 2E 32 2E | server_ver = 5.2.0-MariaDB-alpha-debug-log
+ ..... ...
+ 0000004B | A1 A0 2C 4B | time_created = 1261215905
+ 0000004F | 13 | common_header_len = 19
+ ------------------------
+ post_header_len
+ ------------------------
+ 00000050 | 38 | 56 - START_EVENT_V3 [1]
+ ..... ...
+ 00000069 | 02 | 2 - INCIDENT_EVENT [26]
+ 0000006A | 00 | 0 - RESERVED [27]
+ ..... ...
+ 00000081 | 00 | 0 - RESERVED [50]
+ 00000082 | 00 | 0 - ANNOTATE_RBR_EVENT [51]
+ ************************
+
+2. Outline of Annotate_rows event behavior
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+Each Annotate_rows_log_event object has two private members describing the
+corresponding query:
+
+ char *m_query_txt;
+ uint m_query_len;
+
+When the object is created for writing to a binary log, this query is taken
+from 'thd' (for short, below we omit the 'Annotate_rows_log_event::' prefix
+as well as other implementation details):
+
+ Annotate_rows_log_event(THD *thd)
+ {
+ m_query_txt = thd->query();
+ m_query_len = thd->query_length();
+ }
+
+When the object is read from a binary log, the query is taken from the buffer
+containing the binary log representation of the event (this buffer is allocated
+in Log_event object from which all Log events are derived):
+
+ Annotate_rows_log_event(char *buf, uint event_len,
+ Format_description_log_event *desc)
+ {
+ m_query_len = event_len - desc->common_header_len;
+ m_query_txt = buf + desc->common_header_len;
+ }
+
+The events are written to the binary log by the Log_event::write() member
+which calls virtual write_data_header() and write_data_body() members
+("data header" and "post header" are synonym in replication terminology).
+In our case, data header is empty and data body is just the query:
+
+ bool write_data_body(IO_CACHE *file)
+ {
+ return my_b_safe_write(file, (uchar*) m_query_txt, m_query_len);
+ }
+
+Printing the event is just printing the query:
+
+ void Annotate_rows_log_event::print(FILE *file, PRINT_EVENT_INFO *pinfo)
+ {
+ my_b_printf(&pinfo->head_cache, "\tQuery: `%s`\n", m_query_txt);
+ }
+
+3. How Master writes Annotate_rows events to the binary log
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+The event is written to the binary log just before the group of Table_map
+events which precede corresponding Rows events (one query may generate
+several Table map events in the binary log, but the corresponding
+Annotate_rows event must be written only once before the first Table map
+event; hence the boolean variable 'with_annotate' below):
+
+ int write_locked_table_maps(THD *thd)
+ { ...
+ bool with_annotate= thd->variables.binlog_annotate_rows_events;
+ ...
+ for (uint i= 0; i < ... <number of tables> ...; ++i)
+ { ...
+ thd->binlog_write_table_map(table, ..., with_annotate);
+ with_annotate= 0; // write Annotate_event not more than once
+ ...
+ }
+ ...
+ }
+
+ int THD::binlog_write_table_map(TABLE *table, ..., bool with_annotate)
+ { ...
+ Table_map_log_event the_event(...);
+ ...
+ if (with_annotate)
+ {
+ Annotate_rows_log_event anno(this);
+ mysql_bin_log.write(&anno);
+ }
+
+ mysql_bin_log.write(&the_event);
+ ...
+ }
+
+4. How slave treats replicate-annotate-rows-events option
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+The replicate-annotate-rows-events option is treated just as the session
+value of the binlog_annotate_rows_events variable for the slave IO and
+SQL threads. This setting is done during initialization of these threads:
+
+ pthread_handler_t handle_slave_io(void *arg)
+ {
+ THD *thd= new THD;
+ ...
+ init_slave_thread(thd, SLAVE_THD_IO);
+ ...
+ }
+
+ pthread_handler_t handle_slave_sql(void *arg)
+ {
+ THD *thd= new THD;
+ ...
+ init_slave_thread(thd, SLAVE_THD_SQL);
+ ...
+ }
+
+ int init_slave_thread(THD* thd, SLAVE_THD_TYPE thd_type)
+ { ...
+ thd->variables.binlog_annotate_rows_events=
+ opt_replicate_annotate_rows_events;
+ ...
+ }
+
+5. How slave IO thread requests Annotate_rows events
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+When requesting an event, the slave should inform the master whether
+it should send Annotate_rows events or not. To that end we add a new
+BINLOG_SEND_ANNOTATE_ROWS_EVENT flag used when requesting an event:
+
+ #define BINLOG_DUMP_NON_BLOCK 1
+ #define BINLOG_SEND_ANNOTATE_ROWS_EVENT 2
+
+ pthread_handler_t handle_slave_io(void *arg)
+ { ...
+ request_dump(mysql, ...);
+ ...
+ }
+
+ int request_dump(MYSQL* mysql, ...)
+ { ...
+ if (opt_log_slave_updates &&
+ mi->io_thd->variables.binlog_annotate_rows_events)
+ binlog_flags|= BINLOG_SEND_ANNOTATE_ROWS_EVENT;
+ ...
+ int2store(buf + 4, binlog_flags);
+ ...
+ simple_command(mysql, COM_BINLOG_DUMP, buf, ...);
+ ...
+ }
+
+6. How master executes the request
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+ case COM_BINLOG_DUMP:
+ { ...
+ flags= uint2korr(packet + 4);
+ ...
+ mysql_binlog_send(thd, ..., flags);
+ ...
+ }
+
+ void mysql_binlog_send(THD* thd, ..., ushort flags)
+ { ...
+ Log_event::read_log_event(&log, packet, ...);
+ ...
+ if ((*packet)[EVENT_TYPE_OFFSET + 1] != ANNOTATE_ROWS_EVENT ||
+ flags & BINLOG_SEND_ANNOTATE_ROWS_EVENT ||
+ thd->server_id == 0 /* slave == mysqlbinlog */ )
+ {
+ my_net_write(net, packet->ptr(), packet->length());
+ }
+ ...
+ }
+
+7. How slave SQL thread processes Annotate_rows events
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+The slave processes each recieved event by "applying" it, i.e. by
+calling the Log_event::apply_event() function which in turn calls
+the virtual do_apply_event() member specific for each type of the
+event.
+
+ int exec_relay_log_event(THD* thd, Relay_log_info* rli)
+ { ...
+ Log_event *ev = next_event(rli);
+ ...
+ apply_event_and_update_pos(ev, ...);
+
+ if (ev->get_type_code() != FORMAT_DESCRIPTION_EVENT)
+ delete ev;
+ ...
+ }
+
+ int apply_event_and_update_pos(Log_event *ev, ...)
+ { ...
+ ev->apply_event(...);
+ ...
+ }
+
+ int Log_event::apply_event(...)
+ {
+ return do_apply_event(...);
+ }
+
+What does it mean to "apply" an Annotate_rows event? It means to set current
+thd query to that of the described by the event, i.e. to the query which
+caused the subsequent Rows events (see "How Master writes Annotate_rows
+events to the binary log" to follow what happens further when the subsequent
+Rows events is applied):
+
+ int Annotate_rows_log_event::do_apply_event(...)
+ {
+ thd->set_query(m_query_txt, m_query_len);
+ }
+
+NOTE. I am not sure, but possibly current values of thd->query and
+thd->query_length should be saved before calling set_query() and to be
+restored on the Annotate_rows_log_event object deletion.
+Is it really needed ?
+
+After calling this do_apply_event() function we may not delete the
+Annotate_rows_log_event object immediatedly (see exec_relay_log_event()
+above) because thd->query now points to the string inside this object.
+We may keep the pointer to this object in the Relay_log_info:
+
+ class Relay_log_info
+ {
+ public:
+ ...
+ void set_annotate_event(Annotate_rows_log_event*);
+ Annotate_rows_log_event* get_annotate_event();
+ void free_annotate_event();
+ ...
+ private:
+ Annotate_rows_log_event* m_annotate_event;
+ };
+
+When the saved Annotate_rows object may be deleted? When all corresponding
+Rows events will be processed, i.e. before processing the first non-Rows
+event (note that Annotate_rows object resides in the binary log *after*
+the (possible) 'BEGIN' Query event which accompanies the rows events; note
+also that this deletion is adjusted with the case when some or all
+corresponding Rows events are filtered out by replicate filter rules):
+
+ int exec_relay_log_event(THD* thd, Relay_log_info* rli)
+ { ...
+ Log_event *ev= next_event(rli);
+ ...
+ if (rli->get_annotate_event() && !IS_RBR_EVENT_TYPE(ev->get_type_code()))
+ rli->free_annotate_event();
+
+ apply_event_and_update_pos(ev, ...);
+
+ if (ev->get_type_code() == ANNOTATE_ROWS_EVENT)
+ rli->set_annotate_event((Annotate_rows_log_event*) ev);
+ else if (ev->get_type_code() != FORMAT_DESCRIPTION_EVENT)
+ delete ev;
+ ...
+ }
+
+where
+
+ #define IS_RBR_EVENT_TYPE(type) ( (type) == TABLE_MAP_EVENT || \
+ (type) == WRITE_ROWS_EVENT || \
+ (type) == UPDATE_ROWS_EVENT || \
+ (type) == DELETE_ROWS_EVENT )
+
+8. General remarks
+~~~~~~~~~~~~~~~~~~
+Kristian noticed that introducing new log event type should be coordinated
+somehow with MySQL/Sun:
+
+ Kristian: The numeric code for this event must be assigned carefully.
+ It should be coordinated with MySQL/Sun, otherwise we can get into a
+ situation where MySQL uses the same numeric code for one event that
+ MariaDB uses for ANNOTATE_ROWS_EVENT, which would make merging the two
+ impossible.
+ Alex: I reserved about 20 numbers not to have possible conflicts
+ with MySQL.
+ Kristian: Still, I think it would be appropriate to send a polite email
+ to internals(a)lists.mysql.com about this and suggesting to reserve the
+ event number.
+
+Also we should notice the introduction of the BINLOG_SEND_ANNOTATE_ROWS_EVENT
+flag taking into account that MySQL/Sun may also introduce a flag with the
+same value to be used in the request_dump-mysql_binlog_send interface.
+But this is mainly the question of merging: if a conflict concerning this
+flag occur, we may simply change the BINLOG_SEND_ANNOTATE_ROWS_EVENT value
+(this does not require additional changes in the code).
------------------------------------------------------------
-=-=(View All Progress Notes, 23 total)=-=-
http://askmonty.org/worklog/index.pl?tid=47&nolimit=1
DESCRIPTION:
Store in binlog (and show in mysqlbinlog output) texts of statements that
caused RBR events
This is needed for (list from Monty):
- Easier to understand why updates happened
- Would make it easier to find out where in application things went
wrong (as you can search for exact strings)
- Allow one to filter things based on comments in the statement.
The cost of this can be that the binlog will be approximately 2x in size
(especially insert of big blob's would be a bit painful), so this should
be an optional feature.
HIGH-LEVEL SPECIFICATION:
Content
~~~~~~~
1. Annotate_rows_log_event
2. Server option: --binlog-annotate-rows-events
3. Server option: --replicate-annotate-rows-events
4. mysqlbinlog option: --print-annotate-rows-events
5. mysqlbinlog output
1. Annotate_rows_log_event [ ANNOTATE_ROWS_EVENT ]
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Describes the query which caused the corresponding rows events. Has empty
post-header and contains the query text in its data part. Example:
************************
ANNOTATE_ROWS_EVENT
************************
00000220 | B6 A0 2C 4B | time_when = 1261215926
00000224 | 33 | event_type = 51
00000225 | 64 00 00 00 | server_id = 100
00000229 | 36 00 00 00 | event_len = 54
0000022D | 56 02 00 00 | log_pos = 00000256
00000231 | 00 00 | flags = <none>
------------------------
00000233 | 49 4E 53 45 | query = "INSERT INTO t1 VALUES (1), (2), (3)"
00000237 | 52 54 20 49 |
0000023B | 4E 54 4F 20 |
0000023F | 74 31 20 56 |
00000243 | 41 4C 55 45 |
00000247 | 53 20 28 31 |
0000024B | 29 2C 20 28 |
0000024F | 32 29 2C 20 |
00000253 | 28 33 29 |
************************
In binary log, Annotate_rows event follows the (possible) 'BEGIN' Query event
and precedes the first of Table map events which accompany the corresponding
rows events. (See example in the "mysqlbinlog output" section below.)
2. Server option: --binlog-annotate-rows-events
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Tells the master to write Annotate_rows events to the binary log.
* Variable Name: binlog_annotate_rows_events
* Scope: Global & Session
* Access Type: Dynamic
* Data Type: bool
* Default Value: OFF
NOTE. Session values allows to annotate only some selected statements:
...
SET SESSION binlog_annotate_rows_events=ON;
... statements to be annotated ...
SET SESSION binlog_annotate_rows_events=OFF;
... statements not to be annotated ...
3. Server option: --replicate-annotate-rows-events
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Tells the slave to reproduce Annotate_rows events recieved from the master
in its own binary log (sensible only in pair with log-slave-updates option).
* Variable Name: replicate_annotate_rows_events
* Scope: Global
* Access Type: Read only
* Data Type: bool
* Default Value: OFF
NOTE. Why do we additionally need this 'replicate' option? Why not to make
the slave to reproduce this events when its binlog-annotate-rows-events
global value is ON? Well, because, for example, we may want to configure
the slave which should reproduce Annotate_rows events but has global
binlog-annotate-rows-events = OFF meaning this to be the default value for
the client threads (see also "How slave treats replicate-annotate-rows-events
option" in LLD part).
4. mysqlbinlog option: --print-annotate-rows-events
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
With this option, mysqlbinlog prints the content of Annotate_rows events (if
the binary log does contain them). Without this option (i.e. by default),
mysqlbinlog skips Annotate_rows events.
5. mysqlbinlog output
~~~~~~~~~~~~~~~~~~~~~
With --print-annotate-rows-events, mysqlbinlog outputs Annotate_rows events
in a form like this:
...
# at 1646
#091219 12:45:26 server id 100 end_log_pos 1714 Query thread_id=1
exec_time=0 error_code=0
SET TIMESTAMP=1261215926/*!*/;
BEGIN
/*!*/;
# at 1714
# at 1812
# at 1853
# at 1894
# at 1938
#091219 12:45:26 server id 100 end_log_pos 1812 Query: `DELETE t1, t2 FROM
t1 INNER JOIN t2 INNER JOIN t3 WHERE t1.a=t2.a AND t2.a=t3.a`
#091219 12:45:26 server id 100 end_log_pos 1853 Table_map: `test`.`t1`
mapped to number 16
#091219 12:45:26 server id 100 end_log_pos 1894 Table_map: `test`.`t2`
mapped to number 17
#091219 12:45:26 server id 100 end_log_pos 1938 Delete_rows: table id 16
#091219 12:45:26 server id 100 end_log_pos 1982 Delete_rows: table id 17
flags: STMT_END_F
...
LOW-LEVEL DESIGN:
Content
~~~~~~~
1. Annotate_rows event number
2. Outline of Annotate_rows event behavior
3. How Master writes Annotate_rows events to the binary log
4. How slave treats replicate-annotate-rows-events option
5. How slave IO thread requests Annotate_rows events
6. How master executes the request
7. How slave SQL thread processes Annotate_rows events
8. General remarks
1. Annotate_rows event number
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
To avoid possible event numbers conflict with MySQL/Sun, we leave a gap
between the last MySQL event number and the Annotate_rows event number:
enum Log_event_type
{ ...
INCIDENT_EVENT= 26,
// New MySQL event numbers are to be added here
MYSQL_EVENTS_END,
MARIA_EVENTS_BEGIN= 51,
// New Maria event numbers start from here
ANNOTATE_ROWS_EVENT= 51,
ENUM_END_EVENT
};
together with the corresponding extension of 'post_header_len' array in the
Format description event. (This extension does not affect the compatibility
of the binary log). Here is how Format description event looks like with
this extension:
************************
FORMAT_DESCRIPTION_EVENT
************************
00000004 | A1 A0 2C 4B | time_when = 1261215905
00000008 | 0F | event_type = 15
00000009 | 64 00 00 00 | server_id = 100
0000000D | 7F 00 00 00 | event_len = 127
00000011 | 83 00 00 00 | log_pos = 00000083
00000015 | 01 00 | flags = LOG_EVENT_BINLOG_IN_USE_F
------------------------
00000017 | 04 00 | binlog_ver = 4
00000019 | 35 2E 32 2E | server_ver = 5.2.0-MariaDB-alpha-debug-log
..... ...
0000004B | A1 A0 2C 4B | time_created = 1261215905
0000004F | 13 | common_header_len = 19
------------------------
post_header_len
------------------------
00000050 | 38 | 56 - START_EVENT_V3 [1]
..... ...
00000069 | 02 | 2 - INCIDENT_EVENT [26]
0000006A | 00 | 0 - RESERVED [27]
..... ...
00000081 | 00 | 0 - RESERVED [50]
00000082 | 00 | 0 - ANNOTATE_ROWS_EVENT [51]
************************
2. Outline of Annotate_rows event behavior
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Each Annotate_rows_log_event object has two private members describing the
corresponding query:
char *m_query_txt;
uint m_query_len;
When the object is created for writing to a binary log, this query is taken
from 'thd' (for short, below we omit the 'Annotate_rows_log_event::' prefix
as well as other implementation details):
Annotate_rows_log_event(THD *thd)
{
m_query_txt = thd->query();
m_query_len = thd->query_length();
}
When the object is read from a binary log, the query is taken from the buffer
containing the binary log representation of the event (this buffer is allocated
in Log_event object from which all Log events are derived):
Annotate_rows_log_event(char *buf, uint event_len,
Format_description_log_event *desc)
{
m_query_len = event_len - desc->common_header_len;
m_query_txt = buf + desc->common_header_len;
}
The events are written to the binary log by the Log_event::write() member
which calls virtual write_data_header() and write_data_body() members
("data header" and "post header" are synonym in replication terminology).
In our case, data header is empty and data body is just the query:
bool write_data_body(IO_CACHE *file)
{
return my_b_safe_write(file, (uchar*) m_query_txt, m_query_len);
}
Printing the event is just printing the query:
void Annotate_rows_log_event::print(FILE *file, PRINT_EVENT_INFO *pinfo)
{
my_b_printf(&pinfo->head_cache, "\tQuery: `%s`\n", m_query_txt);
}
3. How Master writes Annotate_rows events to the binary log
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
The event is written to the binary log just before the group of Table_map
events which precede corresponding Rows events (one query may generate
several Table map events in the binary log, but the corresponding
Annotate_rows event must be written only once before the first Table map
event; hence the boolean variable 'with_annotate' below):
int write_locked_table_maps(THD *thd)
{ ...
bool with_annotate= thd->variables.binlog_annotate_rows_events;
...
for (uint i= 0; i < ... <number of tables> ...; ++i)
{ ...
thd->binlog_write_table_map(table, ..., with_annotate);
with_annotate= 0; // write Annotate_event not more than once
...
}
...
}
int THD::binlog_write_table_map(TABLE *table, ..., bool with_annotate)
{ ...
Table_map_log_event the_event(...);
...
if (with_annotate)
{
Annotate_rows_log_event anno(this);
mysql_bin_log.write(&anno);
}
mysql_bin_log.write(&the_event);
...
}
4. How slave treats replicate-annotate-rows-events option
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
The replicate-annotate-rows-events option is treated just as the session
value of the binlog_annotate_rows_events variable for the slave IO and
SQL threads. This setting is done during initialization of these threads:
pthread_handler_t handle_slave_io(void *arg)
{
THD *thd= new THD;
...
init_slave_thread(thd, SLAVE_THD_IO);
...
}
pthread_handler_t handle_slave_sql(void *arg)
{
THD *thd= new THD;
...
init_slave_thread(thd, SLAVE_THD_SQL);
...
}
int init_slave_thread(THD* thd, SLAVE_THD_TYPE thd_type)
{ ...
thd->variables.binlog_annotate_rows_events=
opt_replicate_annotate_rows_events;
...
}
5. How slave IO thread requests Annotate_rows events
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
If the replicate-annotate-rows-events option is not set on a slave, there
is no need for master to send Annotate_rows events to this slave. The slave
(or mysqlbinlog in remote case), before requesting binlog dump via the
COM_BINLOG_DUMP command, informs the master whether it should send these
events by executing the newly added COM_BINLOG_DUMP_OPTIONS_EXT server
command:
case COM_BINLOG_DUMP_OPTIONS_EXT:
thd->binlog_dump_flags_ext= packet[0];
my_ok(thd);
break;
Note. We add this new command and don't use COM_BINLOG_DUMP to avoid possible
conflicts with MySQL/Sun.
6. How master executes the request
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
case COM_BINLOG_DUMP:
{ ...
flags= uint2korr(packet + 4);
...
mysql_binlog_send(thd, ..., flags);
...
}
void mysql_binlog_send(THD* thd, ..., ushort flags)
{ ...
Log_event::read_log_event(&log, packet, ...);
...
if ((*packet)[EVENT_TYPE_OFFSET + 1] != ANNOTATE_ROWS_EVENT ||
flags & BINLOG_SEND_ANNOTATE_ROWS_EVENT)
{
my_net_write(net, packet->ptr(), packet->length());
}
...
}
7. How slave SQL thread processes Annotate_rows events
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
The slave processes each recieved event by "applying" it, i.e. by
calling the Log_event::apply_event() function which in turn calls
the virtual do_apply_event() member specific for each type of the
event.
int exec_relay_log_event(THD* thd, Relay_log_info* rli)
{ ...
Log_event *ev = next_event(rli);
...
apply_event_and_update_pos(ev, ...);
if (ev->get_type_code() != FORMAT_DESCRIPTION_EVENT)
delete ev;
...
}
int apply_event_and_update_pos(Log_event *ev, ...)
{ ...
ev->apply_event(...);
...
}
int Log_event::apply_event(...)
{
return do_apply_event(...);
}
What does it mean to "apply" an Annotate_rows event? It means to set current
thd query to that of the described by the event, i.e. to the query which
caused the subsequent Rows events (see "How Master writes Annotate_rows
events to the binary log" to follow what happens further when the subsequent
Rows events are applied):
int Annotate_rows_log_event::do_apply_event(...)
{
thd->set_query(m_query_txt, m_query_len);
}
NOTE. I am not sure, but possibly current values of thd->query and
thd->query_length should be saved before calling set_query() and to be
restored on the Annotate_rows_log_event object deletion.
Is it really needed ?
After calling this do_apply_event() function we may not delete the
Annotate_rows_log_event object immediatedly (see exec_relay_log_event()
above) because thd->query now points to the string inside this object.
We may keep the pointer to this object in the Relay_log_info:
class Relay_log_info
{
public:
...
void set_annotate_event(Annotate_rows_log_event*);
Annotate_rows_log_event* get_annotate_event();
void free_annotate_event();
...
private:
Annotate_rows_log_event* m_annotate_event;
};
The saved Annotate_rows object should be deleted when all corresponding
Rows events will be processed:
int exec_relay_log_event(THD* thd, Relay_log_info* rli)
{ ...
Log_event *ev= next_event(rli);
...
apply_event_and_update_pos(ev, ...);
if (rli->get_annotate_event() && is_last_rows_event(ev))
rli->free_annotate_event();
else if (ev->get_type_code() == ANNOTATE_ROWS_EVENT)
rli->set_annotate_event((Annotate_rows_log_event*) ev);
else if (ev->get_type_code() != FORMAT_DESCRIPTION_EVENT)
delete ev;
...
}
where
bool is_last_rows_event(Log_event* ev)
{
Log_event_type type= ev->get_type_code();
if (IS_ROWS_EVENT_TYPE(type))
{
Rows_log_event* rows= (Rows_log_event*)ev;
return rows->get_flags(Rows_log_event::STMT_END_F);
}
return 0;
}
#define IS_ROWS_EVENT_TYPE(type) ((type) == WRITE_ROWS_EVENT || \
(type) == UPDATE_ROWS_EVENT || \
(type) == DELETE_ROWS_EVENT)
8. General remarks
~~~~~~~~~~~~~~~~~~
Kristian noticed that introducing new log event type should be coordinated
somehow with MySQL/Sun:
Kristian: The numeric code for this event must be assigned carefully.
It should be coordinated with MySQL/Sun, otherwise we can get into a
situation where MySQL uses the same numeric code for one event that
MariaDB uses for ANNOTATE_ROWS_EVENT, which would make merging the two
impossible.
Alex: I reserved about 20 numbers not to have possible conflicts
with MySQL.
Kristian: Still, I think it would be appropriate to send a polite email
to internals(a)lists.mysql.com about this and suggesting to reserve the
event number.
ESTIMATED WORK TIME
ESTIMATED COMPLETION DATE
-----------------------------------------------------------------------
WorkLog (v3.5.9)
1
0
[Maria-developers] Updated (by Serg): Store in binlog text of statements that caused RBR events (47)
by worklog-noreply@askmonty.org 05 Feb '10
by worklog-noreply@askmonty.org 05 Feb '10
05 Feb '10
-----------------------------------------------------------------------
WORKLOG TASK
-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
TASK...........: Store in binlog text of statements that caused RBR events
CREATION DATE..: Sat, 15 Aug 2009, 23:48
SUPERVISOR.....: Monty
IMPLEMENTOR....:
COPIES TO......: Knielsen, Serg
CATEGORY.......: Server-Sprint
TASK ID........: 47 (http://askmonty.org/worklog/?tid=47)
VERSION........: Server-9.x
STATUS.........: In-Progress
PRIORITY.......: 60
WORKED HOURS...: 20
ESTIMATE.......: 35 (hours remain)
ORIG. ESTIMATE.: 35
PROGRESS NOTES:
-=-=(Serg - Fri, 05 Feb 2010, 14:04)=-=-
Observers changed: Knielsen,Serg
-=-=(Guest - Fri, 05 Feb 2010, 13:40)=-=-
Category updated.
--- /tmp/wklog.47.old.9197 2010-02-05 13:40:36.000000000 +0200
+++ /tmp/wklog.47.new.9197 2010-02-05 13:40:36.000000000 +0200
@@ -1 +1 @@
-Server-RawIdeaBin
+Server-Sprint
-=-=(Guest - Fri, 05 Feb 2010, 13:40)=-=-
Status updated.
--- /tmp/wklog.47.old.9197 2010-02-05 13:40:36.000000000 +0200
+++ /tmp/wklog.47.new.9197 2010-02-05 13:40:36.000000000 +0200
@@ -1 +1 @@
-Un-Assigned
+In-Progress
-=-=(Alexi - Thu, 04 Feb 2010, 09:54)=-=-
Low Level Design modified.
--- /tmp/wklog.47.old.16174 2010-02-04 09:54:13.000000000 +0200
+++ /tmp/wklog.47.new.16174 2010-02-04 09:54:13.000000000 +0200
@@ -171,35 +171,20 @@
5. How slave IO thread requests Annotate_rows events
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-When requesting an event, the slave should inform the master whether
-it should send Annotate_rows events or not. To that end we add a new
-BINLOG_SEND_ANNOTATE_ROWS_EVENT flag used when requesting an event:
+If the replicate-annotate-rows-events option is not set on a slave, there
+is no need for master to send Annotate_rows events to this slave. The slave
+(or mysqlbinlog in remote case), before requesting binlog dump via the
+COM_BINLOG_DUMP command, informs the master whether it should send these
+events by executing the newly added COM_BINLOG_DUMP_OPTIONS_EXT server
+command:
+
+ case COM_BINLOG_DUMP_OPTIONS_EXT:
+ thd->binlog_dump_flags_ext= packet[0];
+ my_ok(thd);
+ break;
- #define BINLOG_DUMP_NON_BLOCK 1
- #define BINLOG_SEND_ANNOTATE_ROWS_EVENT 2
-
- pthread_handler_t handle_slave_io(void *arg)
- { ...
- request_dump(mysql, ...);
- ...
- }
-
- int request_dump(MYSQL* mysql, ...)
- { ...
- if (opt_log_slave_updates &&
- mi->io_thd->variables.binlog_annotate_rows_events)
- binlog_flags|= BINLOG_SEND_ANNOTATE_ROWS_EVENT;
- ...
- int2store(buf + 4, binlog_flags);
- ...
- simple_command(mysql, COM_BINLOG_DUMP, buf, ...);
- ...
- }
-
-NOTE. mysqlbinlog, when remotely requesting BINLOG_DUMP by calling the
-simple_command() function, should also use this flag if it wants (in case
-of the --print-annotate-rows-events option set) to recieve Annotate_rows
-events.
+Note. We add this new command and don't use COM_BINLOG_DUMP to avoid possible
+conflicts with MySQL/Sun.
6. How master executes the request
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -338,10 +323,4 @@
to internals(a)lists.mysql.com about this and suggesting to reserve the
event number.
-Also we should notice the introduction of the BINLOG_SEND_ANNOTATE_ROWS_EVENT
-flag taking into account that MySQL/Sun may also introduce a flag with the
-same value to be used in the request_dump-mysql_binlog_send interface.
-But this is mainly the question of merging: if a conflict concerning this
-flag occur, we may simply change the BINLOG_SEND_ANNOTATE_ROWS_EVENT value
-(this does not require additional changes in the code).
-=-=(Alexi - Sun, 20 Dec 2009, 16:00)=-=-
Low Level Design modified.
--- /tmp/wklog.47.old.19667 2009-12-20 14:00:56.000000000 +0000
+++ /tmp/wklog.47.new.19667 2009-12-20 14:00:56.000000000 +0000
@@ -196,6 +196,11 @@
...
}
+NOTE. mysqlbinlog, when remotely requesting BINLOG_DUMP by calling the
+simple_command() function, should also use this flag if it wants (in case
+of the --print-annotate-rows-events option set) to recieve Annotate_rows
+events.
+
6. How master executes the request
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -212,8 +217,7 @@
Log_event::read_log_event(&log, packet, ...);
...
if ((*packet)[EVENT_TYPE_OFFSET + 1] != ANNOTATE_ROWS_EVENT ||
- flags & BINLOG_SEND_ANNOTATE_ROWS_EVENT ||
- thd->server_id == 0 /* slave == mysqlbinlog */ )
+ flags & BINLOG_SEND_ANNOTATE_ROWS_EVENT)
{
my_net_write(net, packet->ptr(), packet->length());
}
-=-=(Alexi - Sun, 20 Dec 2009, 13:14)=-=-
Low Level Design modified.
--- /tmp/wklog.47.old.11350 2009-12-20 13:14:04.000000000 +0200
+++ /tmp/wklog.47.new.11350 2009-12-20 13:14:04.000000000 +0200
@@ -282,23 +282,18 @@
Annotate_rows_log_event* m_annotate_event;
};
-When the saved Annotate_rows object may be deleted? When all corresponding
-Rows events will be processed, i.e. before processing the first non-Rows
-event (note that Annotate_rows object resides in the binary log *after*
-the (possible) 'BEGIN' Query event which accompanies the rows events; note
-also that this deletion is adjusted with the case when some or all
-corresponding Rows events are filtered out by replicate filter rules):
+The saved Annotate_rows object should be deleted when all corresponding
+Rows events will be processed:
int exec_relay_log_event(THD* thd, Relay_log_info* rli)
{ ...
Log_event *ev= next_event(rli);
...
- if (rli->get_annotate_event() && !IS_RBR_EVENT_TYPE(ev->get_type_code()))
- rli->free_annotate_event();
-
apply_event_and_update_pos(ev, ...);
- if (ev->get_type_code() == ANNOTATE_ROWS_EVENT)
+ if (rli->get_annotate_event() && is_last_rows_event(ev))
+ rli->free_annotate_event();
+ else if (ev->get_type_code() == ANNOTATE_ROWS_EVENT)
rli->set_annotate_event((Annotate_rows_log_event*) ev);
else if (ev->get_type_code() != FORMAT_DESCRIPTION_EVENT)
delete ev;
@@ -307,10 +302,21 @@
where
- #define IS_RBR_EVENT_TYPE(type) ( (type) == TABLE_MAP_EVENT || \
- (type) == WRITE_ROWS_EVENT || \
+ bool is_last_rows_event(Log_event* ev)
+ {
+ Log_event_type type= ev->get_type_code();
+ if (IS_ROWS_EVENT_TYPE(type))
+ {
+ Rows_log_event* rows= (Rows_log_event*)ev;
+ return rows->get_flags(Rows_log_event::STMT_END_F);
+ }
+
+ return 0;
+ }
+
+ #define IS_ROWS_EVENT_TYPE(type) ((type) == WRITE_ROWS_EVENT || \
(type) == UPDATE_ROWS_EVENT || \
- (type) == DELETE_ROWS_EVENT )
+ (type) == DELETE_ROWS_EVENT)
8. General remarks
~~~~~~~~~~~~~~~~~~
-=-=(Alexi - Sun, 20 Dec 2009, 09:29)=-=-
Low Level Design modified.
--- /tmp/wklog.47.old.32726 2009-12-20 07:29:56.000000000 +0000
+++ /tmp/wklog.47.new.32726 2009-12-20 07:29:56.000000000 +0000
@@ -56,7 +56,7 @@
0000006A | 00 | 0 - RESERVED [27]
..... ...
00000081 | 00 | 0 - RESERVED [50]
- 00000082 | 00 | 0 - ANNOTATE_RBR_EVENT [51]
+ 00000082 | 00 | 0 - ANNOTATE_ROWS_EVENT [51]
************************
2. Outline of Annotate_rows event behavior
-=-=(Alexi - Sat, 19 Dec 2009, 16:10)=-=-
Low Level Design modified.
--- /tmp/wklog.47.old.16051 2009-12-19 16:10:48.000000000 +0200
+++ /tmp/wklog.47.new.16051 2009-12-19 16:10:48.000000000 +0200
@@ -253,7 +253,7 @@
thd query to that of the described by the event, i.e. to the query which
caused the subsequent Rows events (see "How Master writes Annotate_rows
events to the binary log" to follow what happens further when the subsequent
-Rows events is applied):
+Rows events are applied):
int Annotate_rows_log_event::do_apply_event(...)
{
-=-=(Alexi - Sat, 19 Dec 2009, 16:02)=-=-
High-Level Specification modified.
--- /tmp/wklog.47.old.15695 2009-12-19 16:02:33.000000000 +0200
+++ /tmp/wklog.47.new.15695 2009-12-19 16:02:33.000000000 +0200
@@ -12,7 +12,7 @@
post-header and contains the query text in its data part. Example:
************************
- ANNOTATE_RBR_EVENT
+ ANNOTATE_ROWS_EVENT
************************
00000220 | B6 A0 2C 4B | time_when = 1261215926
00000224 | 33 | event_type = 51
-=-=(Alexi - Sat, 19 Dec 2009, 15:58)=-=-
Low Level Design modified.
--- /tmp/wklog.47.old.15437 2009-12-19 13:58:12.000000000 +0000
+++ /tmp/wklog.47.new.15437 2009-12-19 13:58:12.000000000 +0000
@@ -1 +1,337 @@
+Content
+~~~~~~~
+ 1. Annotate_rows event number
+ 2. Outline of Annotate_rows event behavior
+ 3. How Master writes Annotate_rows events to the binary log
+ 4. How slave treats replicate-annotate-rows-events option
+ 5. How slave IO thread requests Annotate_rows events
+ 6. How master executes the request
+ 7. How slave SQL thread processes Annotate_rows events
+ 8. General remarks
+
+1. Annotate_rows event number
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+To avoid possible event numbers conflict with MySQL/Sun, we leave a gap
+between the last MySQL event number and the Annotate_rows event number:
+
+ enum Log_event_type
+ { ...
+ INCIDENT_EVENT= 26,
+ // New MySQL event numbers are to be added here
+ MYSQL_EVENTS_END,
+
+ MARIA_EVENTS_BEGIN= 51,
+ // New Maria event numbers start from here
+ ANNOTATE_ROWS_EVENT= 51,
+
+ ENUM_END_EVENT
+ };
+
+together with the corresponding extension of 'post_header_len' array in the
+Format description event. (This extension does not affect the compatibility
+of the binary log). Here is how Format description event looks like with
+this extension:
+
+ ************************
+ FORMAT_DESCRIPTION_EVENT
+ ************************
+ 00000004 | A1 A0 2C 4B | time_when = 1261215905
+ 00000008 | 0F | event_type = 15
+ 00000009 | 64 00 00 00 | server_id = 100
+ 0000000D | 7F 00 00 00 | event_len = 127
+ 00000011 | 83 00 00 00 | log_pos = 00000083
+ 00000015 | 01 00 | flags = LOG_EVENT_BINLOG_IN_USE_F
+ ------------------------
+ 00000017 | 04 00 | binlog_ver = 4
+ 00000019 | 35 2E 32 2E | server_ver = 5.2.0-MariaDB-alpha-debug-log
+ ..... ...
+ 0000004B | A1 A0 2C 4B | time_created = 1261215905
+ 0000004F | 13 | common_header_len = 19
+ ------------------------
+ post_header_len
+ ------------------------
+ 00000050 | 38 | 56 - START_EVENT_V3 [1]
+ ..... ...
+ 00000069 | 02 | 2 - INCIDENT_EVENT [26]
+ 0000006A | 00 | 0 - RESERVED [27]
+ ..... ...
+ 00000081 | 00 | 0 - RESERVED [50]
+ 00000082 | 00 | 0 - ANNOTATE_RBR_EVENT [51]
+ ************************
+
+2. Outline of Annotate_rows event behavior
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+Each Annotate_rows_log_event object has two private members describing the
+corresponding query:
+
+ char *m_query_txt;
+ uint m_query_len;
+
+When the object is created for writing to a binary log, this query is taken
+from 'thd' (for short, below we omit the 'Annotate_rows_log_event::' prefix
+as well as other implementation details):
+
+ Annotate_rows_log_event(THD *thd)
+ {
+ m_query_txt = thd->query();
+ m_query_len = thd->query_length();
+ }
+
+When the object is read from a binary log, the query is taken from the buffer
+containing the binary log representation of the event (this buffer is allocated
+in Log_event object from which all Log events are derived):
+
+ Annotate_rows_log_event(char *buf, uint event_len,
+ Format_description_log_event *desc)
+ {
+ m_query_len = event_len - desc->common_header_len;
+ m_query_txt = buf + desc->common_header_len;
+ }
+
+The events are written to the binary log by the Log_event::write() member
+which calls virtual write_data_header() and write_data_body() members
+("data header" and "post header" are synonym in replication terminology).
+In our case, data header is empty and data body is just the query:
+
+ bool write_data_body(IO_CACHE *file)
+ {
+ return my_b_safe_write(file, (uchar*) m_query_txt, m_query_len);
+ }
+
+Printing the event is just printing the query:
+
+ void Annotate_rows_log_event::print(FILE *file, PRINT_EVENT_INFO *pinfo)
+ {
+ my_b_printf(&pinfo->head_cache, "\tQuery: `%s`\n", m_query_txt);
+ }
+
+3. How Master writes Annotate_rows events to the binary log
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+The event is written to the binary log just before the group of Table_map
+events which precede corresponding Rows events (one query may generate
+several Table map events in the binary log, but the corresponding
+Annotate_rows event must be written only once before the first Table map
+event; hence the boolean variable 'with_annotate' below):
+
+ int write_locked_table_maps(THD *thd)
+ { ...
+ bool with_annotate= thd->variables.binlog_annotate_rows_events;
+ ...
+ for (uint i= 0; i < ... <number of tables> ...; ++i)
+ { ...
+ thd->binlog_write_table_map(table, ..., with_annotate);
+ with_annotate= 0; // write Annotate_event not more than once
+ ...
+ }
+ ...
+ }
+
+ int THD::binlog_write_table_map(TABLE *table, ..., bool with_annotate)
+ { ...
+ Table_map_log_event the_event(...);
+ ...
+ if (with_annotate)
+ {
+ Annotate_rows_log_event anno(this);
+ mysql_bin_log.write(&anno);
+ }
+
+ mysql_bin_log.write(&the_event);
+ ...
+ }
+
+4. How slave treats replicate-annotate-rows-events option
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+The replicate-annotate-rows-events option is treated just as the session
+value of the binlog_annotate_rows_events variable for the slave IO and
+SQL threads. This setting is done during initialization of these threads:
+
+ pthread_handler_t handle_slave_io(void *arg)
+ {
+ THD *thd= new THD;
+ ...
+ init_slave_thread(thd, SLAVE_THD_IO);
+ ...
+ }
+
+ pthread_handler_t handle_slave_sql(void *arg)
+ {
+ THD *thd= new THD;
+ ...
+ init_slave_thread(thd, SLAVE_THD_SQL);
+ ...
+ }
+
+ int init_slave_thread(THD* thd, SLAVE_THD_TYPE thd_type)
+ { ...
+ thd->variables.binlog_annotate_rows_events=
+ opt_replicate_annotate_rows_events;
+ ...
+ }
+
+5. How slave IO thread requests Annotate_rows events
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+When requesting an event, the slave should inform the master whether
+it should send Annotate_rows events or not. To that end we add a new
+BINLOG_SEND_ANNOTATE_ROWS_EVENT flag used when requesting an event:
+
+ #define BINLOG_DUMP_NON_BLOCK 1
+ #define BINLOG_SEND_ANNOTATE_ROWS_EVENT 2
+
+ pthread_handler_t handle_slave_io(void *arg)
+ { ...
+ request_dump(mysql, ...);
+ ...
+ }
+
+ int request_dump(MYSQL* mysql, ...)
+ { ...
+ if (opt_log_slave_updates &&
+ mi->io_thd->variables.binlog_annotate_rows_events)
+ binlog_flags|= BINLOG_SEND_ANNOTATE_ROWS_EVENT;
+ ...
+ int2store(buf + 4, binlog_flags);
+ ...
+ simple_command(mysql, COM_BINLOG_DUMP, buf, ...);
+ ...
+ }
+
+6. How master executes the request
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+ case COM_BINLOG_DUMP:
+ { ...
+ flags= uint2korr(packet + 4);
+ ...
+ mysql_binlog_send(thd, ..., flags);
+ ...
+ }
+
+ void mysql_binlog_send(THD* thd, ..., ushort flags)
+ { ...
+ Log_event::read_log_event(&log, packet, ...);
+ ...
+ if ((*packet)[EVENT_TYPE_OFFSET + 1] != ANNOTATE_ROWS_EVENT ||
+ flags & BINLOG_SEND_ANNOTATE_ROWS_EVENT ||
+ thd->server_id == 0 /* slave == mysqlbinlog */ )
+ {
+ my_net_write(net, packet->ptr(), packet->length());
+ }
+ ...
+ }
+
+7. How slave SQL thread processes Annotate_rows events
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+The slave processes each recieved event by "applying" it, i.e. by
+calling the Log_event::apply_event() function which in turn calls
+the virtual do_apply_event() member specific for each type of the
+event.
+
+ int exec_relay_log_event(THD* thd, Relay_log_info* rli)
+ { ...
+ Log_event *ev = next_event(rli);
+ ...
+ apply_event_and_update_pos(ev, ...);
+
+ if (ev->get_type_code() != FORMAT_DESCRIPTION_EVENT)
+ delete ev;
+ ...
+ }
+
+ int apply_event_and_update_pos(Log_event *ev, ...)
+ { ...
+ ev->apply_event(...);
+ ...
+ }
+
+ int Log_event::apply_event(...)
+ {
+ return do_apply_event(...);
+ }
+
+What does it mean to "apply" an Annotate_rows event? It means to set current
+thd query to that of the described by the event, i.e. to the query which
+caused the subsequent Rows events (see "How Master writes Annotate_rows
+events to the binary log" to follow what happens further when the subsequent
+Rows events is applied):
+
+ int Annotate_rows_log_event::do_apply_event(...)
+ {
+ thd->set_query(m_query_txt, m_query_len);
+ }
+
+NOTE. I am not sure, but possibly current values of thd->query and
+thd->query_length should be saved before calling set_query() and to be
+restored on the Annotate_rows_log_event object deletion.
+Is it really needed ?
+
+After calling this do_apply_event() function we may not delete the
+Annotate_rows_log_event object immediatedly (see exec_relay_log_event()
+above) because thd->query now points to the string inside this object.
+We may keep the pointer to this object in the Relay_log_info:
+
+ class Relay_log_info
+ {
+ public:
+ ...
+ void set_annotate_event(Annotate_rows_log_event*);
+ Annotate_rows_log_event* get_annotate_event();
+ void free_annotate_event();
+ ...
+ private:
+ Annotate_rows_log_event* m_annotate_event;
+ };
+
+When the saved Annotate_rows object may be deleted? When all corresponding
+Rows events will be processed, i.e. before processing the first non-Rows
+event (note that Annotate_rows object resides in the binary log *after*
+the (possible) 'BEGIN' Query event which accompanies the rows events; note
+also that this deletion is adjusted with the case when some or all
+corresponding Rows events are filtered out by replicate filter rules):
+
+ int exec_relay_log_event(THD* thd, Relay_log_info* rli)
+ { ...
+ Log_event *ev= next_event(rli);
+ ...
+ if (rli->get_annotate_event() && !IS_RBR_EVENT_TYPE(ev->get_type_code()))
+ rli->free_annotate_event();
+
+ apply_event_and_update_pos(ev, ...);
+
+ if (ev->get_type_code() == ANNOTATE_ROWS_EVENT)
+ rli->set_annotate_event((Annotate_rows_log_event*) ev);
+ else if (ev->get_type_code() != FORMAT_DESCRIPTION_EVENT)
+ delete ev;
+ ...
+ }
+
+where
+
+ #define IS_RBR_EVENT_TYPE(type) ( (type) == TABLE_MAP_EVENT || \
+ (type) == WRITE_ROWS_EVENT || \
+ (type) == UPDATE_ROWS_EVENT || \
+ (type) == DELETE_ROWS_EVENT )
+
+8. General remarks
+~~~~~~~~~~~~~~~~~~
+Kristian noticed that introducing new log event type should be coordinated
+somehow with MySQL/Sun:
+
+ Kristian: The numeric code for this event must be assigned carefully.
+ It should be coordinated with MySQL/Sun, otherwise we can get into a
+ situation where MySQL uses the same numeric code for one event that
+ MariaDB uses for ANNOTATE_ROWS_EVENT, which would make merging the two
+ impossible.
+ Alex: I reserved about 20 numbers not to have possible conflicts
+ with MySQL.
+ Kristian: Still, I think it would be appropriate to send a polite email
+ to internals(a)lists.mysql.com about this and suggesting to reserve the
+ event number.
+
+Also we should notice the introduction of the BINLOG_SEND_ANNOTATE_ROWS_EVENT
+flag taking into account that MySQL/Sun may also introduce a flag with the
+same value to be used in the request_dump-mysql_binlog_send interface.
+But this is mainly the question of merging: if a conflict concerning this
+flag occur, we may simply change the BINLOG_SEND_ANNOTATE_ROWS_EVENT value
+(this does not require additional changes in the code).
------------------------------------------------------------
-=-=(View All Progress Notes, 23 total)=-=-
http://askmonty.org/worklog/index.pl?tid=47&nolimit=1
DESCRIPTION:
Store in binlog (and show in mysqlbinlog output) texts of statements that
caused RBR events
This is needed for (list from Monty):
- Easier to understand why updates happened
- Would make it easier to find out where in application things went
wrong (as you can search for exact strings)
- Allow one to filter things based on comments in the statement.
The cost of this can be that the binlog will be approximately 2x in size
(especially insert of big blob's would be a bit painful), so this should
be an optional feature.
HIGH-LEVEL SPECIFICATION:
Content
~~~~~~~
1. Annotate_rows_log_event
2. Server option: --binlog-annotate-rows-events
3. Server option: --replicate-annotate-rows-events
4. mysqlbinlog option: --print-annotate-rows-events
5. mysqlbinlog output
1. Annotate_rows_log_event [ ANNOTATE_ROWS_EVENT ]
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Describes the query which caused the corresponding rows events. Has empty
post-header and contains the query text in its data part. Example:
************************
ANNOTATE_ROWS_EVENT
************************
00000220 | B6 A0 2C 4B | time_when = 1261215926
00000224 | 33 | event_type = 51
00000225 | 64 00 00 00 | server_id = 100
00000229 | 36 00 00 00 | event_len = 54
0000022D | 56 02 00 00 | log_pos = 00000256
00000231 | 00 00 | flags = <none>
------------------------
00000233 | 49 4E 53 45 | query = "INSERT INTO t1 VALUES (1), (2), (3)"
00000237 | 52 54 20 49 |
0000023B | 4E 54 4F 20 |
0000023F | 74 31 20 56 |
00000243 | 41 4C 55 45 |
00000247 | 53 20 28 31 |
0000024B | 29 2C 20 28 |
0000024F | 32 29 2C 20 |
00000253 | 28 33 29 |
************************
In binary log, Annotate_rows event follows the (possible) 'BEGIN' Query event
and precedes the first of Table map events which accompany the corresponding
rows events. (See example in the "mysqlbinlog output" section below.)
2. Server option: --binlog-annotate-rows-events
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Tells the master to write Annotate_rows events to the binary log.
* Variable Name: binlog_annotate_rows_events
* Scope: Global & Session
* Access Type: Dynamic
* Data Type: bool
* Default Value: OFF
NOTE. Session values allows to annotate only some selected statements:
...
SET SESSION binlog_annotate_rows_events=ON;
... statements to be annotated ...
SET SESSION binlog_annotate_rows_events=OFF;
... statements not to be annotated ...
3. Server option: --replicate-annotate-rows-events
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Tells the slave to reproduce Annotate_rows events recieved from the master
in its own binary log (sensible only in pair with log-slave-updates option).
* Variable Name: replicate_annotate_rows_events
* Scope: Global
* Access Type: Read only
* Data Type: bool
* Default Value: OFF
NOTE. Why do we additionally need this 'replicate' option? Why not to make
the slave to reproduce this events when its binlog-annotate-rows-events
global value is ON? Well, because, for example, we may want to configure
the slave which should reproduce Annotate_rows events but has global
binlog-annotate-rows-events = OFF meaning this to be the default value for
the client threads (see also "How slave treats replicate-annotate-rows-events
option" in LLD part).
4. mysqlbinlog option: --print-annotate-rows-events
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
With this option, mysqlbinlog prints the content of Annotate_rows events (if
the binary log does contain them). Without this option (i.e. by default),
mysqlbinlog skips Annotate_rows events.
5. mysqlbinlog output
~~~~~~~~~~~~~~~~~~~~~
With --print-annotate-rows-events, mysqlbinlog outputs Annotate_rows events
in a form like this:
...
# at 1646
#091219 12:45:26 server id 100 end_log_pos 1714 Query thread_id=1
exec_time=0 error_code=0
SET TIMESTAMP=1261215926/*!*/;
BEGIN
/*!*/;
# at 1714
# at 1812
# at 1853
# at 1894
# at 1938
#091219 12:45:26 server id 100 end_log_pos 1812 Query: `DELETE t1, t2 FROM
t1 INNER JOIN t2 INNER JOIN t3 WHERE t1.a=t2.a AND t2.a=t3.a`
#091219 12:45:26 server id 100 end_log_pos 1853 Table_map: `test`.`t1`
mapped to number 16
#091219 12:45:26 server id 100 end_log_pos 1894 Table_map: `test`.`t2`
mapped to number 17
#091219 12:45:26 server id 100 end_log_pos 1938 Delete_rows: table id 16
#091219 12:45:26 server id 100 end_log_pos 1982 Delete_rows: table id 17
flags: STMT_END_F
...
LOW-LEVEL DESIGN:
Content
~~~~~~~
1. Annotate_rows event number
2. Outline of Annotate_rows event behavior
3. How Master writes Annotate_rows events to the binary log
4. How slave treats replicate-annotate-rows-events option
5. How slave IO thread requests Annotate_rows events
6. How master executes the request
7. How slave SQL thread processes Annotate_rows events
8. General remarks
1. Annotate_rows event number
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
To avoid possible event numbers conflict with MySQL/Sun, we leave a gap
between the last MySQL event number and the Annotate_rows event number:
enum Log_event_type
{ ...
INCIDENT_EVENT= 26,
// New MySQL event numbers are to be added here
MYSQL_EVENTS_END,
MARIA_EVENTS_BEGIN= 51,
// New Maria event numbers start from here
ANNOTATE_ROWS_EVENT= 51,
ENUM_END_EVENT
};
together with the corresponding extension of 'post_header_len' array in the
Format description event. (This extension does not affect the compatibility
of the binary log). Here is how Format description event looks like with
this extension:
************************
FORMAT_DESCRIPTION_EVENT
************************
00000004 | A1 A0 2C 4B | time_when = 1261215905
00000008 | 0F | event_type = 15
00000009 | 64 00 00 00 | server_id = 100
0000000D | 7F 00 00 00 | event_len = 127
00000011 | 83 00 00 00 | log_pos = 00000083
00000015 | 01 00 | flags = LOG_EVENT_BINLOG_IN_USE_F
------------------------
00000017 | 04 00 | binlog_ver = 4
00000019 | 35 2E 32 2E | server_ver = 5.2.0-MariaDB-alpha-debug-log
..... ...
0000004B | A1 A0 2C 4B | time_created = 1261215905
0000004F | 13 | common_header_len = 19
------------------------
post_header_len
------------------------
00000050 | 38 | 56 - START_EVENT_V3 [1]
..... ...
00000069 | 02 | 2 - INCIDENT_EVENT [26]
0000006A | 00 | 0 - RESERVED [27]
..... ...
00000081 | 00 | 0 - RESERVED [50]
00000082 | 00 | 0 - ANNOTATE_ROWS_EVENT [51]
************************
2. Outline of Annotate_rows event behavior
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Each Annotate_rows_log_event object has two private members describing the
corresponding query:
char *m_query_txt;
uint m_query_len;
When the object is created for writing to a binary log, this query is taken
from 'thd' (for short, below we omit the 'Annotate_rows_log_event::' prefix
as well as other implementation details):
Annotate_rows_log_event(THD *thd)
{
m_query_txt = thd->query();
m_query_len = thd->query_length();
}
When the object is read from a binary log, the query is taken from the buffer
containing the binary log representation of the event (this buffer is allocated
in Log_event object from which all Log events are derived):
Annotate_rows_log_event(char *buf, uint event_len,
Format_description_log_event *desc)
{
m_query_len = event_len - desc->common_header_len;
m_query_txt = buf + desc->common_header_len;
}
The events are written to the binary log by the Log_event::write() member
which calls virtual write_data_header() and write_data_body() members
("data header" and "post header" are synonym in replication terminology).
In our case, data header is empty and data body is just the query:
bool write_data_body(IO_CACHE *file)
{
return my_b_safe_write(file, (uchar*) m_query_txt, m_query_len);
}
Printing the event is just printing the query:
void Annotate_rows_log_event::print(FILE *file, PRINT_EVENT_INFO *pinfo)
{
my_b_printf(&pinfo->head_cache, "\tQuery: `%s`\n", m_query_txt);
}
3. How Master writes Annotate_rows events to the binary log
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
The event is written to the binary log just before the group of Table_map
events which precede corresponding Rows events (one query may generate
several Table map events in the binary log, but the corresponding
Annotate_rows event must be written only once before the first Table map
event; hence the boolean variable 'with_annotate' below):
int write_locked_table_maps(THD *thd)
{ ...
bool with_annotate= thd->variables.binlog_annotate_rows_events;
...
for (uint i= 0; i < ... <number of tables> ...; ++i)
{ ...
thd->binlog_write_table_map(table, ..., with_annotate);
with_annotate= 0; // write Annotate_event not more than once
...
}
...
}
int THD::binlog_write_table_map(TABLE *table, ..., bool with_annotate)
{ ...
Table_map_log_event the_event(...);
...
if (with_annotate)
{
Annotate_rows_log_event anno(this);
mysql_bin_log.write(&anno);
}
mysql_bin_log.write(&the_event);
...
}
4. How slave treats replicate-annotate-rows-events option
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
The replicate-annotate-rows-events option is treated just as the session
value of the binlog_annotate_rows_events variable for the slave IO and
SQL threads. This setting is done during initialization of these threads:
pthread_handler_t handle_slave_io(void *arg)
{
THD *thd= new THD;
...
init_slave_thread(thd, SLAVE_THD_IO);
...
}
pthread_handler_t handle_slave_sql(void *arg)
{
THD *thd= new THD;
...
init_slave_thread(thd, SLAVE_THD_SQL);
...
}
int init_slave_thread(THD* thd, SLAVE_THD_TYPE thd_type)
{ ...
thd->variables.binlog_annotate_rows_events=
opt_replicate_annotate_rows_events;
...
}
5. How slave IO thread requests Annotate_rows events
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
If the replicate-annotate-rows-events option is not set on a slave, there
is no need for master to send Annotate_rows events to this slave. The slave
(or mysqlbinlog in remote case), before requesting binlog dump via the
COM_BINLOG_DUMP command, informs the master whether it should send these
events by executing the newly added COM_BINLOG_DUMP_OPTIONS_EXT server
command:
case COM_BINLOG_DUMP_OPTIONS_EXT:
thd->binlog_dump_flags_ext= packet[0];
my_ok(thd);
break;
Note. We add this new command and don't use COM_BINLOG_DUMP to avoid possible
conflicts with MySQL/Sun.
6. How master executes the request
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
case COM_BINLOG_DUMP:
{ ...
flags= uint2korr(packet + 4);
...
mysql_binlog_send(thd, ..., flags);
...
}
void mysql_binlog_send(THD* thd, ..., ushort flags)
{ ...
Log_event::read_log_event(&log, packet, ...);
...
if ((*packet)[EVENT_TYPE_OFFSET + 1] != ANNOTATE_ROWS_EVENT ||
flags & BINLOG_SEND_ANNOTATE_ROWS_EVENT)
{
my_net_write(net, packet->ptr(), packet->length());
}
...
}
7. How slave SQL thread processes Annotate_rows events
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
The slave processes each recieved event by "applying" it, i.e. by
calling the Log_event::apply_event() function which in turn calls
the virtual do_apply_event() member specific for each type of the
event.
int exec_relay_log_event(THD* thd, Relay_log_info* rli)
{ ...
Log_event *ev = next_event(rli);
...
apply_event_and_update_pos(ev, ...);
if (ev->get_type_code() != FORMAT_DESCRIPTION_EVENT)
delete ev;
...
}
int apply_event_and_update_pos(Log_event *ev, ...)
{ ...
ev->apply_event(...);
...
}
int Log_event::apply_event(...)
{
return do_apply_event(...);
}
What does it mean to "apply" an Annotate_rows event? It means to set current
thd query to that of the described by the event, i.e. to the query which
caused the subsequent Rows events (see "How Master writes Annotate_rows
events to the binary log" to follow what happens further when the subsequent
Rows events are applied):
int Annotate_rows_log_event::do_apply_event(...)
{
thd->set_query(m_query_txt, m_query_len);
}
NOTE. I am not sure, but possibly current values of thd->query and
thd->query_length should be saved before calling set_query() and to be
restored on the Annotate_rows_log_event object deletion.
Is it really needed ?
After calling this do_apply_event() function we may not delete the
Annotate_rows_log_event object immediatedly (see exec_relay_log_event()
above) because thd->query now points to the string inside this object.
We may keep the pointer to this object in the Relay_log_info:
class Relay_log_info
{
public:
...
void set_annotate_event(Annotate_rows_log_event*);
Annotate_rows_log_event* get_annotate_event();
void free_annotate_event();
...
private:
Annotate_rows_log_event* m_annotate_event;
};
The saved Annotate_rows object should be deleted when all corresponding
Rows events will be processed:
int exec_relay_log_event(THD* thd, Relay_log_info* rli)
{ ...
Log_event *ev= next_event(rli);
...
apply_event_and_update_pos(ev, ...);
if (rli->get_annotate_event() && is_last_rows_event(ev))
rli->free_annotate_event();
else if (ev->get_type_code() == ANNOTATE_ROWS_EVENT)
rli->set_annotate_event((Annotate_rows_log_event*) ev);
else if (ev->get_type_code() != FORMAT_DESCRIPTION_EVENT)
delete ev;
...
}
where
bool is_last_rows_event(Log_event* ev)
{
Log_event_type type= ev->get_type_code();
if (IS_ROWS_EVENT_TYPE(type))
{
Rows_log_event* rows= (Rows_log_event*)ev;
return rows->get_flags(Rows_log_event::STMT_END_F);
}
return 0;
}
#define IS_ROWS_EVENT_TYPE(type) ((type) == WRITE_ROWS_EVENT || \
(type) == UPDATE_ROWS_EVENT || \
(type) == DELETE_ROWS_EVENT)
8. General remarks
~~~~~~~~~~~~~~~~~~
Kristian noticed that introducing new log event type should be coordinated
somehow with MySQL/Sun:
Kristian: The numeric code for this event must be assigned carefully.
It should be coordinated with MySQL/Sun, otherwise we can get into a
situation where MySQL uses the same numeric code for one event that
MariaDB uses for ANNOTATE_ROWS_EVENT, which would make merging the two
impossible.
Alex: I reserved about 20 numbers not to have possible conflicts
with MySQL.
Kristian: Still, I think it would be appropriate to send a polite email
to internals(a)lists.mysql.com about this and suggesting to reserve the
event number.
ESTIMATED WORK TIME
ESTIMATED COMPLETION DATE
-----------------------------------------------------------------------
WorkLog (v3.5.9)
1
0
[Maria-developers] Updated (by Guest): Store in binlog text of statements that caused RBR events (47)
by worklog-noreply@askmonty.org 05 Feb '10
by worklog-noreply@askmonty.org 05 Feb '10
05 Feb '10
-----------------------------------------------------------------------
WORKLOG TASK
-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
TASK...........: Store in binlog text of statements that caused RBR events
CREATION DATE..: Sat, 15 Aug 2009, 23:48
SUPERVISOR.....: Monty
IMPLEMENTOR....:
COPIES TO......: Knielsen
CATEGORY.......: Server-Sprint
TASK ID........: 47 (http://askmonty.org/worklog/?tid=47)
VERSION........: Server-9.x
STATUS.........: In-Progress
PRIORITY.......: 60
WORKED HOURS...: 20
ESTIMATE.......: 35 (hours remain)
ORIG. ESTIMATE.: 35
PROGRESS NOTES:
-=-=(Guest - Fri, 05 Feb 2010, 13:40)=-=-
Category updated.
--- /tmp/wklog.47.old.9197 2010-02-05 13:40:36.000000000 +0200
+++ /tmp/wklog.47.new.9197 2010-02-05 13:40:36.000000000 +0200
@@ -1 +1 @@
-Server-RawIdeaBin
+Server-Sprint
-=-=(Guest - Fri, 05 Feb 2010, 13:40)=-=-
Status updated.
--- /tmp/wklog.47.old.9197 2010-02-05 13:40:36.000000000 +0200
+++ /tmp/wklog.47.new.9197 2010-02-05 13:40:36.000000000 +0200
@@ -1 +1 @@
-Un-Assigned
+In-Progress
-=-=(Alexi - Thu, 04 Feb 2010, 09:54)=-=-
Low Level Design modified.
--- /tmp/wklog.47.old.16174 2010-02-04 09:54:13.000000000 +0200
+++ /tmp/wklog.47.new.16174 2010-02-04 09:54:13.000000000 +0200
@@ -171,35 +171,20 @@
5. How slave IO thread requests Annotate_rows events
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-When requesting an event, the slave should inform the master whether
-it should send Annotate_rows events or not. To that end we add a new
-BINLOG_SEND_ANNOTATE_ROWS_EVENT flag used when requesting an event:
+If the replicate-annotate-rows-events option is not set on a slave, there
+is no need for master to send Annotate_rows events to this slave. The slave
+(or mysqlbinlog in remote case), before requesting binlog dump via the
+COM_BINLOG_DUMP command, informs the master whether it should send these
+events by executing the newly added COM_BINLOG_DUMP_OPTIONS_EXT server
+command:
+
+ case COM_BINLOG_DUMP_OPTIONS_EXT:
+ thd->binlog_dump_flags_ext= packet[0];
+ my_ok(thd);
+ break;
- #define BINLOG_DUMP_NON_BLOCK 1
- #define BINLOG_SEND_ANNOTATE_ROWS_EVENT 2
-
- pthread_handler_t handle_slave_io(void *arg)
- { ...
- request_dump(mysql, ...);
- ...
- }
-
- int request_dump(MYSQL* mysql, ...)
- { ...
- if (opt_log_slave_updates &&
- mi->io_thd->variables.binlog_annotate_rows_events)
- binlog_flags|= BINLOG_SEND_ANNOTATE_ROWS_EVENT;
- ...
- int2store(buf + 4, binlog_flags);
- ...
- simple_command(mysql, COM_BINLOG_DUMP, buf, ...);
- ...
- }
-
-NOTE. mysqlbinlog, when remotely requesting BINLOG_DUMP by calling the
-simple_command() function, should also use this flag if it wants (in case
-of the --print-annotate-rows-events option set) to recieve Annotate_rows
-events.
+Note. We add this new command and don't use COM_BINLOG_DUMP to avoid possible
+conflicts with MySQL/Sun.
6. How master executes the request
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -338,10 +323,4 @@
to internals(a)lists.mysql.com about this and suggesting to reserve the
event number.
-Also we should notice the introduction of the BINLOG_SEND_ANNOTATE_ROWS_EVENT
-flag taking into account that MySQL/Sun may also introduce a flag with the
-same value to be used in the request_dump-mysql_binlog_send interface.
-But this is mainly the question of merging: if a conflict concerning this
-flag occur, we may simply change the BINLOG_SEND_ANNOTATE_ROWS_EVENT value
-(this does not require additional changes in the code).
-=-=(Alexi - Sun, 20 Dec 2009, 16:00)=-=-
Low Level Design modified.
--- /tmp/wklog.47.old.19667 2009-12-20 14:00:56.000000000 +0000
+++ /tmp/wklog.47.new.19667 2009-12-20 14:00:56.000000000 +0000
@@ -196,6 +196,11 @@
...
}
+NOTE. mysqlbinlog, when remotely requesting BINLOG_DUMP by calling the
+simple_command() function, should also use this flag if it wants (in case
+of the --print-annotate-rows-events option set) to recieve Annotate_rows
+events.
+
6. How master executes the request
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -212,8 +217,7 @@
Log_event::read_log_event(&log, packet, ...);
...
if ((*packet)[EVENT_TYPE_OFFSET + 1] != ANNOTATE_ROWS_EVENT ||
- flags & BINLOG_SEND_ANNOTATE_ROWS_EVENT ||
- thd->server_id == 0 /* slave == mysqlbinlog */ )
+ flags & BINLOG_SEND_ANNOTATE_ROWS_EVENT)
{
my_net_write(net, packet->ptr(), packet->length());
}
-=-=(Alexi - Sun, 20 Dec 2009, 13:14)=-=-
Low Level Design modified.
--- /tmp/wklog.47.old.11350 2009-12-20 13:14:04.000000000 +0200
+++ /tmp/wklog.47.new.11350 2009-12-20 13:14:04.000000000 +0200
@@ -282,23 +282,18 @@
Annotate_rows_log_event* m_annotate_event;
};
-When the saved Annotate_rows object may be deleted? When all corresponding
-Rows events will be processed, i.e. before processing the first non-Rows
-event (note that Annotate_rows object resides in the binary log *after*
-the (possible) 'BEGIN' Query event which accompanies the rows events; note
-also that this deletion is adjusted with the case when some or all
-corresponding Rows events are filtered out by replicate filter rules):
+The saved Annotate_rows object should be deleted when all corresponding
+Rows events will be processed:
int exec_relay_log_event(THD* thd, Relay_log_info* rli)
{ ...
Log_event *ev= next_event(rli);
...
- if (rli->get_annotate_event() && !IS_RBR_EVENT_TYPE(ev->get_type_code()))
- rli->free_annotate_event();
-
apply_event_and_update_pos(ev, ...);
- if (ev->get_type_code() == ANNOTATE_ROWS_EVENT)
+ if (rli->get_annotate_event() && is_last_rows_event(ev))
+ rli->free_annotate_event();
+ else if (ev->get_type_code() == ANNOTATE_ROWS_EVENT)
rli->set_annotate_event((Annotate_rows_log_event*) ev);
else if (ev->get_type_code() != FORMAT_DESCRIPTION_EVENT)
delete ev;
@@ -307,10 +302,21 @@
where
- #define IS_RBR_EVENT_TYPE(type) ( (type) == TABLE_MAP_EVENT || \
- (type) == WRITE_ROWS_EVENT || \
+ bool is_last_rows_event(Log_event* ev)
+ {
+ Log_event_type type= ev->get_type_code();
+ if (IS_ROWS_EVENT_TYPE(type))
+ {
+ Rows_log_event* rows= (Rows_log_event*)ev;
+ return rows->get_flags(Rows_log_event::STMT_END_F);
+ }
+
+ return 0;
+ }
+
+ #define IS_ROWS_EVENT_TYPE(type) ((type) == WRITE_ROWS_EVENT || \
(type) == UPDATE_ROWS_EVENT || \
- (type) == DELETE_ROWS_EVENT )
+ (type) == DELETE_ROWS_EVENT)
8. General remarks
~~~~~~~~~~~~~~~~~~
-=-=(Alexi - Sun, 20 Dec 2009, 09:29)=-=-
Low Level Design modified.
--- /tmp/wklog.47.old.32726 2009-12-20 07:29:56.000000000 +0000
+++ /tmp/wklog.47.new.32726 2009-12-20 07:29:56.000000000 +0000
@@ -56,7 +56,7 @@
0000006A | 00 | 0 - RESERVED [27]
..... ...
00000081 | 00 | 0 - RESERVED [50]
- 00000082 | 00 | 0 - ANNOTATE_RBR_EVENT [51]
+ 00000082 | 00 | 0 - ANNOTATE_ROWS_EVENT [51]
************************
2. Outline of Annotate_rows event behavior
-=-=(Alexi - Sat, 19 Dec 2009, 16:10)=-=-
Low Level Design modified.
--- /tmp/wklog.47.old.16051 2009-12-19 16:10:48.000000000 +0200
+++ /tmp/wklog.47.new.16051 2009-12-19 16:10:48.000000000 +0200
@@ -253,7 +253,7 @@
thd query to that of the described by the event, i.e. to the query which
caused the subsequent Rows events (see "How Master writes Annotate_rows
events to the binary log" to follow what happens further when the subsequent
-Rows events is applied):
+Rows events are applied):
int Annotate_rows_log_event::do_apply_event(...)
{
-=-=(Alexi - Sat, 19 Dec 2009, 16:02)=-=-
High-Level Specification modified.
--- /tmp/wklog.47.old.15695 2009-12-19 16:02:33.000000000 +0200
+++ /tmp/wklog.47.new.15695 2009-12-19 16:02:33.000000000 +0200
@@ -12,7 +12,7 @@
post-header and contains the query text in its data part. Example:
************************
- ANNOTATE_RBR_EVENT
+ ANNOTATE_ROWS_EVENT
************************
00000220 | B6 A0 2C 4B | time_when = 1261215926
00000224 | 33 | event_type = 51
-=-=(Alexi - Sat, 19 Dec 2009, 15:58)=-=-
Low Level Design modified.
--- /tmp/wklog.47.old.15437 2009-12-19 13:58:12.000000000 +0000
+++ /tmp/wklog.47.new.15437 2009-12-19 13:58:12.000000000 +0000
@@ -1 +1,337 @@
+Content
+~~~~~~~
+ 1. Annotate_rows event number
+ 2. Outline of Annotate_rows event behavior
+ 3. How Master writes Annotate_rows events to the binary log
+ 4. How slave treats replicate-annotate-rows-events option
+ 5. How slave IO thread requests Annotate_rows events
+ 6. How master executes the request
+ 7. How slave SQL thread processes Annotate_rows events
+ 8. General remarks
+
+1. Annotate_rows event number
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+To avoid possible event numbers conflict with MySQL/Sun, we leave a gap
+between the last MySQL event number and the Annotate_rows event number:
+
+ enum Log_event_type
+ { ...
+ INCIDENT_EVENT= 26,
+ // New MySQL event numbers are to be added here
+ MYSQL_EVENTS_END,
+
+ MARIA_EVENTS_BEGIN= 51,
+ // New Maria event numbers start from here
+ ANNOTATE_ROWS_EVENT= 51,
+
+ ENUM_END_EVENT
+ };
+
+together with the corresponding extension of 'post_header_len' array in the
+Format description event. (This extension does not affect the compatibility
+of the binary log). Here is how Format description event looks like with
+this extension:
+
+ ************************
+ FORMAT_DESCRIPTION_EVENT
+ ************************
+ 00000004 | A1 A0 2C 4B | time_when = 1261215905
+ 00000008 | 0F | event_type = 15
+ 00000009 | 64 00 00 00 | server_id = 100
+ 0000000D | 7F 00 00 00 | event_len = 127
+ 00000011 | 83 00 00 00 | log_pos = 00000083
+ 00000015 | 01 00 | flags = LOG_EVENT_BINLOG_IN_USE_F
+ ------------------------
+ 00000017 | 04 00 | binlog_ver = 4
+ 00000019 | 35 2E 32 2E | server_ver = 5.2.0-MariaDB-alpha-debug-log
+ ..... ...
+ 0000004B | A1 A0 2C 4B | time_created = 1261215905
+ 0000004F | 13 | common_header_len = 19
+ ------------------------
+ post_header_len
+ ------------------------
+ 00000050 | 38 | 56 - START_EVENT_V3 [1]
+ ..... ...
+ 00000069 | 02 | 2 - INCIDENT_EVENT [26]
+ 0000006A | 00 | 0 - RESERVED [27]
+ ..... ...
+ 00000081 | 00 | 0 - RESERVED [50]
+ 00000082 | 00 | 0 - ANNOTATE_RBR_EVENT [51]
+ ************************
+
+2. Outline of Annotate_rows event behavior
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+Each Annotate_rows_log_event object has two private members describing the
+corresponding query:
+
+ char *m_query_txt;
+ uint m_query_len;
+
+When the object is created for writing to a binary log, this query is taken
+from 'thd' (for short, below we omit the 'Annotate_rows_log_event::' prefix
+as well as other implementation details):
+
+ Annotate_rows_log_event(THD *thd)
+ {
+ m_query_txt = thd->query();
+ m_query_len = thd->query_length();
+ }
+
+When the object is read from a binary log, the query is taken from the buffer
+containing the binary log representation of the event (this buffer is allocated
+in Log_event object from which all Log events are derived):
+
+ Annotate_rows_log_event(char *buf, uint event_len,
+ Format_description_log_event *desc)
+ {
+ m_query_len = event_len - desc->common_header_len;
+ m_query_txt = buf + desc->common_header_len;
+ }
+
+The events are written to the binary log by the Log_event::write() member
+which calls virtual write_data_header() and write_data_body() members
+("data header" and "post header" are synonym in replication terminology).
+In our case, data header is empty and data body is just the query:
+
+ bool write_data_body(IO_CACHE *file)
+ {
+ return my_b_safe_write(file, (uchar*) m_query_txt, m_query_len);
+ }
+
+Printing the event is just printing the query:
+
+ void Annotate_rows_log_event::print(FILE *file, PRINT_EVENT_INFO *pinfo)
+ {
+ my_b_printf(&pinfo->head_cache, "\tQuery: `%s`\n", m_query_txt);
+ }
+
+3. How Master writes Annotate_rows events to the binary log
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+The event is written to the binary log just before the group of Table_map
+events which precede corresponding Rows events (one query may generate
+several Table map events in the binary log, but the corresponding
+Annotate_rows event must be written only once before the first Table map
+event; hence the boolean variable 'with_annotate' below):
+
+ int write_locked_table_maps(THD *thd)
+ { ...
+ bool with_annotate= thd->variables.binlog_annotate_rows_events;
+ ...
+ for (uint i= 0; i < ... <number of tables> ...; ++i)
+ { ...
+ thd->binlog_write_table_map(table, ..., with_annotate);
+ with_annotate= 0; // write Annotate_event not more than once
+ ...
+ }
+ ...
+ }
+
+ int THD::binlog_write_table_map(TABLE *table, ..., bool with_annotate)
+ { ...
+ Table_map_log_event the_event(...);
+ ...
+ if (with_annotate)
+ {
+ Annotate_rows_log_event anno(this);
+ mysql_bin_log.write(&anno);
+ }
+
+ mysql_bin_log.write(&the_event);
+ ...
+ }
+
+4. How slave treats replicate-annotate-rows-events option
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+The replicate-annotate-rows-events option is treated just as the session
+value of the binlog_annotate_rows_events variable for the slave IO and
+SQL threads. This setting is done during initialization of these threads:
+
+ pthread_handler_t handle_slave_io(void *arg)
+ {
+ THD *thd= new THD;
+ ...
+ init_slave_thread(thd, SLAVE_THD_IO);
+ ...
+ }
+
+ pthread_handler_t handle_slave_sql(void *arg)
+ {
+ THD *thd= new THD;
+ ...
+ init_slave_thread(thd, SLAVE_THD_SQL);
+ ...
+ }
+
+ int init_slave_thread(THD* thd, SLAVE_THD_TYPE thd_type)
+ { ...
+ thd->variables.binlog_annotate_rows_events=
+ opt_replicate_annotate_rows_events;
+ ...
+ }
+
+5. How slave IO thread requests Annotate_rows events
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+When requesting an event, the slave should inform the master whether
+it should send Annotate_rows events or not. To that end we add a new
+BINLOG_SEND_ANNOTATE_ROWS_EVENT flag used when requesting an event:
+
+ #define BINLOG_DUMP_NON_BLOCK 1
+ #define BINLOG_SEND_ANNOTATE_ROWS_EVENT 2
+
+ pthread_handler_t handle_slave_io(void *arg)
+ { ...
+ request_dump(mysql, ...);
+ ...
+ }
+
+ int request_dump(MYSQL* mysql, ...)
+ { ...
+ if (opt_log_slave_updates &&
+ mi->io_thd->variables.binlog_annotate_rows_events)
+ binlog_flags|= BINLOG_SEND_ANNOTATE_ROWS_EVENT;
+ ...
+ int2store(buf + 4, binlog_flags);
+ ...
+ simple_command(mysql, COM_BINLOG_DUMP, buf, ...);
+ ...
+ }
+
+6. How master executes the request
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+ case COM_BINLOG_DUMP:
+ { ...
+ flags= uint2korr(packet + 4);
+ ...
+ mysql_binlog_send(thd, ..., flags);
+ ...
+ }
+
+ void mysql_binlog_send(THD* thd, ..., ushort flags)
+ { ...
+ Log_event::read_log_event(&log, packet, ...);
+ ...
+ if ((*packet)[EVENT_TYPE_OFFSET + 1] != ANNOTATE_ROWS_EVENT ||
+ flags & BINLOG_SEND_ANNOTATE_ROWS_EVENT ||
+ thd->server_id == 0 /* slave == mysqlbinlog */ )
+ {
+ my_net_write(net, packet->ptr(), packet->length());
+ }
+ ...
+ }
+
+7. How slave SQL thread processes Annotate_rows events
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+The slave processes each recieved event by "applying" it, i.e. by
+calling the Log_event::apply_event() function which in turn calls
+the virtual do_apply_event() member specific for each type of the
+event.
+
+ int exec_relay_log_event(THD* thd, Relay_log_info* rli)
+ { ...
+ Log_event *ev = next_event(rli);
+ ...
+ apply_event_and_update_pos(ev, ...);
+
+ if (ev->get_type_code() != FORMAT_DESCRIPTION_EVENT)
+ delete ev;
+ ...
+ }
+
+ int apply_event_and_update_pos(Log_event *ev, ...)
+ { ...
+ ev->apply_event(...);
+ ...
+ }
+
+ int Log_event::apply_event(...)
+ {
+ return do_apply_event(...);
+ }
+
+What does it mean to "apply" an Annotate_rows event? It means to set current
+thd query to that of the described by the event, i.e. to the query which
+caused the subsequent Rows events (see "How Master writes Annotate_rows
+events to the binary log" to follow what happens further when the subsequent
+Rows events is applied):
+
+ int Annotate_rows_log_event::do_apply_event(...)
+ {
+ thd->set_query(m_query_txt, m_query_len);
+ }
+
+NOTE. I am not sure, but possibly current values of thd->query and
+thd->query_length should be saved before calling set_query() and to be
+restored on the Annotate_rows_log_event object deletion.
+Is it really needed ?
+
+After calling this do_apply_event() function we may not delete the
+Annotate_rows_log_event object immediatedly (see exec_relay_log_event()
+above) because thd->query now points to the string inside this object.
+We may keep the pointer to this object in the Relay_log_info:
+
+ class Relay_log_info
+ {
+ public:
+ ...
+ void set_annotate_event(Annotate_rows_log_event*);
+ Annotate_rows_log_event* get_annotate_event();
+ void free_annotate_event();
+ ...
+ private:
+ Annotate_rows_log_event* m_annotate_event;
+ };
+
+When the saved Annotate_rows object may be deleted? When all corresponding
+Rows events will be processed, i.e. before processing the first non-Rows
+event (note that Annotate_rows object resides in the binary log *after*
+the (possible) 'BEGIN' Query event which accompanies the rows events; note
+also that this deletion is adjusted with the case when some or all
+corresponding Rows events are filtered out by replicate filter rules):
+
+ int exec_relay_log_event(THD* thd, Relay_log_info* rli)
+ { ...
+ Log_event *ev= next_event(rli);
+ ...
+ if (rli->get_annotate_event() && !IS_RBR_EVENT_TYPE(ev->get_type_code()))
+ rli->free_annotate_event();
+
+ apply_event_and_update_pos(ev, ...);
+
+ if (ev->get_type_code() == ANNOTATE_ROWS_EVENT)
+ rli->set_annotate_event((Annotate_rows_log_event*) ev);
+ else if (ev->get_type_code() != FORMAT_DESCRIPTION_EVENT)
+ delete ev;
+ ...
+ }
+
+where
+
+ #define IS_RBR_EVENT_TYPE(type) ( (type) == TABLE_MAP_EVENT || \
+ (type) == WRITE_ROWS_EVENT || \
+ (type) == UPDATE_ROWS_EVENT || \
+ (type) == DELETE_ROWS_EVENT )
+
+8. General remarks
+~~~~~~~~~~~~~~~~~~
+Kristian noticed that introducing new log event type should be coordinated
+somehow with MySQL/Sun:
+
+ Kristian: The numeric code for this event must be assigned carefully.
+ It should be coordinated with MySQL/Sun, otherwise we can get into a
+ situation where MySQL uses the same numeric code for one event that
+ MariaDB uses for ANNOTATE_ROWS_EVENT, which would make merging the two
+ impossible.
+ Alex: I reserved about 20 numbers not to have possible conflicts
+ with MySQL.
+ Kristian: Still, I think it would be appropriate to send a polite email
+ to internals(a)lists.mysql.com about this and suggesting to reserve the
+ event number.
+
+Also we should notice the introduction of the BINLOG_SEND_ANNOTATE_ROWS_EVENT
+flag taking into account that MySQL/Sun may also introduce a flag with the
+same value to be used in the request_dump-mysql_binlog_send interface.
+But this is mainly the question of merging: if a conflict concerning this
+flag occur, we may simply change the BINLOG_SEND_ANNOTATE_ROWS_EVENT value
+(this does not require additional changes in the code).
-=-=(Alexi - Sat, 19 Dec 2009, 15:41)=-=-
High-Level Specification modified.
--- /tmp/wklog.47.old.14545 2009-12-19 15:41:21.000000000 +0200
+++ /tmp/wklog.47.new.14545 2009-12-19 15:41:21.000000000 +0200
@@ -1,122 +1,107 @@
-First suggestion:
-
-> I think for this we would actually need a new binlog event type
-> (Comment_log_event?). Unless we want to log an empty statement Query_log_event
-> containing only a comment (a bit of a hack).
-
-New server option
-~~~~~~~~~~~~~~~~~
- --binlog-annotate-rows-events
-
-Setting this option makes RBR (rows-) events in the binary log to be
-preceded by Annotate rows events (see below). The corresponding
-'binlog_annotate_rows_events' system variable is dynamic and has both
-global and session values. Default global value is OFF.
-
-Note. Session values are usefull to make it possible to annotate only
- some selected statements:
+Content
+~~~~~~~
+ 1. Annotate_rows_log_event
+ 2. Server option: --binlog-annotate-rows-events
+ 3. Server option: --replicate-annotate-rows-events
+ 4. mysqlbinlog option: --print-annotate-rows-events
+ 5. mysqlbinlog output
+
+1. Annotate_rows_log_event [ ANNOTATE_ROWS_EVENT ]
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+Describes the query which caused the corresponding rows events. Has empty
+post-header and contains the query text in its data part. Example:
+
+ ************************
+ ANNOTATE_RBR_EVENT
+ ************************
+ 00000220 | B6 A0 2C 4B | time_when = 1261215926
+ 00000224 | 33 | event_type = 51
+ 00000225 | 64 00 00 00 | server_id = 100
+ 00000229 | 36 00 00 00 | event_len = 54
+ 0000022D | 56 02 00 00 | log_pos = 00000256
+ 00000231 | 00 00 | flags = <none>
+ ------------------------
+ 00000233 | 49 4E 53 45 | query = "INSERT INTO t1 VALUES (1), (2), (3)"
+ 00000237 | 52 54 20 49 |
+ 0000023B | 4E 54 4F 20 |
+ 0000023F | 74 31 20 56 |
+ 00000243 | 41 4C 55 45 |
+ 00000247 | 53 20 28 31 |
+ 0000024B | 29 2C 20 28 |
+ 0000024F | 32 29 2C 20 |
+ 00000253 | 28 33 29 |
+ ************************
+
+In binary log, Annotate_rows event follows the (possible) 'BEGIN' Query event
+and precedes the first of Table map events which accompany the corresponding
+rows events. (See example in the "mysqlbinlog output" section below.)
+
+2. Server option: --binlog-annotate-rows-events
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+Tells the master to write Annotate_rows events to the binary log.
+
+ * Variable Name: binlog_annotate_rows_events
+ * Scope: Global & Session
+ * Access Type: Dynamic
+ * Data Type: bool
+ * Default Value: OFF
+NOTE. Session values allows to annotate only some selected statements:
...
SET SESSION binlog_annotate_rows_events=ON;
... statements to be annotated ...
SET SESSION binlog_annotate_rows_events=OFF;
... statements not to be annotated ...
-New binlog event type
-~~~~~~~~~~~~~~~~~~~~~
- Annotate_rows_log_event [ ANNOTATE_ROWS_EVENT ]
-
-Describes the query which caused the corresponding rows event. In binary log,
-precedes each Table_map_log_event. Contains empty post-header and the query
-text in its data part.
-
-The numeric code for this event must be assigned carefully. It should be
-coordinated with MySQL/Sun, otherwise we can get into a situation where MySQL
-uses the same numeric code for one event that MariaDB uses for
-ANNOTATE_ROWS_EVENT, which would make merging the two impossible.
-
-Example:
-
- ...
- ************************
- ANNOTATE_ROWS_EVENT [51]
- ************************
- 000000C7 | 54 1B 12 4B | time_when = 1259477844
- 000000CB | 33 | event_type = 51
- 000000CC | 64 00 00 00 | server_id = 100
- 000000D0 | 2C 00 00 00 | event_len = 44
- 000000D4 | F3 00 00 00 | log_pos = 000000F3
- 000000D8 | 00 00 | flags = <none>
- ------------------------
- 000000DA | 69 6E 73 65 | query = "insert into t1 values (1)"
- 000000DE | 72 74 20 69 |
- 000000E2 | 6E 74 6F 20 |
- 000000E6 | 74 31 20 76 |
- 000000EA | 61 6C 75 65 |
- 000000EE | 73 20 28 31 |
- 000000F2 | 29 |
- ************************
- TABLE_MAP_EVENT [19]
- ************************
- 000000F3 | 54 1B 12 4B | time_when = 1259477844
- 000000F7 | 13 | event_type = 19
- 000000F8 | 64 00 00 00 | server_id = 100
- 000000FC | 29 00 00 00 | event_len = 41
- 00000100 | 1C 01 00 00 | log_pos = 0000011C
- 00000104 | 00 00 | flags = <none>
- ------------------------
- ...
- ************************
- WRITE_ROWS_EVENT [23]
- ************************
- 0000011C | 54 1B 12 4B | time_when = 1259477844
- 00000120 | 17 | event_type = 23
- 00000121 | 64 00 00 00 | server_id = 100
- 00000125 | 22 00 00 00 | event_len = 34
- 00000129 | 3E 01 00 00 | log_pos = 0000013E
- 0000012D | 10 00 | flags = LOG_EVENT_UPDATE_TABLE_MAP_VERSION_F
- ------------------------
- 0000012F | 0F 00 00 00 | table_id = 15
- ...
-
-New mysqlbinlog option
-~~~~~~~~~~~~~~~~~~~~~~
- --print-annotate-rows-events
-
-With this option, mysqlbinlog prints the content of Annotate-rows
-events (if the binary log does contain them). Without this option
-(i.e. by default), mysqlbinlog skips Annotate rows events.
-
-
-mysqlbinlog output
-~~~~~~~~~~~~~~~~~~
-Something like this:
+3. Server option: --replicate-annotate-rows-events
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+Tells the slave to reproduce Annotate_rows events recieved from the master
+in its own binary log (sensible only in pair with log-slave-updates option).
+
+ * Variable Name: replicate_annotate_rows_events
+ * Scope: Global
+ * Access Type: Read only
+ * Data Type: bool
+ * Default Value: OFF
+
+NOTE. Why do we additionally need this 'replicate' option? Why not to make
+the slave to reproduce this events when its binlog-annotate-rows-events
+global value is ON? Well, because, for example, we may want to configure
+the slave which should reproduce Annotate_rows events but has global
+binlog-annotate-rows-events = OFF meaning this to be the default value for
+the client threads (see also "How slave treats replicate-annotate-rows-events
+option" in LLD part).
+
+4. mysqlbinlog option: --print-annotate-rows-events
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+With this option, mysqlbinlog prints the content of Annotate_rows events (if
+the binary log does contain them). Without this option (i.e. by default),
+mysqlbinlog skips Annotate_rows events.
+5. mysqlbinlog output
+~~~~~~~~~~~~~~~~~~~~~
+With --print-annotate-rows-events, mysqlbinlog outputs Annotate_rows events
+in a form like this:
...
- # at 199
- # at 243
- # at 284
- #091129 9:57:24 server id 100 end_log_pos 243 Query: `insert into t1 values
-(1)`
- #091129 9:57:24 server id 100 end_log_pos 284 Table_map: `test`.`t1` mapped
-to number 15
- #091129 9:57:24 server id 100 end_log_pos 318 Write_rows: table id 15
+ # at 1646
+ #091219 12:45:26 server id 100 end_log_pos 1714 Query thread_id=1
+exec_time=0 error_code=0
+ SET TIMESTAMP=1261215926/*!*/;
+ BEGIN
+ /*!*/;
+ # at 1714
+ # at 1812
+ # at 1853
+ # at 1894
+ # at 1938
+ #091219 12:45:26 server id 100 end_log_pos 1812 Query: `DELETE t1, t2 FROM
+t1 INNER JOIN t2 INNER JOIN t3 WHERE t1.a=t2.a AND t2.a=t3.a`
+ #091219 12:45:26 server id 100 end_log_pos 1853 Table_map: `test`.`t1`
+mapped to number 16
+ #091219 12:45:26 server id 100 end_log_pos 1894 Table_map: `test`.`t2`
+mapped to number 17
+ #091219 12:45:26 server id 100 end_log_pos 1938 Delete_rows: table id 16
+ #091219 12:45:26 server id 100 end_log_pos 1982 Delete_rows: table id 17
flags: STMT_END_F
-
- BINLOG '
- VBsSSzNkAAAALAAAAPMAAAAAAGluc2VydCBpbnRvIHQxIHZhbHVlcyAoMSk=
- VBsSSxNkAAAAKQAAABwBAAAAAA8AAAAAAAAABHRlc3QAAnQxAAEDAAE=
- VBsSSxdkAAAAIgAAAD4BAAAQAA8AAAAAAAEAAf/+AQAAAA==
- '/*!*/;
- ### INSERT INTO test.t1
- ### SET
- ### @1=1 /* INT meta=0 nullable=1 is_null=0 */
...
-When master sends Annotate rows events
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-1. Master always sends Annotate_rows events to mysqlbinlog (in
- remote case).
-2. Master sends Annotate_rows events to a slave only if the slave has
- both log-slave-updates and binlog-annotate-rows-events options set.
-
------------------------------------------------------------
-=-=(View All Progress Notes, 22 total)=-=-
http://askmonty.org/worklog/index.pl?tid=47&nolimit=1
DESCRIPTION:
Store in binlog (and show in mysqlbinlog output) texts of statements that
caused RBR events
This is needed for (list from Monty):
- Easier to understand why updates happened
- Would make it easier to find out where in application things went
wrong (as you can search for exact strings)
- Allow one to filter things based on comments in the statement.
The cost of this can be that the binlog will be approximately 2x in size
(especially insert of big blob's would be a bit painful), so this should
be an optional feature.
HIGH-LEVEL SPECIFICATION:
Content
~~~~~~~
1. Annotate_rows_log_event
2. Server option: --binlog-annotate-rows-events
3. Server option: --replicate-annotate-rows-events
4. mysqlbinlog option: --print-annotate-rows-events
5. mysqlbinlog output
1. Annotate_rows_log_event [ ANNOTATE_ROWS_EVENT ]
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Describes the query which caused the corresponding rows events. Has empty
post-header and contains the query text in its data part. Example:
************************
ANNOTATE_ROWS_EVENT
************************
00000220 | B6 A0 2C 4B | time_when = 1261215926
00000224 | 33 | event_type = 51
00000225 | 64 00 00 00 | server_id = 100
00000229 | 36 00 00 00 | event_len = 54
0000022D | 56 02 00 00 | log_pos = 00000256
00000231 | 00 00 | flags = <none>
------------------------
00000233 | 49 4E 53 45 | query = "INSERT INTO t1 VALUES (1), (2), (3)"
00000237 | 52 54 20 49 |
0000023B | 4E 54 4F 20 |
0000023F | 74 31 20 56 |
00000243 | 41 4C 55 45 |
00000247 | 53 20 28 31 |
0000024B | 29 2C 20 28 |
0000024F | 32 29 2C 20 |
00000253 | 28 33 29 |
************************
In binary log, Annotate_rows event follows the (possible) 'BEGIN' Query event
and precedes the first of Table map events which accompany the corresponding
rows events. (See example in the "mysqlbinlog output" section below.)
2. Server option: --binlog-annotate-rows-events
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Tells the master to write Annotate_rows events to the binary log.
* Variable Name: binlog_annotate_rows_events
* Scope: Global & Session
* Access Type: Dynamic
* Data Type: bool
* Default Value: OFF
NOTE. Session values allows to annotate only some selected statements:
...
SET SESSION binlog_annotate_rows_events=ON;
... statements to be annotated ...
SET SESSION binlog_annotate_rows_events=OFF;
... statements not to be annotated ...
3. Server option: --replicate-annotate-rows-events
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Tells the slave to reproduce Annotate_rows events recieved from the master
in its own binary log (sensible only in pair with log-slave-updates option).
* Variable Name: replicate_annotate_rows_events
* Scope: Global
* Access Type: Read only
* Data Type: bool
* Default Value: OFF
NOTE. Why do we additionally need this 'replicate' option? Why not to make
the slave to reproduce this events when its binlog-annotate-rows-events
global value is ON? Well, because, for example, we may want to configure
the slave which should reproduce Annotate_rows events but has global
binlog-annotate-rows-events = OFF meaning this to be the default value for
the client threads (see also "How slave treats replicate-annotate-rows-events
option" in LLD part).
4. mysqlbinlog option: --print-annotate-rows-events
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
With this option, mysqlbinlog prints the content of Annotate_rows events (if
the binary log does contain them). Without this option (i.e. by default),
mysqlbinlog skips Annotate_rows events.
5. mysqlbinlog output
~~~~~~~~~~~~~~~~~~~~~
With --print-annotate-rows-events, mysqlbinlog outputs Annotate_rows events
in a form like this:
...
# at 1646
#091219 12:45:26 server id 100 end_log_pos 1714 Query thread_id=1
exec_time=0 error_code=0
SET TIMESTAMP=1261215926/*!*/;
BEGIN
/*!*/;
# at 1714
# at 1812
# at 1853
# at 1894
# at 1938
#091219 12:45:26 server id 100 end_log_pos 1812 Query: `DELETE t1, t2 FROM
t1 INNER JOIN t2 INNER JOIN t3 WHERE t1.a=t2.a AND t2.a=t3.a`
#091219 12:45:26 server id 100 end_log_pos 1853 Table_map: `test`.`t1`
mapped to number 16
#091219 12:45:26 server id 100 end_log_pos 1894 Table_map: `test`.`t2`
mapped to number 17
#091219 12:45:26 server id 100 end_log_pos 1938 Delete_rows: table id 16
#091219 12:45:26 server id 100 end_log_pos 1982 Delete_rows: table id 17
flags: STMT_END_F
...
LOW-LEVEL DESIGN:
Content
~~~~~~~
1. Annotate_rows event number
2. Outline of Annotate_rows event behavior
3. How Master writes Annotate_rows events to the binary log
4. How slave treats replicate-annotate-rows-events option
5. How slave IO thread requests Annotate_rows events
6. How master executes the request
7. How slave SQL thread processes Annotate_rows events
8. General remarks
1. Annotate_rows event number
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
To avoid possible event numbers conflict with MySQL/Sun, we leave a gap
between the last MySQL event number and the Annotate_rows event number:
enum Log_event_type
{ ...
INCIDENT_EVENT= 26,
// New MySQL event numbers are to be added here
MYSQL_EVENTS_END,
MARIA_EVENTS_BEGIN= 51,
// New Maria event numbers start from here
ANNOTATE_ROWS_EVENT= 51,
ENUM_END_EVENT
};
together with the corresponding extension of 'post_header_len' array in the
Format description event. (This extension does not affect the compatibility
of the binary log). Here is how Format description event looks like with
this extension:
************************
FORMAT_DESCRIPTION_EVENT
************************
00000004 | A1 A0 2C 4B | time_when = 1261215905
00000008 | 0F | event_type = 15
00000009 | 64 00 00 00 | server_id = 100
0000000D | 7F 00 00 00 | event_len = 127
00000011 | 83 00 00 00 | log_pos = 00000083
00000015 | 01 00 | flags = LOG_EVENT_BINLOG_IN_USE_F
------------------------
00000017 | 04 00 | binlog_ver = 4
00000019 | 35 2E 32 2E | server_ver = 5.2.0-MariaDB-alpha-debug-log
..... ...
0000004B | A1 A0 2C 4B | time_created = 1261215905
0000004F | 13 | common_header_len = 19
------------------------
post_header_len
------------------------
00000050 | 38 | 56 - START_EVENT_V3 [1]
..... ...
00000069 | 02 | 2 - INCIDENT_EVENT [26]
0000006A | 00 | 0 - RESERVED [27]
..... ...
00000081 | 00 | 0 - RESERVED [50]
00000082 | 00 | 0 - ANNOTATE_ROWS_EVENT [51]
************************
2. Outline of Annotate_rows event behavior
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Each Annotate_rows_log_event object has two private members describing the
corresponding query:
char *m_query_txt;
uint m_query_len;
When the object is created for writing to a binary log, this query is taken
from 'thd' (for short, below we omit the 'Annotate_rows_log_event::' prefix
as well as other implementation details):
Annotate_rows_log_event(THD *thd)
{
m_query_txt = thd->query();
m_query_len = thd->query_length();
}
When the object is read from a binary log, the query is taken from the buffer
containing the binary log representation of the event (this buffer is allocated
in Log_event object from which all Log events are derived):
Annotate_rows_log_event(char *buf, uint event_len,
Format_description_log_event *desc)
{
m_query_len = event_len - desc->common_header_len;
m_query_txt = buf + desc->common_header_len;
}
The events are written to the binary log by the Log_event::write() member
which calls virtual write_data_header() and write_data_body() members
("data header" and "post header" are synonym in replication terminology).
In our case, data header is empty and data body is just the query:
bool write_data_body(IO_CACHE *file)
{
return my_b_safe_write(file, (uchar*) m_query_txt, m_query_len);
}
Printing the event is just printing the query:
void Annotate_rows_log_event::print(FILE *file, PRINT_EVENT_INFO *pinfo)
{
my_b_printf(&pinfo->head_cache, "\tQuery: `%s`\n", m_query_txt);
}
3. How Master writes Annotate_rows events to the binary log
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
The event is written to the binary log just before the group of Table_map
events which precede corresponding Rows events (one query may generate
several Table map events in the binary log, but the corresponding
Annotate_rows event must be written only once before the first Table map
event; hence the boolean variable 'with_annotate' below):
int write_locked_table_maps(THD *thd)
{ ...
bool with_annotate= thd->variables.binlog_annotate_rows_events;
...
for (uint i= 0; i < ... <number of tables> ...; ++i)
{ ...
thd->binlog_write_table_map(table, ..., with_annotate);
with_annotate= 0; // write Annotate_event not more than once
...
}
...
}
int THD::binlog_write_table_map(TABLE *table, ..., bool with_annotate)
{ ...
Table_map_log_event the_event(...);
...
if (with_annotate)
{
Annotate_rows_log_event anno(this);
mysql_bin_log.write(&anno);
}
mysql_bin_log.write(&the_event);
...
}
4. How slave treats replicate-annotate-rows-events option
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
The replicate-annotate-rows-events option is treated just as the session
value of the binlog_annotate_rows_events variable for the slave IO and
SQL threads. This setting is done during initialization of these threads:
pthread_handler_t handle_slave_io(void *arg)
{
THD *thd= new THD;
...
init_slave_thread(thd, SLAVE_THD_IO);
...
}
pthread_handler_t handle_slave_sql(void *arg)
{
THD *thd= new THD;
...
init_slave_thread(thd, SLAVE_THD_SQL);
...
}
int init_slave_thread(THD* thd, SLAVE_THD_TYPE thd_type)
{ ...
thd->variables.binlog_annotate_rows_events=
opt_replicate_annotate_rows_events;
...
}
5. How slave IO thread requests Annotate_rows events
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
If the replicate-annotate-rows-events option is not set on a slave, there
is no need for master to send Annotate_rows events to this slave. The slave
(or mysqlbinlog in remote case), before requesting binlog dump via the
COM_BINLOG_DUMP command, informs the master whether it should send these
events by executing the newly added COM_BINLOG_DUMP_OPTIONS_EXT server
command:
case COM_BINLOG_DUMP_OPTIONS_EXT:
thd->binlog_dump_flags_ext= packet[0];
my_ok(thd);
break;
Note. We add this new command and don't use COM_BINLOG_DUMP to avoid possible
conflicts with MySQL/Sun.
6. How master executes the request
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
case COM_BINLOG_DUMP:
{ ...
flags= uint2korr(packet + 4);
...
mysql_binlog_send(thd, ..., flags);
...
}
void mysql_binlog_send(THD* thd, ..., ushort flags)
{ ...
Log_event::read_log_event(&log, packet, ...);
...
if ((*packet)[EVENT_TYPE_OFFSET + 1] != ANNOTATE_ROWS_EVENT ||
flags & BINLOG_SEND_ANNOTATE_ROWS_EVENT)
{
my_net_write(net, packet->ptr(), packet->length());
}
...
}
7. How slave SQL thread processes Annotate_rows events
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
The slave processes each recieved event by "applying" it, i.e. by
calling the Log_event::apply_event() function which in turn calls
the virtual do_apply_event() member specific for each type of the
event.
int exec_relay_log_event(THD* thd, Relay_log_info* rli)
{ ...
Log_event *ev = next_event(rli);
...
apply_event_and_update_pos(ev, ...);
if (ev->get_type_code() != FORMAT_DESCRIPTION_EVENT)
delete ev;
...
}
int apply_event_and_update_pos(Log_event *ev, ...)
{ ...
ev->apply_event(...);
...
}
int Log_event::apply_event(...)
{
return do_apply_event(...);
}
What does it mean to "apply" an Annotate_rows event? It means to set current
thd query to that of the described by the event, i.e. to the query which
caused the subsequent Rows events (see "How Master writes Annotate_rows
events to the binary log" to follow what happens further when the subsequent
Rows events are applied):
int Annotate_rows_log_event::do_apply_event(...)
{
thd->set_query(m_query_txt, m_query_len);
}
NOTE. I am not sure, but possibly current values of thd->query and
thd->query_length should be saved before calling set_query() and to be
restored on the Annotate_rows_log_event object deletion.
Is it really needed ?
After calling this do_apply_event() function we may not delete the
Annotate_rows_log_event object immediatedly (see exec_relay_log_event()
above) because thd->query now points to the string inside this object.
We may keep the pointer to this object in the Relay_log_info:
class Relay_log_info
{
public:
...
void set_annotate_event(Annotate_rows_log_event*);
Annotate_rows_log_event* get_annotate_event();
void free_annotate_event();
...
private:
Annotate_rows_log_event* m_annotate_event;
};
The saved Annotate_rows object should be deleted when all corresponding
Rows events will be processed:
int exec_relay_log_event(THD* thd, Relay_log_info* rli)
{ ...
Log_event *ev= next_event(rli);
...
apply_event_and_update_pos(ev, ...);
if (rli->get_annotate_event() && is_last_rows_event(ev))
rli->free_annotate_event();
else if (ev->get_type_code() == ANNOTATE_ROWS_EVENT)
rli->set_annotate_event((Annotate_rows_log_event*) ev);
else if (ev->get_type_code() != FORMAT_DESCRIPTION_EVENT)
delete ev;
...
}
where
bool is_last_rows_event(Log_event* ev)
{
Log_event_type type= ev->get_type_code();
if (IS_ROWS_EVENT_TYPE(type))
{
Rows_log_event* rows= (Rows_log_event*)ev;
return rows->get_flags(Rows_log_event::STMT_END_F);
}
return 0;
}
#define IS_ROWS_EVENT_TYPE(type) ((type) == WRITE_ROWS_EVENT || \
(type) == UPDATE_ROWS_EVENT || \
(type) == DELETE_ROWS_EVENT)
8. General remarks
~~~~~~~~~~~~~~~~~~
Kristian noticed that introducing new log event type should be coordinated
somehow with MySQL/Sun:
Kristian: The numeric code for this event must be assigned carefully.
It should be coordinated with MySQL/Sun, otherwise we can get into a
situation where MySQL uses the same numeric code for one event that
MariaDB uses for ANNOTATE_ROWS_EVENT, which would make merging the two
impossible.
Alex: I reserved about 20 numbers not to have possible conflicts
with MySQL.
Kristian: Still, I think it would be appropriate to send a polite email
to internals(a)lists.mysql.com about this and suggesting to reserve the
event number.
ESTIMATED WORK TIME
ESTIMATED COMPLETION DATE
-----------------------------------------------------------------------
WorkLog (v3.5.9)
1
0
[Maria-developers] Updated (by Guest): Store in binlog text of statements that caused RBR events (47)
by worklog-noreply@askmonty.org 05 Feb '10
by worklog-noreply@askmonty.org 05 Feb '10
05 Feb '10
-----------------------------------------------------------------------
WORKLOG TASK
-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
TASK...........: Store in binlog text of statements that caused RBR events
CREATION DATE..: Sat, 15 Aug 2009, 23:48
SUPERVISOR.....: Monty
IMPLEMENTOR....:
COPIES TO......: Knielsen
CATEGORY.......: Server-Sprint
TASK ID........: 47 (http://askmonty.org/worklog/?tid=47)
VERSION........: Server-9.x
STATUS.........: In-Progress
PRIORITY.......: 60
WORKED HOURS...: 20
ESTIMATE.......: 35 (hours remain)
ORIG. ESTIMATE.: 35
PROGRESS NOTES:
-=-=(Guest - Fri, 05 Feb 2010, 13:40)=-=-
Category updated.
--- /tmp/wklog.47.old.9197 2010-02-05 13:40:36.000000000 +0200
+++ /tmp/wklog.47.new.9197 2010-02-05 13:40:36.000000000 +0200
@@ -1 +1 @@
-Server-RawIdeaBin
+Server-Sprint
-=-=(Guest - Fri, 05 Feb 2010, 13:40)=-=-
Status updated.
--- /tmp/wklog.47.old.9197 2010-02-05 13:40:36.000000000 +0200
+++ /tmp/wklog.47.new.9197 2010-02-05 13:40:36.000000000 +0200
@@ -1 +1 @@
-Un-Assigned
+In-Progress
-=-=(Alexi - Thu, 04 Feb 2010, 09:54)=-=-
Low Level Design modified.
--- /tmp/wklog.47.old.16174 2010-02-04 09:54:13.000000000 +0200
+++ /tmp/wklog.47.new.16174 2010-02-04 09:54:13.000000000 +0200
@@ -171,35 +171,20 @@
5. How slave IO thread requests Annotate_rows events
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-When requesting an event, the slave should inform the master whether
-it should send Annotate_rows events or not. To that end we add a new
-BINLOG_SEND_ANNOTATE_ROWS_EVENT flag used when requesting an event:
+If the replicate-annotate-rows-events option is not set on a slave, there
+is no need for master to send Annotate_rows events to this slave. The slave
+(or mysqlbinlog in remote case), before requesting binlog dump via the
+COM_BINLOG_DUMP command, informs the master whether it should send these
+events by executing the newly added COM_BINLOG_DUMP_OPTIONS_EXT server
+command:
+
+ case COM_BINLOG_DUMP_OPTIONS_EXT:
+ thd->binlog_dump_flags_ext= packet[0];
+ my_ok(thd);
+ break;
- #define BINLOG_DUMP_NON_BLOCK 1
- #define BINLOG_SEND_ANNOTATE_ROWS_EVENT 2
-
- pthread_handler_t handle_slave_io(void *arg)
- { ...
- request_dump(mysql, ...);
- ...
- }
-
- int request_dump(MYSQL* mysql, ...)
- { ...
- if (opt_log_slave_updates &&
- mi->io_thd->variables.binlog_annotate_rows_events)
- binlog_flags|= BINLOG_SEND_ANNOTATE_ROWS_EVENT;
- ...
- int2store(buf + 4, binlog_flags);
- ...
- simple_command(mysql, COM_BINLOG_DUMP, buf, ...);
- ...
- }
-
-NOTE. mysqlbinlog, when remotely requesting BINLOG_DUMP by calling the
-simple_command() function, should also use this flag if it wants (in case
-of the --print-annotate-rows-events option set) to recieve Annotate_rows
-events.
+Note. We add this new command and don't use COM_BINLOG_DUMP to avoid possible
+conflicts with MySQL/Sun.
6. How master executes the request
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -338,10 +323,4 @@
to internals(a)lists.mysql.com about this and suggesting to reserve the
event number.
-Also we should notice the introduction of the BINLOG_SEND_ANNOTATE_ROWS_EVENT
-flag taking into account that MySQL/Sun may also introduce a flag with the
-same value to be used in the request_dump-mysql_binlog_send interface.
-But this is mainly the question of merging: if a conflict concerning this
-flag occur, we may simply change the BINLOG_SEND_ANNOTATE_ROWS_EVENT value
-(this does not require additional changes in the code).
-=-=(Alexi - Sun, 20 Dec 2009, 16:00)=-=-
Low Level Design modified.
--- /tmp/wklog.47.old.19667 2009-12-20 14:00:56.000000000 +0000
+++ /tmp/wklog.47.new.19667 2009-12-20 14:00:56.000000000 +0000
@@ -196,6 +196,11 @@
...
}
+NOTE. mysqlbinlog, when remotely requesting BINLOG_DUMP by calling the
+simple_command() function, should also use this flag if it wants (in case
+of the --print-annotate-rows-events option set) to recieve Annotate_rows
+events.
+
6. How master executes the request
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -212,8 +217,7 @@
Log_event::read_log_event(&log, packet, ...);
...
if ((*packet)[EVENT_TYPE_OFFSET + 1] != ANNOTATE_ROWS_EVENT ||
- flags & BINLOG_SEND_ANNOTATE_ROWS_EVENT ||
- thd->server_id == 0 /* slave == mysqlbinlog */ )
+ flags & BINLOG_SEND_ANNOTATE_ROWS_EVENT)
{
my_net_write(net, packet->ptr(), packet->length());
}
-=-=(Alexi - Sun, 20 Dec 2009, 13:14)=-=-
Low Level Design modified.
--- /tmp/wklog.47.old.11350 2009-12-20 13:14:04.000000000 +0200
+++ /tmp/wklog.47.new.11350 2009-12-20 13:14:04.000000000 +0200
@@ -282,23 +282,18 @@
Annotate_rows_log_event* m_annotate_event;
};
-When the saved Annotate_rows object may be deleted? When all corresponding
-Rows events will be processed, i.e. before processing the first non-Rows
-event (note that Annotate_rows object resides in the binary log *after*
-the (possible) 'BEGIN' Query event which accompanies the rows events; note
-also that this deletion is adjusted with the case when some or all
-corresponding Rows events are filtered out by replicate filter rules):
+The saved Annotate_rows object should be deleted when all corresponding
+Rows events will be processed:
int exec_relay_log_event(THD* thd, Relay_log_info* rli)
{ ...
Log_event *ev= next_event(rli);
...
- if (rli->get_annotate_event() && !IS_RBR_EVENT_TYPE(ev->get_type_code()))
- rli->free_annotate_event();
-
apply_event_and_update_pos(ev, ...);
- if (ev->get_type_code() == ANNOTATE_ROWS_EVENT)
+ if (rli->get_annotate_event() && is_last_rows_event(ev))
+ rli->free_annotate_event();
+ else if (ev->get_type_code() == ANNOTATE_ROWS_EVENT)
rli->set_annotate_event((Annotate_rows_log_event*) ev);
else if (ev->get_type_code() != FORMAT_DESCRIPTION_EVENT)
delete ev;
@@ -307,10 +302,21 @@
where
- #define IS_RBR_EVENT_TYPE(type) ( (type) == TABLE_MAP_EVENT || \
- (type) == WRITE_ROWS_EVENT || \
+ bool is_last_rows_event(Log_event* ev)
+ {
+ Log_event_type type= ev->get_type_code();
+ if (IS_ROWS_EVENT_TYPE(type))
+ {
+ Rows_log_event* rows= (Rows_log_event*)ev;
+ return rows->get_flags(Rows_log_event::STMT_END_F);
+ }
+
+ return 0;
+ }
+
+ #define IS_ROWS_EVENT_TYPE(type) ((type) == WRITE_ROWS_EVENT || \
(type) == UPDATE_ROWS_EVENT || \
- (type) == DELETE_ROWS_EVENT )
+ (type) == DELETE_ROWS_EVENT)
8. General remarks
~~~~~~~~~~~~~~~~~~
-=-=(Alexi - Sun, 20 Dec 2009, 09:29)=-=-
Low Level Design modified.
--- /tmp/wklog.47.old.32726 2009-12-20 07:29:56.000000000 +0000
+++ /tmp/wklog.47.new.32726 2009-12-20 07:29:56.000000000 +0000
@@ -56,7 +56,7 @@
0000006A | 00 | 0 - RESERVED [27]
..... ...
00000081 | 00 | 0 - RESERVED [50]
- 00000082 | 00 | 0 - ANNOTATE_RBR_EVENT [51]
+ 00000082 | 00 | 0 - ANNOTATE_ROWS_EVENT [51]
************************
2. Outline of Annotate_rows event behavior
-=-=(Alexi - Sat, 19 Dec 2009, 16:10)=-=-
Low Level Design modified.
--- /tmp/wklog.47.old.16051 2009-12-19 16:10:48.000000000 +0200
+++ /tmp/wklog.47.new.16051 2009-12-19 16:10:48.000000000 +0200
@@ -253,7 +253,7 @@
thd query to that of the described by the event, i.e. to the query which
caused the subsequent Rows events (see "How Master writes Annotate_rows
events to the binary log" to follow what happens further when the subsequent
-Rows events is applied):
+Rows events are applied):
int Annotate_rows_log_event::do_apply_event(...)
{
-=-=(Alexi - Sat, 19 Dec 2009, 16:02)=-=-
High-Level Specification modified.
--- /tmp/wklog.47.old.15695 2009-12-19 16:02:33.000000000 +0200
+++ /tmp/wklog.47.new.15695 2009-12-19 16:02:33.000000000 +0200
@@ -12,7 +12,7 @@
post-header and contains the query text in its data part. Example:
************************
- ANNOTATE_RBR_EVENT
+ ANNOTATE_ROWS_EVENT
************************
00000220 | B6 A0 2C 4B | time_when = 1261215926
00000224 | 33 | event_type = 51
-=-=(Alexi - Sat, 19 Dec 2009, 15:58)=-=-
Low Level Design modified.
--- /tmp/wklog.47.old.15437 2009-12-19 13:58:12.000000000 +0000
+++ /tmp/wklog.47.new.15437 2009-12-19 13:58:12.000000000 +0000
@@ -1 +1,337 @@
+Content
+~~~~~~~
+ 1. Annotate_rows event number
+ 2. Outline of Annotate_rows event behavior
+ 3. How Master writes Annotate_rows events to the binary log
+ 4. How slave treats replicate-annotate-rows-events option
+ 5. How slave IO thread requests Annotate_rows events
+ 6. How master executes the request
+ 7. How slave SQL thread processes Annotate_rows events
+ 8. General remarks
+
+1. Annotate_rows event number
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+To avoid possible event numbers conflict with MySQL/Sun, we leave a gap
+between the last MySQL event number and the Annotate_rows event number:
+
+ enum Log_event_type
+ { ...
+ INCIDENT_EVENT= 26,
+ // New MySQL event numbers are to be added here
+ MYSQL_EVENTS_END,
+
+ MARIA_EVENTS_BEGIN= 51,
+ // New Maria event numbers start from here
+ ANNOTATE_ROWS_EVENT= 51,
+
+ ENUM_END_EVENT
+ };
+
+together with the corresponding extension of 'post_header_len' array in the
+Format description event. (This extension does not affect the compatibility
+of the binary log). Here is how Format description event looks like with
+this extension:
+
+ ************************
+ FORMAT_DESCRIPTION_EVENT
+ ************************
+ 00000004 | A1 A0 2C 4B | time_when = 1261215905
+ 00000008 | 0F | event_type = 15
+ 00000009 | 64 00 00 00 | server_id = 100
+ 0000000D | 7F 00 00 00 | event_len = 127
+ 00000011 | 83 00 00 00 | log_pos = 00000083
+ 00000015 | 01 00 | flags = LOG_EVENT_BINLOG_IN_USE_F
+ ------------------------
+ 00000017 | 04 00 | binlog_ver = 4
+ 00000019 | 35 2E 32 2E | server_ver = 5.2.0-MariaDB-alpha-debug-log
+ ..... ...
+ 0000004B | A1 A0 2C 4B | time_created = 1261215905
+ 0000004F | 13 | common_header_len = 19
+ ------------------------
+ post_header_len
+ ------------------------
+ 00000050 | 38 | 56 - START_EVENT_V3 [1]
+ ..... ...
+ 00000069 | 02 | 2 - INCIDENT_EVENT [26]
+ 0000006A | 00 | 0 - RESERVED [27]
+ ..... ...
+ 00000081 | 00 | 0 - RESERVED [50]
+ 00000082 | 00 | 0 - ANNOTATE_RBR_EVENT [51]
+ ************************
+
+2. Outline of Annotate_rows event behavior
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+Each Annotate_rows_log_event object has two private members describing the
+corresponding query:
+
+ char *m_query_txt;
+ uint m_query_len;
+
+When the object is created for writing to a binary log, this query is taken
+from 'thd' (for short, below we omit the 'Annotate_rows_log_event::' prefix
+as well as other implementation details):
+
+ Annotate_rows_log_event(THD *thd)
+ {
+ m_query_txt = thd->query();
+ m_query_len = thd->query_length();
+ }
+
+When the object is read from a binary log, the query is taken from the buffer
+containing the binary log representation of the event (this buffer is allocated
+in Log_event object from which all Log events are derived):
+
+ Annotate_rows_log_event(char *buf, uint event_len,
+ Format_description_log_event *desc)
+ {
+ m_query_len = event_len - desc->common_header_len;
+ m_query_txt = buf + desc->common_header_len;
+ }
+
+The events are written to the binary log by the Log_event::write() member
+which calls virtual write_data_header() and write_data_body() members
+("data header" and "post header" are synonym in replication terminology).
+In our case, data header is empty and data body is just the query:
+
+ bool write_data_body(IO_CACHE *file)
+ {
+ return my_b_safe_write(file, (uchar*) m_query_txt, m_query_len);
+ }
+
+Printing the event is just printing the query:
+
+ void Annotate_rows_log_event::print(FILE *file, PRINT_EVENT_INFO *pinfo)
+ {
+ my_b_printf(&pinfo->head_cache, "\tQuery: `%s`\n", m_query_txt);
+ }
+
+3. How Master writes Annotate_rows events to the binary log
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+The event is written to the binary log just before the group of Table_map
+events which precede corresponding Rows events (one query may generate
+several Table map events in the binary log, but the corresponding
+Annotate_rows event must be written only once before the first Table map
+event; hence the boolean variable 'with_annotate' below):
+
+ int write_locked_table_maps(THD *thd)
+ { ...
+ bool with_annotate= thd->variables.binlog_annotate_rows_events;
+ ...
+ for (uint i= 0; i < ... <number of tables> ...; ++i)
+ { ...
+ thd->binlog_write_table_map(table, ..., with_annotate);
+ with_annotate= 0; // write Annotate_event not more than once
+ ...
+ }
+ ...
+ }
+
+ int THD::binlog_write_table_map(TABLE *table, ..., bool with_annotate)
+ { ...
+ Table_map_log_event the_event(...);
+ ...
+ if (with_annotate)
+ {
+ Annotate_rows_log_event anno(this);
+ mysql_bin_log.write(&anno);
+ }
+
+ mysql_bin_log.write(&the_event);
+ ...
+ }
+
+4. How slave treats replicate-annotate-rows-events option
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+The replicate-annotate-rows-events option is treated just as the session
+value of the binlog_annotate_rows_events variable for the slave IO and
+SQL threads. This setting is done during initialization of these threads:
+
+ pthread_handler_t handle_slave_io(void *arg)
+ {
+ THD *thd= new THD;
+ ...
+ init_slave_thread(thd, SLAVE_THD_IO);
+ ...
+ }
+
+ pthread_handler_t handle_slave_sql(void *arg)
+ {
+ THD *thd= new THD;
+ ...
+ init_slave_thread(thd, SLAVE_THD_SQL);
+ ...
+ }
+
+ int init_slave_thread(THD* thd, SLAVE_THD_TYPE thd_type)
+ { ...
+ thd->variables.binlog_annotate_rows_events=
+ opt_replicate_annotate_rows_events;
+ ...
+ }
+
+5. How slave IO thread requests Annotate_rows events
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+When requesting an event, the slave should inform the master whether
+it should send Annotate_rows events or not. To that end we add a new
+BINLOG_SEND_ANNOTATE_ROWS_EVENT flag used when requesting an event:
+
+ #define BINLOG_DUMP_NON_BLOCK 1
+ #define BINLOG_SEND_ANNOTATE_ROWS_EVENT 2
+
+ pthread_handler_t handle_slave_io(void *arg)
+ { ...
+ request_dump(mysql, ...);
+ ...
+ }
+
+ int request_dump(MYSQL* mysql, ...)
+ { ...
+ if (opt_log_slave_updates &&
+ mi->io_thd->variables.binlog_annotate_rows_events)
+ binlog_flags|= BINLOG_SEND_ANNOTATE_ROWS_EVENT;
+ ...
+ int2store(buf + 4, binlog_flags);
+ ...
+ simple_command(mysql, COM_BINLOG_DUMP, buf, ...);
+ ...
+ }
+
+6. How master executes the request
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+ case COM_BINLOG_DUMP:
+ { ...
+ flags= uint2korr(packet + 4);
+ ...
+ mysql_binlog_send(thd, ..., flags);
+ ...
+ }
+
+ void mysql_binlog_send(THD* thd, ..., ushort flags)
+ { ...
+ Log_event::read_log_event(&log, packet, ...);
+ ...
+ if ((*packet)[EVENT_TYPE_OFFSET + 1] != ANNOTATE_ROWS_EVENT ||
+ flags & BINLOG_SEND_ANNOTATE_ROWS_EVENT ||
+ thd->server_id == 0 /* slave == mysqlbinlog */ )
+ {
+ my_net_write(net, packet->ptr(), packet->length());
+ }
+ ...
+ }
+
+7. How slave SQL thread processes Annotate_rows events
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+The slave processes each recieved event by "applying" it, i.e. by
+calling the Log_event::apply_event() function which in turn calls
+the virtual do_apply_event() member specific for each type of the
+event.
+
+ int exec_relay_log_event(THD* thd, Relay_log_info* rli)
+ { ...
+ Log_event *ev = next_event(rli);
+ ...
+ apply_event_and_update_pos(ev, ...);
+
+ if (ev->get_type_code() != FORMAT_DESCRIPTION_EVENT)
+ delete ev;
+ ...
+ }
+
+ int apply_event_and_update_pos(Log_event *ev, ...)
+ { ...
+ ev->apply_event(...);
+ ...
+ }
+
+ int Log_event::apply_event(...)
+ {
+ return do_apply_event(...);
+ }
+
+What does it mean to "apply" an Annotate_rows event? It means to set current
+thd query to that of the described by the event, i.e. to the query which
+caused the subsequent Rows events (see "How Master writes Annotate_rows
+events to the binary log" to follow what happens further when the subsequent
+Rows events is applied):
+
+ int Annotate_rows_log_event::do_apply_event(...)
+ {
+ thd->set_query(m_query_txt, m_query_len);
+ }
+
+NOTE. I am not sure, but possibly current values of thd->query and
+thd->query_length should be saved before calling set_query() and to be
+restored on the Annotate_rows_log_event object deletion.
+Is it really needed ?
+
+After calling this do_apply_event() function we may not delete the
+Annotate_rows_log_event object immediatedly (see exec_relay_log_event()
+above) because thd->query now points to the string inside this object.
+We may keep the pointer to this object in the Relay_log_info:
+
+ class Relay_log_info
+ {
+ public:
+ ...
+ void set_annotate_event(Annotate_rows_log_event*);
+ Annotate_rows_log_event* get_annotate_event();
+ void free_annotate_event();
+ ...
+ private:
+ Annotate_rows_log_event* m_annotate_event;
+ };
+
+When the saved Annotate_rows object may be deleted? When all corresponding
+Rows events will be processed, i.e. before processing the first non-Rows
+event (note that Annotate_rows object resides in the binary log *after*
+the (possible) 'BEGIN' Query event which accompanies the rows events; note
+also that this deletion is adjusted with the case when some or all
+corresponding Rows events are filtered out by replicate filter rules):
+
+ int exec_relay_log_event(THD* thd, Relay_log_info* rli)
+ { ...
+ Log_event *ev= next_event(rli);
+ ...
+ if (rli->get_annotate_event() && !IS_RBR_EVENT_TYPE(ev->get_type_code()))
+ rli->free_annotate_event();
+
+ apply_event_and_update_pos(ev, ...);
+
+ if (ev->get_type_code() == ANNOTATE_ROWS_EVENT)
+ rli->set_annotate_event((Annotate_rows_log_event*) ev);
+ else if (ev->get_type_code() != FORMAT_DESCRIPTION_EVENT)
+ delete ev;
+ ...
+ }
+
+where
+
+ #define IS_RBR_EVENT_TYPE(type) ( (type) == TABLE_MAP_EVENT || \
+ (type) == WRITE_ROWS_EVENT || \
+ (type) == UPDATE_ROWS_EVENT || \
+ (type) == DELETE_ROWS_EVENT )
+
+8. General remarks
+~~~~~~~~~~~~~~~~~~
+Kristian noticed that introducing new log event type should be coordinated
+somehow with MySQL/Sun:
+
+ Kristian: The numeric code for this event must be assigned carefully.
+ It should be coordinated with MySQL/Sun, otherwise we can get into a
+ situation where MySQL uses the same numeric code for one event that
+ MariaDB uses for ANNOTATE_ROWS_EVENT, which would make merging the two
+ impossible.
+ Alex: I reserved about 20 numbers not to have possible conflicts
+ with MySQL.
+ Kristian: Still, I think it would be appropriate to send a polite email
+ to internals(a)lists.mysql.com about this and suggesting to reserve the
+ event number.
+
+Also we should notice the introduction of the BINLOG_SEND_ANNOTATE_ROWS_EVENT
+flag taking into account that MySQL/Sun may also introduce a flag with the
+same value to be used in the request_dump-mysql_binlog_send interface.
+But this is mainly the question of merging: if a conflict concerning this
+flag occur, we may simply change the BINLOG_SEND_ANNOTATE_ROWS_EVENT value
+(this does not require additional changes in the code).
-=-=(Alexi - Sat, 19 Dec 2009, 15:41)=-=-
High-Level Specification modified.
--- /tmp/wklog.47.old.14545 2009-12-19 15:41:21.000000000 +0200
+++ /tmp/wklog.47.new.14545 2009-12-19 15:41:21.000000000 +0200
@@ -1,122 +1,107 @@
-First suggestion:
-
-> I think for this we would actually need a new binlog event type
-> (Comment_log_event?). Unless we want to log an empty statement Query_log_event
-> containing only a comment (a bit of a hack).
-
-New server option
-~~~~~~~~~~~~~~~~~
- --binlog-annotate-rows-events
-
-Setting this option makes RBR (rows-) events in the binary log to be
-preceded by Annotate rows events (see below). The corresponding
-'binlog_annotate_rows_events' system variable is dynamic and has both
-global and session values. Default global value is OFF.
-
-Note. Session values are usefull to make it possible to annotate only
- some selected statements:
+Content
+~~~~~~~
+ 1. Annotate_rows_log_event
+ 2. Server option: --binlog-annotate-rows-events
+ 3. Server option: --replicate-annotate-rows-events
+ 4. mysqlbinlog option: --print-annotate-rows-events
+ 5. mysqlbinlog output
+
+1. Annotate_rows_log_event [ ANNOTATE_ROWS_EVENT ]
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+Describes the query which caused the corresponding rows events. Has empty
+post-header and contains the query text in its data part. Example:
+
+ ************************
+ ANNOTATE_RBR_EVENT
+ ************************
+ 00000220 | B6 A0 2C 4B | time_when = 1261215926
+ 00000224 | 33 | event_type = 51
+ 00000225 | 64 00 00 00 | server_id = 100
+ 00000229 | 36 00 00 00 | event_len = 54
+ 0000022D | 56 02 00 00 | log_pos = 00000256
+ 00000231 | 00 00 | flags = <none>
+ ------------------------
+ 00000233 | 49 4E 53 45 | query = "INSERT INTO t1 VALUES (1), (2), (3)"
+ 00000237 | 52 54 20 49 |
+ 0000023B | 4E 54 4F 20 |
+ 0000023F | 74 31 20 56 |
+ 00000243 | 41 4C 55 45 |
+ 00000247 | 53 20 28 31 |
+ 0000024B | 29 2C 20 28 |
+ 0000024F | 32 29 2C 20 |
+ 00000253 | 28 33 29 |
+ ************************
+
+In binary log, Annotate_rows event follows the (possible) 'BEGIN' Query event
+and precedes the first of Table map events which accompany the corresponding
+rows events. (See example in the "mysqlbinlog output" section below.)
+
+2. Server option: --binlog-annotate-rows-events
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+Tells the master to write Annotate_rows events to the binary log.
+
+ * Variable Name: binlog_annotate_rows_events
+ * Scope: Global & Session
+ * Access Type: Dynamic
+ * Data Type: bool
+ * Default Value: OFF
+NOTE. Session values allows to annotate only some selected statements:
...
SET SESSION binlog_annotate_rows_events=ON;
... statements to be annotated ...
SET SESSION binlog_annotate_rows_events=OFF;
... statements not to be annotated ...
-New binlog event type
-~~~~~~~~~~~~~~~~~~~~~
- Annotate_rows_log_event [ ANNOTATE_ROWS_EVENT ]
-
-Describes the query which caused the corresponding rows event. In binary log,
-precedes each Table_map_log_event. Contains empty post-header and the query
-text in its data part.
-
-The numeric code for this event must be assigned carefully. It should be
-coordinated with MySQL/Sun, otherwise we can get into a situation where MySQL
-uses the same numeric code for one event that MariaDB uses for
-ANNOTATE_ROWS_EVENT, which would make merging the two impossible.
-
-Example:
-
- ...
- ************************
- ANNOTATE_ROWS_EVENT [51]
- ************************
- 000000C7 | 54 1B 12 4B | time_when = 1259477844
- 000000CB | 33 | event_type = 51
- 000000CC | 64 00 00 00 | server_id = 100
- 000000D0 | 2C 00 00 00 | event_len = 44
- 000000D4 | F3 00 00 00 | log_pos = 000000F3
- 000000D8 | 00 00 | flags = <none>
- ------------------------
- 000000DA | 69 6E 73 65 | query = "insert into t1 values (1)"
- 000000DE | 72 74 20 69 |
- 000000E2 | 6E 74 6F 20 |
- 000000E6 | 74 31 20 76 |
- 000000EA | 61 6C 75 65 |
- 000000EE | 73 20 28 31 |
- 000000F2 | 29 |
- ************************
- TABLE_MAP_EVENT [19]
- ************************
- 000000F3 | 54 1B 12 4B | time_when = 1259477844
- 000000F7 | 13 | event_type = 19
- 000000F8 | 64 00 00 00 | server_id = 100
- 000000FC | 29 00 00 00 | event_len = 41
- 00000100 | 1C 01 00 00 | log_pos = 0000011C
- 00000104 | 00 00 | flags = <none>
- ------------------------
- ...
- ************************
- WRITE_ROWS_EVENT [23]
- ************************
- 0000011C | 54 1B 12 4B | time_when = 1259477844
- 00000120 | 17 | event_type = 23
- 00000121 | 64 00 00 00 | server_id = 100
- 00000125 | 22 00 00 00 | event_len = 34
- 00000129 | 3E 01 00 00 | log_pos = 0000013E
- 0000012D | 10 00 | flags = LOG_EVENT_UPDATE_TABLE_MAP_VERSION_F
- ------------------------
- 0000012F | 0F 00 00 00 | table_id = 15
- ...
-
-New mysqlbinlog option
-~~~~~~~~~~~~~~~~~~~~~~
- --print-annotate-rows-events
-
-With this option, mysqlbinlog prints the content of Annotate-rows
-events (if the binary log does contain them). Without this option
-(i.e. by default), mysqlbinlog skips Annotate rows events.
-
-
-mysqlbinlog output
-~~~~~~~~~~~~~~~~~~
-Something like this:
+3. Server option: --replicate-annotate-rows-events
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+Tells the slave to reproduce Annotate_rows events recieved from the master
+in its own binary log (sensible only in pair with log-slave-updates option).
+
+ * Variable Name: replicate_annotate_rows_events
+ * Scope: Global
+ * Access Type: Read only
+ * Data Type: bool
+ * Default Value: OFF
+
+NOTE. Why do we additionally need this 'replicate' option? Why not to make
+the slave to reproduce this events when its binlog-annotate-rows-events
+global value is ON? Well, because, for example, we may want to configure
+the slave which should reproduce Annotate_rows events but has global
+binlog-annotate-rows-events = OFF meaning this to be the default value for
+the client threads (see also "How slave treats replicate-annotate-rows-events
+option" in LLD part).
+
+4. mysqlbinlog option: --print-annotate-rows-events
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+With this option, mysqlbinlog prints the content of Annotate_rows events (if
+the binary log does contain them). Without this option (i.e. by default),
+mysqlbinlog skips Annotate_rows events.
+5. mysqlbinlog output
+~~~~~~~~~~~~~~~~~~~~~
+With --print-annotate-rows-events, mysqlbinlog outputs Annotate_rows events
+in a form like this:
...
- # at 199
- # at 243
- # at 284
- #091129 9:57:24 server id 100 end_log_pos 243 Query: `insert into t1 values
-(1)`
- #091129 9:57:24 server id 100 end_log_pos 284 Table_map: `test`.`t1` mapped
-to number 15
- #091129 9:57:24 server id 100 end_log_pos 318 Write_rows: table id 15
+ # at 1646
+ #091219 12:45:26 server id 100 end_log_pos 1714 Query thread_id=1
+exec_time=0 error_code=0
+ SET TIMESTAMP=1261215926/*!*/;
+ BEGIN
+ /*!*/;
+ # at 1714
+ # at 1812
+ # at 1853
+ # at 1894
+ # at 1938
+ #091219 12:45:26 server id 100 end_log_pos 1812 Query: `DELETE t1, t2 FROM
+t1 INNER JOIN t2 INNER JOIN t3 WHERE t1.a=t2.a AND t2.a=t3.a`
+ #091219 12:45:26 server id 100 end_log_pos 1853 Table_map: `test`.`t1`
+mapped to number 16
+ #091219 12:45:26 server id 100 end_log_pos 1894 Table_map: `test`.`t2`
+mapped to number 17
+ #091219 12:45:26 server id 100 end_log_pos 1938 Delete_rows: table id 16
+ #091219 12:45:26 server id 100 end_log_pos 1982 Delete_rows: table id 17
flags: STMT_END_F
-
- BINLOG '
- VBsSSzNkAAAALAAAAPMAAAAAAGluc2VydCBpbnRvIHQxIHZhbHVlcyAoMSk=
- VBsSSxNkAAAAKQAAABwBAAAAAA8AAAAAAAAABHRlc3QAAnQxAAEDAAE=
- VBsSSxdkAAAAIgAAAD4BAAAQAA8AAAAAAAEAAf/+AQAAAA==
- '/*!*/;
- ### INSERT INTO test.t1
- ### SET
- ### @1=1 /* INT meta=0 nullable=1 is_null=0 */
...
-When master sends Annotate rows events
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-1. Master always sends Annotate_rows events to mysqlbinlog (in
- remote case).
-2. Master sends Annotate_rows events to a slave only if the slave has
- both log-slave-updates and binlog-annotate-rows-events options set.
-
------------------------------------------------------------
-=-=(View All Progress Notes, 22 total)=-=-
http://askmonty.org/worklog/index.pl?tid=47&nolimit=1
DESCRIPTION:
Store in binlog (and show in mysqlbinlog output) texts of statements that
caused RBR events
This is needed for (list from Monty):
- Easier to understand why updates happened
- Would make it easier to find out where in application things went
wrong (as you can search for exact strings)
- Allow one to filter things based on comments in the statement.
The cost of this can be that the binlog will be approximately 2x in size
(especially insert of big blob's would be a bit painful), so this should
be an optional feature.
HIGH-LEVEL SPECIFICATION:
Content
~~~~~~~
1. Annotate_rows_log_event
2. Server option: --binlog-annotate-rows-events
3. Server option: --replicate-annotate-rows-events
4. mysqlbinlog option: --print-annotate-rows-events
5. mysqlbinlog output
1. Annotate_rows_log_event [ ANNOTATE_ROWS_EVENT ]
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Describes the query which caused the corresponding rows events. Has empty
post-header and contains the query text in its data part. Example:
************************
ANNOTATE_ROWS_EVENT
************************
00000220 | B6 A0 2C 4B | time_when = 1261215926
00000224 | 33 | event_type = 51
00000225 | 64 00 00 00 | server_id = 100
00000229 | 36 00 00 00 | event_len = 54
0000022D | 56 02 00 00 | log_pos = 00000256
00000231 | 00 00 | flags = <none>
------------------------
00000233 | 49 4E 53 45 | query = "INSERT INTO t1 VALUES (1), (2), (3)"
00000237 | 52 54 20 49 |
0000023B | 4E 54 4F 20 |
0000023F | 74 31 20 56 |
00000243 | 41 4C 55 45 |
00000247 | 53 20 28 31 |
0000024B | 29 2C 20 28 |
0000024F | 32 29 2C 20 |
00000253 | 28 33 29 |
************************
In binary log, Annotate_rows event follows the (possible) 'BEGIN' Query event
and precedes the first of Table map events which accompany the corresponding
rows events. (See example in the "mysqlbinlog output" section below.)
2. Server option: --binlog-annotate-rows-events
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Tells the master to write Annotate_rows events to the binary log.
* Variable Name: binlog_annotate_rows_events
* Scope: Global & Session
* Access Type: Dynamic
* Data Type: bool
* Default Value: OFF
NOTE. Session values allows to annotate only some selected statements:
...
SET SESSION binlog_annotate_rows_events=ON;
... statements to be annotated ...
SET SESSION binlog_annotate_rows_events=OFF;
... statements not to be annotated ...
3. Server option: --replicate-annotate-rows-events
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Tells the slave to reproduce Annotate_rows events recieved from the master
in its own binary log (sensible only in pair with log-slave-updates option).
* Variable Name: replicate_annotate_rows_events
* Scope: Global
* Access Type: Read only
* Data Type: bool
* Default Value: OFF
NOTE. Why do we additionally need this 'replicate' option? Why not to make
the slave to reproduce this events when its binlog-annotate-rows-events
global value is ON? Well, because, for example, we may want to configure
the slave which should reproduce Annotate_rows events but has global
binlog-annotate-rows-events = OFF meaning this to be the default value for
the client threads (see also "How slave treats replicate-annotate-rows-events
option" in LLD part).
4. mysqlbinlog option: --print-annotate-rows-events
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
With this option, mysqlbinlog prints the content of Annotate_rows events (if
the binary log does contain them). Without this option (i.e. by default),
mysqlbinlog skips Annotate_rows events.
5. mysqlbinlog output
~~~~~~~~~~~~~~~~~~~~~
With --print-annotate-rows-events, mysqlbinlog outputs Annotate_rows events
in a form like this:
...
# at 1646
#091219 12:45:26 server id 100 end_log_pos 1714 Query thread_id=1
exec_time=0 error_code=0
SET TIMESTAMP=1261215926/*!*/;
BEGIN
/*!*/;
# at 1714
# at 1812
# at 1853
# at 1894
# at 1938
#091219 12:45:26 server id 100 end_log_pos 1812 Query: `DELETE t1, t2 FROM
t1 INNER JOIN t2 INNER JOIN t3 WHERE t1.a=t2.a AND t2.a=t3.a`
#091219 12:45:26 server id 100 end_log_pos 1853 Table_map: `test`.`t1`
mapped to number 16
#091219 12:45:26 server id 100 end_log_pos 1894 Table_map: `test`.`t2`
mapped to number 17
#091219 12:45:26 server id 100 end_log_pos 1938 Delete_rows: table id 16
#091219 12:45:26 server id 100 end_log_pos 1982 Delete_rows: table id 17
flags: STMT_END_F
...
LOW-LEVEL DESIGN:
Content
~~~~~~~
1. Annotate_rows event number
2. Outline of Annotate_rows event behavior
3. How Master writes Annotate_rows events to the binary log
4. How slave treats replicate-annotate-rows-events option
5. How slave IO thread requests Annotate_rows events
6. How master executes the request
7. How slave SQL thread processes Annotate_rows events
8. General remarks
1. Annotate_rows event number
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
To avoid possible event numbers conflict with MySQL/Sun, we leave a gap
between the last MySQL event number and the Annotate_rows event number:
enum Log_event_type
{ ...
INCIDENT_EVENT= 26,
// New MySQL event numbers are to be added here
MYSQL_EVENTS_END,
MARIA_EVENTS_BEGIN= 51,
// New Maria event numbers start from here
ANNOTATE_ROWS_EVENT= 51,
ENUM_END_EVENT
};
together with the corresponding extension of 'post_header_len' array in the
Format description event. (This extension does not affect the compatibility
of the binary log). Here is how Format description event looks like with
this extension:
************************
FORMAT_DESCRIPTION_EVENT
************************
00000004 | A1 A0 2C 4B | time_when = 1261215905
00000008 | 0F | event_type = 15
00000009 | 64 00 00 00 | server_id = 100
0000000D | 7F 00 00 00 | event_len = 127
00000011 | 83 00 00 00 | log_pos = 00000083
00000015 | 01 00 | flags = LOG_EVENT_BINLOG_IN_USE_F
------------------------
00000017 | 04 00 | binlog_ver = 4
00000019 | 35 2E 32 2E | server_ver = 5.2.0-MariaDB-alpha-debug-log
..... ...
0000004B | A1 A0 2C 4B | time_created = 1261215905
0000004F | 13 | common_header_len = 19
------------------------
post_header_len
------------------------
00000050 | 38 | 56 - START_EVENT_V3 [1]
..... ...
00000069 | 02 | 2 - INCIDENT_EVENT [26]
0000006A | 00 | 0 - RESERVED [27]
..... ...
00000081 | 00 | 0 - RESERVED [50]
00000082 | 00 | 0 - ANNOTATE_ROWS_EVENT [51]
************************
2. Outline of Annotate_rows event behavior
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Each Annotate_rows_log_event object has two private members describing the
corresponding query:
char *m_query_txt;
uint m_query_len;
When the object is created for writing to a binary log, this query is taken
from 'thd' (for short, below we omit the 'Annotate_rows_log_event::' prefix
as well as other implementation details):
Annotate_rows_log_event(THD *thd)
{
m_query_txt = thd->query();
m_query_len = thd->query_length();
}
When the object is read from a binary log, the query is taken from the buffer
containing the binary log representation of the event (this buffer is allocated
in Log_event object from which all Log events are derived):
Annotate_rows_log_event(char *buf, uint event_len,
Format_description_log_event *desc)
{
m_query_len = event_len - desc->common_header_len;
m_query_txt = buf + desc->common_header_len;
}
The events are written to the binary log by the Log_event::write() member
which calls virtual write_data_header() and write_data_body() members
("data header" and "post header" are synonym in replication terminology).
In our case, data header is empty and data body is just the query:
bool write_data_body(IO_CACHE *file)
{
return my_b_safe_write(file, (uchar*) m_query_txt, m_query_len);
}
Printing the event is just printing the query:
void Annotate_rows_log_event::print(FILE *file, PRINT_EVENT_INFO *pinfo)
{
my_b_printf(&pinfo->head_cache, "\tQuery: `%s`\n", m_query_txt);
}
3. How Master writes Annotate_rows events to the binary log
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
The event is written to the binary log just before the group of Table_map
events which precede corresponding Rows events (one query may generate
several Table map events in the binary log, but the corresponding
Annotate_rows event must be written only once before the first Table map
event; hence the boolean variable 'with_annotate' below):
int write_locked_table_maps(THD *thd)
{ ...
bool with_annotate= thd->variables.binlog_annotate_rows_events;
...
for (uint i= 0; i < ... <number of tables> ...; ++i)
{ ...
thd->binlog_write_table_map(table, ..., with_annotate);
with_annotate= 0; // write Annotate_event not more than once
...
}
...
}
int THD::binlog_write_table_map(TABLE *table, ..., bool with_annotate)
{ ...
Table_map_log_event the_event(...);
...
if (with_annotate)
{
Annotate_rows_log_event anno(this);
mysql_bin_log.write(&anno);
}
mysql_bin_log.write(&the_event);
...
}
4. How slave treats replicate-annotate-rows-events option
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
The replicate-annotate-rows-events option is treated just as the session
value of the binlog_annotate_rows_events variable for the slave IO and
SQL threads. This setting is done during initialization of these threads:
pthread_handler_t handle_slave_io(void *arg)
{
THD *thd= new THD;
...
init_slave_thread(thd, SLAVE_THD_IO);
...
}
pthread_handler_t handle_slave_sql(void *arg)
{
THD *thd= new THD;
...
init_slave_thread(thd, SLAVE_THD_SQL);
...
}
int init_slave_thread(THD* thd, SLAVE_THD_TYPE thd_type)
{ ...
thd->variables.binlog_annotate_rows_events=
opt_replicate_annotate_rows_events;
...
}
5. How slave IO thread requests Annotate_rows events
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
If the replicate-annotate-rows-events option is not set on a slave, there
is no need for master to send Annotate_rows events to this slave. The slave
(or mysqlbinlog in remote case), before requesting binlog dump via the
COM_BINLOG_DUMP command, informs the master whether it should send these
events by executing the newly added COM_BINLOG_DUMP_OPTIONS_EXT server
command:
case COM_BINLOG_DUMP_OPTIONS_EXT:
thd->binlog_dump_flags_ext= packet[0];
my_ok(thd);
break;
Note. We add this new command and don't use COM_BINLOG_DUMP to avoid possible
conflicts with MySQL/Sun.
6. How master executes the request
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
case COM_BINLOG_DUMP:
{ ...
flags= uint2korr(packet + 4);
...
mysql_binlog_send(thd, ..., flags);
...
}
void mysql_binlog_send(THD* thd, ..., ushort flags)
{ ...
Log_event::read_log_event(&log, packet, ...);
...
if ((*packet)[EVENT_TYPE_OFFSET + 1] != ANNOTATE_ROWS_EVENT ||
flags & BINLOG_SEND_ANNOTATE_ROWS_EVENT)
{
my_net_write(net, packet->ptr(), packet->length());
}
...
}
7. How slave SQL thread processes Annotate_rows events
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
The slave processes each recieved event by "applying" it, i.e. by
calling the Log_event::apply_event() function which in turn calls
the virtual do_apply_event() member specific for each type of the
event.
int exec_relay_log_event(THD* thd, Relay_log_info* rli)
{ ...
Log_event *ev = next_event(rli);
...
apply_event_and_update_pos(ev, ...);
if (ev->get_type_code() != FORMAT_DESCRIPTION_EVENT)
delete ev;
...
}
int apply_event_and_update_pos(Log_event *ev, ...)
{ ...
ev->apply_event(...);
...
}
int Log_event::apply_event(...)
{
return do_apply_event(...);
}
What does it mean to "apply" an Annotate_rows event? It means to set current
thd query to that of the described by the event, i.e. to the query which
caused the subsequent Rows events (see "How Master writes Annotate_rows
events to the binary log" to follow what happens further when the subsequent
Rows events are applied):
int Annotate_rows_log_event::do_apply_event(...)
{
thd->set_query(m_query_txt, m_query_len);
}
NOTE. I am not sure, but possibly current values of thd->query and
thd->query_length should be saved before calling set_query() and to be
restored on the Annotate_rows_log_event object deletion.
Is it really needed ?
After calling this do_apply_event() function we may not delete the
Annotate_rows_log_event object immediatedly (see exec_relay_log_event()
above) because thd->query now points to the string inside this object.
We may keep the pointer to this object in the Relay_log_info:
class Relay_log_info
{
public:
...
void set_annotate_event(Annotate_rows_log_event*);
Annotate_rows_log_event* get_annotate_event();
void free_annotate_event();
...
private:
Annotate_rows_log_event* m_annotate_event;
};
The saved Annotate_rows object should be deleted when all corresponding
Rows events will be processed:
int exec_relay_log_event(THD* thd, Relay_log_info* rli)
{ ...
Log_event *ev= next_event(rli);
...
apply_event_and_update_pos(ev, ...);
if (rli->get_annotate_event() && is_last_rows_event(ev))
rli->free_annotate_event();
else if (ev->get_type_code() == ANNOTATE_ROWS_EVENT)
rli->set_annotate_event((Annotate_rows_log_event*) ev);
else if (ev->get_type_code() != FORMAT_DESCRIPTION_EVENT)
delete ev;
...
}
where
bool is_last_rows_event(Log_event* ev)
{
Log_event_type type= ev->get_type_code();
if (IS_ROWS_EVENT_TYPE(type))
{
Rows_log_event* rows= (Rows_log_event*)ev;
return rows->get_flags(Rows_log_event::STMT_END_F);
}
return 0;
}
#define IS_ROWS_EVENT_TYPE(type) ((type) == WRITE_ROWS_EVENT || \
(type) == UPDATE_ROWS_EVENT || \
(type) == DELETE_ROWS_EVENT)
8. General remarks
~~~~~~~~~~~~~~~~~~
Kristian noticed that introducing new log event type should be coordinated
somehow with MySQL/Sun:
Kristian: The numeric code for this event must be assigned carefully.
It should be coordinated with MySQL/Sun, otherwise we can get into a
situation where MySQL uses the same numeric code for one event that
MariaDB uses for ANNOTATE_ROWS_EVENT, which would make merging the two
impossible.
Alex: I reserved about 20 numbers not to have possible conflicts
with MySQL.
Kristian: Still, I think it would be appropriate to send a polite email
to internals(a)lists.mysql.com about this and suggesting to reserve the
event number.
ESTIMATED WORK TIME
ESTIMATED COMPLETION DATE
-----------------------------------------------------------------------
WorkLog (v3.5.9)
1
0
[Maria-developers] Updated (by Guest): Store in binlog text of statements that caused RBR events (47)
by worklog-noreply@askmonty.org 05 Feb '10
by worklog-noreply@askmonty.org 05 Feb '10
05 Feb '10
-----------------------------------------------------------------------
WORKLOG TASK
-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
TASK...........: Store in binlog text of statements that caused RBR events
CREATION DATE..: Sat, 15 Aug 2009, 23:48
SUPERVISOR.....: Monty
IMPLEMENTOR....:
COPIES TO......: Knielsen
CATEGORY.......: Server-Sprint
TASK ID........: 47 (http://askmonty.org/worklog/?tid=47)
VERSION........: Server-9.x
STATUS.........: In-Progress
PRIORITY.......: 60
WORKED HOURS...: 20
ESTIMATE.......: 35 (hours remain)
ORIG. ESTIMATE.: 35
PROGRESS NOTES:
-=-=(Guest - Fri, 05 Feb 2010, 13:40)=-=-
Category updated.
--- /tmp/wklog.47.old.9197 2010-02-05 13:40:36.000000000 +0200
+++ /tmp/wklog.47.new.9197 2010-02-05 13:40:36.000000000 +0200
@@ -1 +1 @@
-Server-RawIdeaBin
+Server-Sprint
-=-=(Guest - Fri, 05 Feb 2010, 13:40)=-=-
Status updated.
--- /tmp/wklog.47.old.9197 2010-02-05 13:40:36.000000000 +0200
+++ /tmp/wklog.47.new.9197 2010-02-05 13:40:36.000000000 +0200
@@ -1 +1 @@
-Un-Assigned
+In-Progress
-=-=(Alexi - Thu, 04 Feb 2010, 09:54)=-=-
Low Level Design modified.
--- /tmp/wklog.47.old.16174 2010-02-04 09:54:13.000000000 +0200
+++ /tmp/wklog.47.new.16174 2010-02-04 09:54:13.000000000 +0200
@@ -171,35 +171,20 @@
5. How slave IO thread requests Annotate_rows events
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-When requesting an event, the slave should inform the master whether
-it should send Annotate_rows events or not. To that end we add a new
-BINLOG_SEND_ANNOTATE_ROWS_EVENT flag used when requesting an event:
+If the replicate-annotate-rows-events option is not set on a slave, there
+is no need for master to send Annotate_rows events to this slave. The slave
+(or mysqlbinlog in remote case), before requesting binlog dump via the
+COM_BINLOG_DUMP command, informs the master whether it should send these
+events by executing the newly added COM_BINLOG_DUMP_OPTIONS_EXT server
+command:
+
+ case COM_BINLOG_DUMP_OPTIONS_EXT:
+ thd->binlog_dump_flags_ext= packet[0];
+ my_ok(thd);
+ break;
- #define BINLOG_DUMP_NON_BLOCK 1
- #define BINLOG_SEND_ANNOTATE_ROWS_EVENT 2
-
- pthread_handler_t handle_slave_io(void *arg)
- { ...
- request_dump(mysql, ...);
- ...
- }
-
- int request_dump(MYSQL* mysql, ...)
- { ...
- if (opt_log_slave_updates &&
- mi->io_thd->variables.binlog_annotate_rows_events)
- binlog_flags|= BINLOG_SEND_ANNOTATE_ROWS_EVENT;
- ...
- int2store(buf + 4, binlog_flags);
- ...
- simple_command(mysql, COM_BINLOG_DUMP, buf, ...);
- ...
- }
-
-NOTE. mysqlbinlog, when remotely requesting BINLOG_DUMP by calling the
-simple_command() function, should also use this flag if it wants (in case
-of the --print-annotate-rows-events option set) to recieve Annotate_rows
-events.
+Note. We add this new command and don't use COM_BINLOG_DUMP to avoid possible
+conflicts with MySQL/Sun.
6. How master executes the request
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -338,10 +323,4 @@
to internals(a)lists.mysql.com about this and suggesting to reserve the
event number.
-Also we should notice the introduction of the BINLOG_SEND_ANNOTATE_ROWS_EVENT
-flag taking into account that MySQL/Sun may also introduce a flag with the
-same value to be used in the request_dump-mysql_binlog_send interface.
-But this is mainly the question of merging: if a conflict concerning this
-flag occur, we may simply change the BINLOG_SEND_ANNOTATE_ROWS_EVENT value
-(this does not require additional changes in the code).
-=-=(Alexi - Sun, 20 Dec 2009, 16:00)=-=-
Low Level Design modified.
--- /tmp/wklog.47.old.19667 2009-12-20 14:00:56.000000000 +0000
+++ /tmp/wklog.47.new.19667 2009-12-20 14:00:56.000000000 +0000
@@ -196,6 +196,11 @@
...
}
+NOTE. mysqlbinlog, when remotely requesting BINLOG_DUMP by calling the
+simple_command() function, should also use this flag if it wants (in case
+of the --print-annotate-rows-events option set) to recieve Annotate_rows
+events.
+
6. How master executes the request
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -212,8 +217,7 @@
Log_event::read_log_event(&log, packet, ...);
...
if ((*packet)[EVENT_TYPE_OFFSET + 1] != ANNOTATE_ROWS_EVENT ||
- flags & BINLOG_SEND_ANNOTATE_ROWS_EVENT ||
- thd->server_id == 0 /* slave == mysqlbinlog */ )
+ flags & BINLOG_SEND_ANNOTATE_ROWS_EVENT)
{
my_net_write(net, packet->ptr(), packet->length());
}
-=-=(Alexi - Sun, 20 Dec 2009, 13:14)=-=-
Low Level Design modified.
--- /tmp/wklog.47.old.11350 2009-12-20 13:14:04.000000000 +0200
+++ /tmp/wklog.47.new.11350 2009-12-20 13:14:04.000000000 +0200
@@ -282,23 +282,18 @@
Annotate_rows_log_event* m_annotate_event;
};
-When the saved Annotate_rows object may be deleted? When all corresponding
-Rows events will be processed, i.e. before processing the first non-Rows
-event (note that Annotate_rows object resides in the binary log *after*
-the (possible) 'BEGIN' Query event which accompanies the rows events; note
-also that this deletion is adjusted with the case when some or all
-corresponding Rows events are filtered out by replicate filter rules):
+The saved Annotate_rows object should be deleted when all corresponding
+Rows events will be processed:
int exec_relay_log_event(THD* thd, Relay_log_info* rli)
{ ...
Log_event *ev= next_event(rli);
...
- if (rli->get_annotate_event() && !IS_RBR_EVENT_TYPE(ev->get_type_code()))
- rli->free_annotate_event();
-
apply_event_and_update_pos(ev, ...);
- if (ev->get_type_code() == ANNOTATE_ROWS_EVENT)
+ if (rli->get_annotate_event() && is_last_rows_event(ev))
+ rli->free_annotate_event();
+ else if (ev->get_type_code() == ANNOTATE_ROWS_EVENT)
rli->set_annotate_event((Annotate_rows_log_event*) ev);
else if (ev->get_type_code() != FORMAT_DESCRIPTION_EVENT)
delete ev;
@@ -307,10 +302,21 @@
where
- #define IS_RBR_EVENT_TYPE(type) ( (type) == TABLE_MAP_EVENT || \
- (type) == WRITE_ROWS_EVENT || \
+ bool is_last_rows_event(Log_event* ev)
+ {
+ Log_event_type type= ev->get_type_code();
+ if (IS_ROWS_EVENT_TYPE(type))
+ {
+ Rows_log_event* rows= (Rows_log_event*)ev;
+ return rows->get_flags(Rows_log_event::STMT_END_F);
+ }
+
+ return 0;
+ }
+
+ #define IS_ROWS_EVENT_TYPE(type) ((type) == WRITE_ROWS_EVENT || \
(type) == UPDATE_ROWS_EVENT || \
- (type) == DELETE_ROWS_EVENT )
+ (type) == DELETE_ROWS_EVENT)
8. General remarks
~~~~~~~~~~~~~~~~~~
-=-=(Alexi - Sun, 20 Dec 2009, 09:29)=-=-
Low Level Design modified.
--- /tmp/wklog.47.old.32726 2009-12-20 07:29:56.000000000 +0000
+++ /tmp/wklog.47.new.32726 2009-12-20 07:29:56.000000000 +0000
@@ -56,7 +56,7 @@
0000006A | 00 | 0 - RESERVED [27]
..... ...
00000081 | 00 | 0 - RESERVED [50]
- 00000082 | 00 | 0 - ANNOTATE_RBR_EVENT [51]
+ 00000082 | 00 | 0 - ANNOTATE_ROWS_EVENT [51]
************************
2. Outline of Annotate_rows event behavior
-=-=(Alexi - Sat, 19 Dec 2009, 16:10)=-=-
Low Level Design modified.
--- /tmp/wklog.47.old.16051 2009-12-19 16:10:48.000000000 +0200
+++ /tmp/wklog.47.new.16051 2009-12-19 16:10:48.000000000 +0200
@@ -253,7 +253,7 @@
thd query to that of the described by the event, i.e. to the query which
caused the subsequent Rows events (see "How Master writes Annotate_rows
events to the binary log" to follow what happens further when the subsequent
-Rows events is applied):
+Rows events are applied):
int Annotate_rows_log_event::do_apply_event(...)
{
-=-=(Alexi - Sat, 19 Dec 2009, 16:02)=-=-
High-Level Specification modified.
--- /tmp/wklog.47.old.15695 2009-12-19 16:02:33.000000000 +0200
+++ /tmp/wklog.47.new.15695 2009-12-19 16:02:33.000000000 +0200
@@ -12,7 +12,7 @@
post-header and contains the query text in its data part. Example:
************************
- ANNOTATE_RBR_EVENT
+ ANNOTATE_ROWS_EVENT
************************
00000220 | B6 A0 2C 4B | time_when = 1261215926
00000224 | 33 | event_type = 51
-=-=(Alexi - Sat, 19 Dec 2009, 15:58)=-=-
Low Level Design modified.
--- /tmp/wklog.47.old.15437 2009-12-19 13:58:12.000000000 +0000
+++ /tmp/wklog.47.new.15437 2009-12-19 13:58:12.000000000 +0000
@@ -1 +1,337 @@
+Content
+~~~~~~~
+ 1. Annotate_rows event number
+ 2. Outline of Annotate_rows event behavior
+ 3. How Master writes Annotate_rows events to the binary log
+ 4. How slave treats replicate-annotate-rows-events option
+ 5. How slave IO thread requests Annotate_rows events
+ 6. How master executes the request
+ 7. How slave SQL thread processes Annotate_rows events
+ 8. General remarks
+
+1. Annotate_rows event number
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+To avoid possible event numbers conflict with MySQL/Sun, we leave a gap
+between the last MySQL event number and the Annotate_rows event number:
+
+ enum Log_event_type
+ { ...
+ INCIDENT_EVENT= 26,
+ // New MySQL event numbers are to be added here
+ MYSQL_EVENTS_END,
+
+ MARIA_EVENTS_BEGIN= 51,
+ // New Maria event numbers start from here
+ ANNOTATE_ROWS_EVENT= 51,
+
+ ENUM_END_EVENT
+ };
+
+together with the corresponding extension of 'post_header_len' array in the
+Format description event. (This extension does not affect the compatibility
+of the binary log). Here is how Format description event looks like with
+this extension:
+
+ ************************
+ FORMAT_DESCRIPTION_EVENT
+ ************************
+ 00000004 | A1 A0 2C 4B | time_when = 1261215905
+ 00000008 | 0F | event_type = 15
+ 00000009 | 64 00 00 00 | server_id = 100
+ 0000000D | 7F 00 00 00 | event_len = 127
+ 00000011 | 83 00 00 00 | log_pos = 00000083
+ 00000015 | 01 00 | flags = LOG_EVENT_BINLOG_IN_USE_F
+ ------------------------
+ 00000017 | 04 00 | binlog_ver = 4
+ 00000019 | 35 2E 32 2E | server_ver = 5.2.0-MariaDB-alpha-debug-log
+ ..... ...
+ 0000004B | A1 A0 2C 4B | time_created = 1261215905
+ 0000004F | 13 | common_header_len = 19
+ ------------------------
+ post_header_len
+ ------------------------
+ 00000050 | 38 | 56 - START_EVENT_V3 [1]
+ ..... ...
+ 00000069 | 02 | 2 - INCIDENT_EVENT [26]
+ 0000006A | 00 | 0 - RESERVED [27]
+ ..... ...
+ 00000081 | 00 | 0 - RESERVED [50]
+ 00000082 | 00 | 0 - ANNOTATE_RBR_EVENT [51]
+ ************************
+
+2. Outline of Annotate_rows event behavior
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+Each Annotate_rows_log_event object has two private members describing the
+corresponding query:
+
+ char *m_query_txt;
+ uint m_query_len;
+
+When the object is created for writing to a binary log, this query is taken
+from 'thd' (for short, below we omit the 'Annotate_rows_log_event::' prefix
+as well as other implementation details):
+
+ Annotate_rows_log_event(THD *thd)
+ {
+ m_query_txt = thd->query();
+ m_query_len = thd->query_length();
+ }
+
+When the object is read from a binary log, the query is taken from the buffer
+containing the binary log representation of the event (this buffer is allocated
+in Log_event object from which all Log events are derived):
+
+ Annotate_rows_log_event(char *buf, uint event_len,
+ Format_description_log_event *desc)
+ {
+ m_query_len = event_len - desc->common_header_len;
+ m_query_txt = buf + desc->common_header_len;
+ }
+
+The events are written to the binary log by the Log_event::write() member
+which calls virtual write_data_header() and write_data_body() members
+("data header" and "post header" are synonym in replication terminology).
+In our case, data header is empty and data body is just the query:
+
+ bool write_data_body(IO_CACHE *file)
+ {
+ return my_b_safe_write(file, (uchar*) m_query_txt, m_query_len);
+ }
+
+Printing the event is just printing the query:
+
+ void Annotate_rows_log_event::print(FILE *file, PRINT_EVENT_INFO *pinfo)
+ {
+ my_b_printf(&pinfo->head_cache, "\tQuery: `%s`\n", m_query_txt);
+ }
+
+3. How Master writes Annotate_rows events to the binary log
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+The event is written to the binary log just before the group of Table_map
+events which precede corresponding Rows events (one query may generate
+several Table map events in the binary log, but the corresponding
+Annotate_rows event must be written only once before the first Table map
+event; hence the boolean variable 'with_annotate' below):
+
+ int write_locked_table_maps(THD *thd)
+ { ...
+ bool with_annotate= thd->variables.binlog_annotate_rows_events;
+ ...
+ for (uint i= 0; i < ... <number of tables> ...; ++i)
+ { ...
+ thd->binlog_write_table_map(table, ..., with_annotate);
+ with_annotate= 0; // write Annotate_event not more than once
+ ...
+ }
+ ...
+ }
+
+ int THD::binlog_write_table_map(TABLE *table, ..., bool with_annotate)
+ { ...
+ Table_map_log_event the_event(...);
+ ...
+ if (with_annotate)
+ {
+ Annotate_rows_log_event anno(this);
+ mysql_bin_log.write(&anno);
+ }
+
+ mysql_bin_log.write(&the_event);
+ ...
+ }
+
+4. How slave treats replicate-annotate-rows-events option
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+The replicate-annotate-rows-events option is treated just as the session
+value of the binlog_annotate_rows_events variable for the slave IO and
+SQL threads. This setting is done during initialization of these threads:
+
+ pthread_handler_t handle_slave_io(void *arg)
+ {
+ THD *thd= new THD;
+ ...
+ init_slave_thread(thd, SLAVE_THD_IO);
+ ...
+ }
+
+ pthread_handler_t handle_slave_sql(void *arg)
+ {
+ THD *thd= new THD;
+ ...
+ init_slave_thread(thd, SLAVE_THD_SQL);
+ ...
+ }
+
+ int init_slave_thread(THD* thd, SLAVE_THD_TYPE thd_type)
+ { ...
+ thd->variables.binlog_annotate_rows_events=
+ opt_replicate_annotate_rows_events;
+ ...
+ }
+
+5. How slave IO thread requests Annotate_rows events
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+When requesting an event, the slave should inform the master whether
+it should send Annotate_rows events or not. To that end we add a new
+BINLOG_SEND_ANNOTATE_ROWS_EVENT flag used when requesting an event:
+
+ #define BINLOG_DUMP_NON_BLOCK 1
+ #define BINLOG_SEND_ANNOTATE_ROWS_EVENT 2
+
+ pthread_handler_t handle_slave_io(void *arg)
+ { ...
+ request_dump(mysql, ...);
+ ...
+ }
+
+ int request_dump(MYSQL* mysql, ...)
+ { ...
+ if (opt_log_slave_updates &&
+ mi->io_thd->variables.binlog_annotate_rows_events)
+ binlog_flags|= BINLOG_SEND_ANNOTATE_ROWS_EVENT;
+ ...
+ int2store(buf + 4, binlog_flags);
+ ...
+ simple_command(mysql, COM_BINLOG_DUMP, buf, ...);
+ ...
+ }
+
+6. How master executes the request
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+ case COM_BINLOG_DUMP:
+ { ...
+ flags= uint2korr(packet + 4);
+ ...
+ mysql_binlog_send(thd, ..., flags);
+ ...
+ }
+
+ void mysql_binlog_send(THD* thd, ..., ushort flags)
+ { ...
+ Log_event::read_log_event(&log, packet, ...);
+ ...
+ if ((*packet)[EVENT_TYPE_OFFSET + 1] != ANNOTATE_ROWS_EVENT ||
+ flags & BINLOG_SEND_ANNOTATE_ROWS_EVENT ||
+ thd->server_id == 0 /* slave == mysqlbinlog */ )
+ {
+ my_net_write(net, packet->ptr(), packet->length());
+ }
+ ...
+ }
+
+7. How slave SQL thread processes Annotate_rows events
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+The slave processes each recieved event by "applying" it, i.e. by
+calling the Log_event::apply_event() function which in turn calls
+the virtual do_apply_event() member specific for each type of the
+event.
+
+ int exec_relay_log_event(THD* thd, Relay_log_info* rli)
+ { ...
+ Log_event *ev = next_event(rli);
+ ...
+ apply_event_and_update_pos(ev, ...);
+
+ if (ev->get_type_code() != FORMAT_DESCRIPTION_EVENT)
+ delete ev;
+ ...
+ }
+
+ int apply_event_and_update_pos(Log_event *ev, ...)
+ { ...
+ ev->apply_event(...);
+ ...
+ }
+
+ int Log_event::apply_event(...)
+ {
+ return do_apply_event(...);
+ }
+
+What does it mean to "apply" an Annotate_rows event? It means to set current
+thd query to that of the described by the event, i.e. to the query which
+caused the subsequent Rows events (see "How Master writes Annotate_rows
+events to the binary log" to follow what happens further when the subsequent
+Rows events is applied):
+
+ int Annotate_rows_log_event::do_apply_event(...)
+ {
+ thd->set_query(m_query_txt, m_query_len);
+ }
+
+NOTE. I am not sure, but possibly current values of thd->query and
+thd->query_length should be saved before calling set_query() and to be
+restored on the Annotate_rows_log_event object deletion.
+Is it really needed ?
+
+After calling this do_apply_event() function we may not delete the
+Annotate_rows_log_event object immediatedly (see exec_relay_log_event()
+above) because thd->query now points to the string inside this object.
+We may keep the pointer to this object in the Relay_log_info:
+
+ class Relay_log_info
+ {
+ public:
+ ...
+ void set_annotate_event(Annotate_rows_log_event*);
+ Annotate_rows_log_event* get_annotate_event();
+ void free_annotate_event();
+ ...
+ private:
+ Annotate_rows_log_event* m_annotate_event;
+ };
+
+When the saved Annotate_rows object may be deleted? When all corresponding
+Rows events will be processed, i.e. before processing the first non-Rows
+event (note that Annotate_rows object resides in the binary log *after*
+the (possible) 'BEGIN' Query event which accompanies the rows events; note
+also that this deletion is adjusted with the case when some or all
+corresponding Rows events are filtered out by replicate filter rules):
+
+ int exec_relay_log_event(THD* thd, Relay_log_info* rli)
+ { ...
+ Log_event *ev= next_event(rli);
+ ...
+ if (rli->get_annotate_event() && !IS_RBR_EVENT_TYPE(ev->get_type_code()))
+ rli->free_annotate_event();
+
+ apply_event_and_update_pos(ev, ...);
+
+ if (ev->get_type_code() == ANNOTATE_ROWS_EVENT)
+ rli->set_annotate_event((Annotate_rows_log_event*) ev);
+ else if (ev->get_type_code() != FORMAT_DESCRIPTION_EVENT)
+ delete ev;
+ ...
+ }
+
+where
+
+ #define IS_RBR_EVENT_TYPE(type) ( (type) == TABLE_MAP_EVENT || \
+ (type) == WRITE_ROWS_EVENT || \
+ (type) == UPDATE_ROWS_EVENT || \
+ (type) == DELETE_ROWS_EVENT )
+
+8. General remarks
+~~~~~~~~~~~~~~~~~~
+Kristian noticed that introducing new log event type should be coordinated
+somehow with MySQL/Sun:
+
+ Kristian: The numeric code for this event must be assigned carefully.
+ It should be coordinated with MySQL/Sun, otherwise we can get into a
+ situation where MySQL uses the same numeric code for one event that
+ MariaDB uses for ANNOTATE_ROWS_EVENT, which would make merging the two
+ impossible.
+ Alex: I reserved about 20 numbers not to have possible conflicts
+ with MySQL.
+ Kristian: Still, I think it would be appropriate to send a polite email
+ to internals(a)lists.mysql.com about this and suggesting to reserve the
+ event number.
+
+Also we should notice the introduction of the BINLOG_SEND_ANNOTATE_ROWS_EVENT
+flag taking into account that MySQL/Sun may also introduce a flag with the
+same value to be used in the request_dump-mysql_binlog_send interface.
+But this is mainly the question of merging: if a conflict concerning this
+flag occur, we may simply change the BINLOG_SEND_ANNOTATE_ROWS_EVENT value
+(this does not require additional changes in the code).
-=-=(Alexi - Sat, 19 Dec 2009, 15:41)=-=-
High-Level Specification modified.
--- /tmp/wklog.47.old.14545 2009-12-19 15:41:21.000000000 +0200
+++ /tmp/wklog.47.new.14545 2009-12-19 15:41:21.000000000 +0200
@@ -1,122 +1,107 @@
-First suggestion:
-
-> I think for this we would actually need a new binlog event type
-> (Comment_log_event?). Unless we want to log an empty statement Query_log_event
-> containing only a comment (a bit of a hack).
-
-New server option
-~~~~~~~~~~~~~~~~~
- --binlog-annotate-rows-events
-
-Setting this option makes RBR (rows-) events in the binary log to be
-preceded by Annotate rows events (see below). The corresponding
-'binlog_annotate_rows_events' system variable is dynamic and has both
-global and session values. Default global value is OFF.
-
-Note. Session values are usefull to make it possible to annotate only
- some selected statements:
+Content
+~~~~~~~
+ 1. Annotate_rows_log_event
+ 2. Server option: --binlog-annotate-rows-events
+ 3. Server option: --replicate-annotate-rows-events
+ 4. mysqlbinlog option: --print-annotate-rows-events
+ 5. mysqlbinlog output
+
+1. Annotate_rows_log_event [ ANNOTATE_ROWS_EVENT ]
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+Describes the query which caused the corresponding rows events. Has empty
+post-header and contains the query text in its data part. Example:
+
+ ************************
+ ANNOTATE_RBR_EVENT
+ ************************
+ 00000220 | B6 A0 2C 4B | time_when = 1261215926
+ 00000224 | 33 | event_type = 51
+ 00000225 | 64 00 00 00 | server_id = 100
+ 00000229 | 36 00 00 00 | event_len = 54
+ 0000022D | 56 02 00 00 | log_pos = 00000256
+ 00000231 | 00 00 | flags = <none>
+ ------------------------
+ 00000233 | 49 4E 53 45 | query = "INSERT INTO t1 VALUES (1), (2), (3)"
+ 00000237 | 52 54 20 49 |
+ 0000023B | 4E 54 4F 20 |
+ 0000023F | 74 31 20 56 |
+ 00000243 | 41 4C 55 45 |
+ 00000247 | 53 20 28 31 |
+ 0000024B | 29 2C 20 28 |
+ 0000024F | 32 29 2C 20 |
+ 00000253 | 28 33 29 |
+ ************************
+
+In binary log, Annotate_rows event follows the (possible) 'BEGIN' Query event
+and precedes the first of Table map events which accompany the corresponding
+rows events. (See example in the "mysqlbinlog output" section below.)
+
+2. Server option: --binlog-annotate-rows-events
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+Tells the master to write Annotate_rows events to the binary log.
+
+ * Variable Name: binlog_annotate_rows_events
+ * Scope: Global & Session
+ * Access Type: Dynamic
+ * Data Type: bool
+ * Default Value: OFF
+NOTE. Session values allows to annotate only some selected statements:
...
SET SESSION binlog_annotate_rows_events=ON;
... statements to be annotated ...
SET SESSION binlog_annotate_rows_events=OFF;
... statements not to be annotated ...
-New binlog event type
-~~~~~~~~~~~~~~~~~~~~~
- Annotate_rows_log_event [ ANNOTATE_ROWS_EVENT ]
-
-Describes the query which caused the corresponding rows event. In binary log,
-precedes each Table_map_log_event. Contains empty post-header and the query
-text in its data part.
-
-The numeric code for this event must be assigned carefully. It should be
-coordinated with MySQL/Sun, otherwise we can get into a situation where MySQL
-uses the same numeric code for one event that MariaDB uses for
-ANNOTATE_ROWS_EVENT, which would make merging the two impossible.
-
-Example:
-
- ...
- ************************
- ANNOTATE_ROWS_EVENT [51]
- ************************
- 000000C7 | 54 1B 12 4B | time_when = 1259477844
- 000000CB | 33 | event_type = 51
- 000000CC | 64 00 00 00 | server_id = 100
- 000000D0 | 2C 00 00 00 | event_len = 44
- 000000D4 | F3 00 00 00 | log_pos = 000000F3
- 000000D8 | 00 00 | flags = <none>
- ------------------------
- 000000DA | 69 6E 73 65 | query = "insert into t1 values (1)"
- 000000DE | 72 74 20 69 |
- 000000E2 | 6E 74 6F 20 |
- 000000E6 | 74 31 20 76 |
- 000000EA | 61 6C 75 65 |
- 000000EE | 73 20 28 31 |
- 000000F2 | 29 |
- ************************
- TABLE_MAP_EVENT [19]
- ************************
- 000000F3 | 54 1B 12 4B | time_when = 1259477844
- 000000F7 | 13 | event_type = 19
- 000000F8 | 64 00 00 00 | server_id = 100
- 000000FC | 29 00 00 00 | event_len = 41
- 00000100 | 1C 01 00 00 | log_pos = 0000011C
- 00000104 | 00 00 | flags = <none>
- ------------------------
- ...
- ************************
- WRITE_ROWS_EVENT [23]
- ************************
- 0000011C | 54 1B 12 4B | time_when = 1259477844
- 00000120 | 17 | event_type = 23
- 00000121 | 64 00 00 00 | server_id = 100
- 00000125 | 22 00 00 00 | event_len = 34
- 00000129 | 3E 01 00 00 | log_pos = 0000013E
- 0000012D | 10 00 | flags = LOG_EVENT_UPDATE_TABLE_MAP_VERSION_F
- ------------------------
- 0000012F | 0F 00 00 00 | table_id = 15
- ...
-
-New mysqlbinlog option
-~~~~~~~~~~~~~~~~~~~~~~
- --print-annotate-rows-events
-
-With this option, mysqlbinlog prints the content of Annotate-rows
-events (if the binary log does contain them). Without this option
-(i.e. by default), mysqlbinlog skips Annotate rows events.
-
-
-mysqlbinlog output
-~~~~~~~~~~~~~~~~~~
-Something like this:
+3. Server option: --replicate-annotate-rows-events
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+Tells the slave to reproduce Annotate_rows events recieved from the master
+in its own binary log (sensible only in pair with log-slave-updates option).
+
+ * Variable Name: replicate_annotate_rows_events
+ * Scope: Global
+ * Access Type: Read only
+ * Data Type: bool
+ * Default Value: OFF
+
+NOTE. Why do we additionally need this 'replicate' option? Why not to make
+the slave to reproduce this events when its binlog-annotate-rows-events
+global value is ON? Well, because, for example, we may want to configure
+the slave which should reproduce Annotate_rows events but has global
+binlog-annotate-rows-events = OFF meaning this to be the default value for
+the client threads (see also "How slave treats replicate-annotate-rows-events
+option" in LLD part).
+
+4. mysqlbinlog option: --print-annotate-rows-events
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+With this option, mysqlbinlog prints the content of Annotate_rows events (if
+the binary log does contain them). Without this option (i.e. by default),
+mysqlbinlog skips Annotate_rows events.
+5. mysqlbinlog output
+~~~~~~~~~~~~~~~~~~~~~
+With --print-annotate-rows-events, mysqlbinlog outputs Annotate_rows events
+in a form like this:
...
- # at 199
- # at 243
- # at 284
- #091129 9:57:24 server id 100 end_log_pos 243 Query: `insert into t1 values
-(1)`
- #091129 9:57:24 server id 100 end_log_pos 284 Table_map: `test`.`t1` mapped
-to number 15
- #091129 9:57:24 server id 100 end_log_pos 318 Write_rows: table id 15
+ # at 1646
+ #091219 12:45:26 server id 100 end_log_pos 1714 Query thread_id=1
+exec_time=0 error_code=0
+ SET TIMESTAMP=1261215926/*!*/;
+ BEGIN
+ /*!*/;
+ # at 1714
+ # at 1812
+ # at 1853
+ # at 1894
+ # at 1938
+ #091219 12:45:26 server id 100 end_log_pos 1812 Query: `DELETE t1, t2 FROM
+t1 INNER JOIN t2 INNER JOIN t3 WHERE t1.a=t2.a AND t2.a=t3.a`
+ #091219 12:45:26 server id 100 end_log_pos 1853 Table_map: `test`.`t1`
+mapped to number 16
+ #091219 12:45:26 server id 100 end_log_pos 1894 Table_map: `test`.`t2`
+mapped to number 17
+ #091219 12:45:26 server id 100 end_log_pos 1938 Delete_rows: table id 16
+ #091219 12:45:26 server id 100 end_log_pos 1982 Delete_rows: table id 17
flags: STMT_END_F
-
- BINLOG '
- VBsSSzNkAAAALAAAAPMAAAAAAGluc2VydCBpbnRvIHQxIHZhbHVlcyAoMSk=
- VBsSSxNkAAAAKQAAABwBAAAAAA8AAAAAAAAABHRlc3QAAnQxAAEDAAE=
- VBsSSxdkAAAAIgAAAD4BAAAQAA8AAAAAAAEAAf/+AQAAAA==
- '/*!*/;
- ### INSERT INTO test.t1
- ### SET
- ### @1=1 /* INT meta=0 nullable=1 is_null=0 */
...
-When master sends Annotate rows events
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-1. Master always sends Annotate_rows events to mysqlbinlog (in
- remote case).
-2. Master sends Annotate_rows events to a slave only if the slave has
- both log-slave-updates and binlog-annotate-rows-events options set.
-
------------------------------------------------------------
-=-=(View All Progress Notes, 22 total)=-=-
http://askmonty.org/worklog/index.pl?tid=47&nolimit=1
DESCRIPTION:
Store in binlog (and show in mysqlbinlog output) texts of statements that
caused RBR events
This is needed for (list from Monty):
- Easier to understand why updates happened
- Would make it easier to find out where in application things went
wrong (as you can search for exact strings)
- Allow one to filter things based on comments in the statement.
The cost of this can be that the binlog will be approximately 2x in size
(especially insert of big blob's would be a bit painful), so this should
be an optional feature.
HIGH-LEVEL SPECIFICATION:
Content
~~~~~~~
1. Annotate_rows_log_event
2. Server option: --binlog-annotate-rows-events
3. Server option: --replicate-annotate-rows-events
4. mysqlbinlog option: --print-annotate-rows-events
5. mysqlbinlog output
1. Annotate_rows_log_event [ ANNOTATE_ROWS_EVENT ]
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Describes the query which caused the corresponding rows events. Has empty
post-header and contains the query text in its data part. Example:
************************
ANNOTATE_ROWS_EVENT
************************
00000220 | B6 A0 2C 4B | time_when = 1261215926
00000224 | 33 | event_type = 51
00000225 | 64 00 00 00 | server_id = 100
00000229 | 36 00 00 00 | event_len = 54
0000022D | 56 02 00 00 | log_pos = 00000256
00000231 | 00 00 | flags = <none>
------------------------
00000233 | 49 4E 53 45 | query = "INSERT INTO t1 VALUES (1), (2), (3)"
00000237 | 52 54 20 49 |
0000023B | 4E 54 4F 20 |
0000023F | 74 31 20 56 |
00000243 | 41 4C 55 45 |
00000247 | 53 20 28 31 |
0000024B | 29 2C 20 28 |
0000024F | 32 29 2C 20 |
00000253 | 28 33 29 |
************************
In binary log, Annotate_rows event follows the (possible) 'BEGIN' Query event
and precedes the first of Table map events which accompany the corresponding
rows events. (See example in the "mysqlbinlog output" section below.)
2. Server option: --binlog-annotate-rows-events
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Tells the master to write Annotate_rows events to the binary log.
* Variable Name: binlog_annotate_rows_events
* Scope: Global & Session
* Access Type: Dynamic
* Data Type: bool
* Default Value: OFF
NOTE. Session values allows to annotate only some selected statements:
...
SET SESSION binlog_annotate_rows_events=ON;
... statements to be annotated ...
SET SESSION binlog_annotate_rows_events=OFF;
... statements not to be annotated ...
3. Server option: --replicate-annotate-rows-events
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Tells the slave to reproduce Annotate_rows events recieved from the master
in its own binary log (sensible only in pair with log-slave-updates option).
* Variable Name: replicate_annotate_rows_events
* Scope: Global
* Access Type: Read only
* Data Type: bool
* Default Value: OFF
NOTE. Why do we additionally need this 'replicate' option? Why not to make
the slave to reproduce this events when its binlog-annotate-rows-events
global value is ON? Well, because, for example, we may want to configure
the slave which should reproduce Annotate_rows events but has global
binlog-annotate-rows-events = OFF meaning this to be the default value for
the client threads (see also "How slave treats replicate-annotate-rows-events
option" in LLD part).
4. mysqlbinlog option: --print-annotate-rows-events
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
With this option, mysqlbinlog prints the content of Annotate_rows events (if
the binary log does contain them). Without this option (i.e. by default),
mysqlbinlog skips Annotate_rows events.
5. mysqlbinlog output
~~~~~~~~~~~~~~~~~~~~~
With --print-annotate-rows-events, mysqlbinlog outputs Annotate_rows events
in a form like this:
...
# at 1646
#091219 12:45:26 server id 100 end_log_pos 1714 Query thread_id=1
exec_time=0 error_code=0
SET TIMESTAMP=1261215926/*!*/;
BEGIN
/*!*/;
# at 1714
# at 1812
# at 1853
# at 1894
# at 1938
#091219 12:45:26 server id 100 end_log_pos 1812 Query: `DELETE t1, t2 FROM
t1 INNER JOIN t2 INNER JOIN t3 WHERE t1.a=t2.a AND t2.a=t3.a`
#091219 12:45:26 server id 100 end_log_pos 1853 Table_map: `test`.`t1`
mapped to number 16
#091219 12:45:26 server id 100 end_log_pos 1894 Table_map: `test`.`t2`
mapped to number 17
#091219 12:45:26 server id 100 end_log_pos 1938 Delete_rows: table id 16
#091219 12:45:26 server id 100 end_log_pos 1982 Delete_rows: table id 17
flags: STMT_END_F
...
LOW-LEVEL DESIGN:
Content
~~~~~~~
1. Annotate_rows event number
2. Outline of Annotate_rows event behavior
3. How Master writes Annotate_rows events to the binary log
4. How slave treats replicate-annotate-rows-events option
5. How slave IO thread requests Annotate_rows events
6. How master executes the request
7. How slave SQL thread processes Annotate_rows events
8. General remarks
1. Annotate_rows event number
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
To avoid possible event numbers conflict with MySQL/Sun, we leave a gap
between the last MySQL event number and the Annotate_rows event number:
enum Log_event_type
{ ...
INCIDENT_EVENT= 26,
// New MySQL event numbers are to be added here
MYSQL_EVENTS_END,
MARIA_EVENTS_BEGIN= 51,
// New Maria event numbers start from here
ANNOTATE_ROWS_EVENT= 51,
ENUM_END_EVENT
};
together with the corresponding extension of 'post_header_len' array in the
Format description event. (This extension does not affect the compatibility
of the binary log). Here is how Format description event looks like with
this extension:
************************
FORMAT_DESCRIPTION_EVENT
************************
00000004 | A1 A0 2C 4B | time_when = 1261215905
00000008 | 0F | event_type = 15
00000009 | 64 00 00 00 | server_id = 100
0000000D | 7F 00 00 00 | event_len = 127
00000011 | 83 00 00 00 | log_pos = 00000083
00000015 | 01 00 | flags = LOG_EVENT_BINLOG_IN_USE_F
------------------------
00000017 | 04 00 | binlog_ver = 4
00000019 | 35 2E 32 2E | server_ver = 5.2.0-MariaDB-alpha-debug-log
..... ...
0000004B | A1 A0 2C 4B | time_created = 1261215905
0000004F | 13 | common_header_len = 19
------------------------
post_header_len
------------------------
00000050 | 38 | 56 - START_EVENT_V3 [1]
..... ...
00000069 | 02 | 2 - INCIDENT_EVENT [26]
0000006A | 00 | 0 - RESERVED [27]
..... ...
00000081 | 00 | 0 - RESERVED [50]
00000082 | 00 | 0 - ANNOTATE_ROWS_EVENT [51]
************************
2. Outline of Annotate_rows event behavior
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Each Annotate_rows_log_event object has two private members describing the
corresponding query:
char *m_query_txt;
uint m_query_len;
When the object is created for writing to a binary log, this query is taken
from 'thd' (for short, below we omit the 'Annotate_rows_log_event::' prefix
as well as other implementation details):
Annotate_rows_log_event(THD *thd)
{
m_query_txt = thd->query();
m_query_len = thd->query_length();
}
When the object is read from a binary log, the query is taken from the buffer
containing the binary log representation of the event (this buffer is allocated
in Log_event object from which all Log events are derived):
Annotate_rows_log_event(char *buf, uint event_len,
Format_description_log_event *desc)
{
m_query_len = event_len - desc->common_header_len;
m_query_txt = buf + desc->common_header_len;
}
The events are written to the binary log by the Log_event::write() member
which calls virtual write_data_header() and write_data_body() members
("data header" and "post header" are synonym in replication terminology).
In our case, data header is empty and data body is just the query:
bool write_data_body(IO_CACHE *file)
{
return my_b_safe_write(file, (uchar*) m_query_txt, m_query_len);
}
Printing the event is just printing the query:
void Annotate_rows_log_event::print(FILE *file, PRINT_EVENT_INFO *pinfo)
{
my_b_printf(&pinfo->head_cache, "\tQuery: `%s`\n", m_query_txt);
}
3. How Master writes Annotate_rows events to the binary log
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
The event is written to the binary log just before the group of Table_map
events which precede corresponding Rows events (one query may generate
several Table map events in the binary log, but the corresponding
Annotate_rows event must be written only once before the first Table map
event; hence the boolean variable 'with_annotate' below):
int write_locked_table_maps(THD *thd)
{ ...
bool with_annotate= thd->variables.binlog_annotate_rows_events;
...
for (uint i= 0; i < ... <number of tables> ...; ++i)
{ ...
thd->binlog_write_table_map(table, ..., with_annotate);
with_annotate= 0; // write Annotate_event not more than once
...
}
...
}
int THD::binlog_write_table_map(TABLE *table, ..., bool with_annotate)
{ ...
Table_map_log_event the_event(...);
...
if (with_annotate)
{
Annotate_rows_log_event anno(this);
mysql_bin_log.write(&anno);
}
mysql_bin_log.write(&the_event);
...
}
4. How slave treats replicate-annotate-rows-events option
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
The replicate-annotate-rows-events option is treated just as the session
value of the binlog_annotate_rows_events variable for the slave IO and
SQL threads. This setting is done during initialization of these threads:
pthread_handler_t handle_slave_io(void *arg)
{
THD *thd= new THD;
...
init_slave_thread(thd, SLAVE_THD_IO);
...
}
pthread_handler_t handle_slave_sql(void *arg)
{
THD *thd= new THD;
...
init_slave_thread(thd, SLAVE_THD_SQL);
...
}
int init_slave_thread(THD* thd, SLAVE_THD_TYPE thd_type)
{ ...
thd->variables.binlog_annotate_rows_events=
opt_replicate_annotate_rows_events;
...
}
5. How slave IO thread requests Annotate_rows events
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
If the replicate-annotate-rows-events option is not set on a slave, there
is no need for master to send Annotate_rows events to this slave. The slave
(or mysqlbinlog in remote case), before requesting binlog dump via the
COM_BINLOG_DUMP command, informs the master whether it should send these
events by executing the newly added COM_BINLOG_DUMP_OPTIONS_EXT server
command:
case COM_BINLOG_DUMP_OPTIONS_EXT:
thd->binlog_dump_flags_ext= packet[0];
my_ok(thd);
break;
Note. We add this new command and don't use COM_BINLOG_DUMP to avoid possible
conflicts with MySQL/Sun.
6. How master executes the request
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
case COM_BINLOG_DUMP:
{ ...
flags= uint2korr(packet + 4);
...
mysql_binlog_send(thd, ..., flags);
...
}
void mysql_binlog_send(THD* thd, ..., ushort flags)
{ ...
Log_event::read_log_event(&log, packet, ...);
...
if ((*packet)[EVENT_TYPE_OFFSET + 1] != ANNOTATE_ROWS_EVENT ||
flags & BINLOG_SEND_ANNOTATE_ROWS_EVENT)
{
my_net_write(net, packet->ptr(), packet->length());
}
...
}
7. How slave SQL thread processes Annotate_rows events
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
The slave processes each recieved event by "applying" it, i.e. by
calling the Log_event::apply_event() function which in turn calls
the virtual do_apply_event() member specific for each type of the
event.
int exec_relay_log_event(THD* thd, Relay_log_info* rli)
{ ...
Log_event *ev = next_event(rli);
...
apply_event_and_update_pos(ev, ...);
if (ev->get_type_code() != FORMAT_DESCRIPTION_EVENT)
delete ev;
...
}
int apply_event_and_update_pos(Log_event *ev, ...)
{ ...
ev->apply_event(...);
...
}
int Log_event::apply_event(...)
{
return do_apply_event(...);
}
What does it mean to "apply" an Annotate_rows event? It means to set current
thd query to that of the described by the event, i.e. to the query which
caused the subsequent Rows events (see "How Master writes Annotate_rows
events to the binary log" to follow what happens further when the subsequent
Rows events are applied):
int Annotate_rows_log_event::do_apply_event(...)
{
thd->set_query(m_query_txt, m_query_len);
}
NOTE. I am not sure, but possibly current values of thd->query and
thd->query_length should be saved before calling set_query() and to be
restored on the Annotate_rows_log_event object deletion.
Is it really needed ?
After calling this do_apply_event() function we may not delete the
Annotate_rows_log_event object immediatedly (see exec_relay_log_event()
above) because thd->query now points to the string inside this object.
We may keep the pointer to this object in the Relay_log_info:
class Relay_log_info
{
public:
...
void set_annotate_event(Annotate_rows_log_event*);
Annotate_rows_log_event* get_annotate_event();
void free_annotate_event();
...
private:
Annotate_rows_log_event* m_annotate_event;
};
The saved Annotate_rows object should be deleted when all corresponding
Rows events will be processed:
int exec_relay_log_event(THD* thd, Relay_log_info* rli)
{ ...
Log_event *ev= next_event(rli);
...
apply_event_and_update_pos(ev, ...);
if (rli->get_annotate_event() && is_last_rows_event(ev))
rli->free_annotate_event();
else if (ev->get_type_code() == ANNOTATE_ROWS_EVENT)
rli->set_annotate_event((Annotate_rows_log_event*) ev);
else if (ev->get_type_code() != FORMAT_DESCRIPTION_EVENT)
delete ev;
...
}
where
bool is_last_rows_event(Log_event* ev)
{
Log_event_type type= ev->get_type_code();
if (IS_ROWS_EVENT_TYPE(type))
{
Rows_log_event* rows= (Rows_log_event*)ev;
return rows->get_flags(Rows_log_event::STMT_END_F);
}
return 0;
}
#define IS_ROWS_EVENT_TYPE(type) ((type) == WRITE_ROWS_EVENT || \
(type) == UPDATE_ROWS_EVENT || \
(type) == DELETE_ROWS_EVENT)
8. General remarks
~~~~~~~~~~~~~~~~~~
Kristian noticed that introducing new log event type should be coordinated
somehow with MySQL/Sun:
Kristian: The numeric code for this event must be assigned carefully.
It should be coordinated with MySQL/Sun, otherwise we can get into a
situation where MySQL uses the same numeric code for one event that
MariaDB uses for ANNOTATE_ROWS_EVENT, which would make merging the two
impossible.
Alex: I reserved about 20 numbers not to have possible conflicts
with MySQL.
Kristian: Still, I think it would be appropriate to send a polite email
to internals(a)lists.mysql.com about this and suggesting to reserve the
event number.
ESTIMATED WORK TIME
ESTIMATED COMPLETION DATE
-----------------------------------------------------------------------
WorkLog (v3.5.9)
1
0
[Maria-developers] FYI: askmonty.org/buildbot is moving to buildbot.askmonty.org/buildbot
by Daniel Bartholomew 04 Feb '10
by Daniel Bartholomew 04 Feb '10
04 Feb '10
All,
This is an FYI for everyone that uses our buildbot web interface:
Due to an upcoming server move I am in the process of moving our
buildbot web interface from http://askmonty.org/buildbot to
http://buildbot.askmonty.org/buildbot
Both of the above currently work (and point at the same data), but as
of Friday 5 Feb 2010 at approx 09:00 US Eastern time,
http://askmonty.org/buildbot will turn into a redirect. The redirect
will handle all of your old bookmarks (i.e. they should still work), but
I thought I should at least let everyone know so that you aren't
surprised by the move. I've already updated all of the links I could
find on the askmonty.org wiki.
And if you are wondering why I kept the /buildbot subdirectory in
place on the new subdomain, well, it was easier and safer to keep it in
place, so I did. If you connect directly to
http://buildbot.askmonty.org you will automatically be redirected to
the /buildbot subdir.
Thanks!
--
Daniel Bartholomew
Monty Program - http://askmonty.org
1
0