1 /* Copyright (C) 2007 Google Inc. 2 Copyright (c) 2008 MySQL AB, 2009 Sun Microsystems, Inc. 3 Use is subject to license terms. 4 5 This program is free software; you can redistribute it and/or modify 6 it under the terms of the GNU General Public License as published by 7 the Free Software Foundation; version 2 of the License. 8 9 This program is distributed in the hope that it will be useful, 10 but WITHOUT ANY WARRANTY; without even the implied warranty of 11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 GNU General Public License for more details. 13 14 You should have received a copy of the GNU General Public License 15 along with this program; if not, write to the Free Software 16 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1335 USA */ 17 18 19 #ifndef SEMISYNC_MASTER_H 20 #define SEMISYNC_MASTER_H 21 22 #include "semisync.h" 23 #include "semisync_master_ack_receiver.h" 24 25 #ifdef HAVE_PSI_INTERFACE 26 extern PSI_mutex_key key_LOCK_rpl_semi_sync_master_enabled; 27 extern PSI_mutex_key key_LOCK_binlog; 28 extern PSI_cond_key key_COND_binlog_send; 29 #endif 30 31 struct Tranx_node { 32 char log_name[FN_REFLEN]; 33 my_off_t log_pos; 34 struct Tranx_node *next; /* the next node in the sorted list */ 35 struct Tranx_node *hash_next; /* the next node during hash collision */ 36 }; 37 38 /** 39 @class Tranx_node_allocator 40 41 This class provides memory allocating and freeing methods for 42 Tranx_node. The main target is performance. 43 44 @section ALLOCATE How to allocate a node 45 The pointer of the first node after 'last_node' in current_block is 46 returned. current_block will move to the next free Block when all nodes of 47 it are in use. A new Block is allocated and is put into the rear of the 48 Block link table if no Block is free. 49 50 The list starts up empty (ie, there is no allocated Block). 51 52 After some nodes are freed, there probably are some free nodes before 53 the sequence of the allocated nodes, but we do not reuse it. It is better 54 to keep the allocated nodes are in the sequence, for it is more efficient 55 for allocating and freeing Tranx_node. 56 57 @section FREENODE How to free nodes 58 There are two methods for freeing nodes. They are free_all_nodes and 59 free_nodes_before. 60 61 'A Block is free' means all of its nodes are free. 62 @subsection free_nodes_before 63 As all allocated nodes are in the sequence, 'Before one node' means all 64 nodes before given node in the same Block and all Blocks before the Block 65 which containing the given node. As such, all Blocks before the given one 66 ('node') are free Block and moved into the rear of the Block link table. 67 The Block containing the given 'node', however, is not. For at least the 68 given 'node' is still in use. This will waste at most one Block, but it is 69 more efficient. 70 */ 71 #define BLOCK_TRANX_NODES 16 72 class Tranx_node_allocator 73 { 74 public: 75 /** 76 @param reserved_nodes 77 The number of reserved Tranx_nodes. It is used to set 'reserved_blocks' 78 which can contain at least 'reserved_nodes' number of Tranx_nodes. When 79 freeing memory, we will reserve at least reserved_blocks of Blocks not 80 freed. 81 */ Tranx_node_allocator(uint reserved_nodes)82 Tranx_node_allocator(uint reserved_nodes) : 83 reserved_blocks(reserved_nodes/BLOCK_TRANX_NODES + 84 (reserved_nodes%BLOCK_TRANX_NODES > 1 ? 2 : 1)), 85 first_block(NULL), last_block(NULL), 86 current_block(NULL), last_node(-1), block_num(0) {} 87 ~Tranx_node_allocator()88 ~Tranx_node_allocator() 89 { 90 Block *block= first_block; 91 while (block != NULL) 92 { 93 Block *next= block->next; 94 free_block(block); 95 block= next; 96 } 97 } 98 99 /** 100 The pointer of the first node after 'last_node' in current_block is 101 returned. current_block will move to the next free Block when all nodes of 102 it are in use. A new Block is allocated and is put into the rear of the 103 Block link table if no Block is free. 104 105 @return Return a Tranx_node *, or NULL if an error occurred. 106 */ allocate_node()107 Tranx_node *allocate_node() 108 { 109 Tranx_node *trx_node; 110 Block *block= current_block; 111 112 if (last_node == BLOCK_TRANX_NODES-1) 113 { 114 current_block= current_block->next; 115 last_node= -1; 116 } 117 118 if (current_block == NULL && allocate_block()) 119 { 120 current_block= block; 121 if (current_block) 122 last_node= BLOCK_TRANX_NODES-1; 123 return NULL; 124 } 125 126 trx_node= &(current_block->nodes[++last_node]); 127 trx_node->log_name[0] = '\0'; 128 trx_node->log_pos= 0; 129 trx_node->next= 0; 130 trx_node->hash_next= 0; 131 return trx_node; 132 } 133 134 /** 135 All nodes are freed. 136 137 @return Return 0, or 1 if an error occurred. 138 */ free_all_nodes()139 int free_all_nodes() 140 { 141 current_block= first_block; 142 last_node= -1; 143 free_blocks(); 144 return 0; 145 } 146 147 /** 148 All Blocks before the given 'node' are free Block and moved into the rear 149 of the Block link table. 150 151 @param node All nodes before 'node' will be freed 152 153 @return Return 0, or 1 if an error occurred. 154 */ free_nodes_before(Tranx_node * node)155 int free_nodes_before(Tranx_node* node) 156 { 157 Block *block; 158 Block *prev_block= NULL; 159 160 block= first_block; 161 while (block != current_block->next) 162 { 163 /* Find the Block containing the given node */ 164 if (&(block->nodes[0]) <= node && &(block->nodes[BLOCK_TRANX_NODES]) >= node) 165 { 166 /* All Blocks before the given node are put into the rear */ 167 if (first_block != block) 168 { 169 last_block->next= first_block; 170 first_block= block; 171 last_block= prev_block; 172 last_block->next= NULL; 173 free_blocks(); 174 } 175 return 0; 176 } 177 prev_block= block; 178 block= block->next; 179 } 180 181 /* Node does not find should never happen */ 182 DBUG_ASSERT(0); 183 return 1; 184 } 185 186 private: 187 uint reserved_blocks; 188 189 /** 190 A sequence memory which contains BLOCK_TRANX_NODES Tranx_nodes. 191 192 BLOCK_TRANX_NODES The number of Tranx_nodes which are in a Block. 193 194 next Every Block has a 'next' pointer which points to the next Block. 195 These linking Blocks constitute a Block link table. 196 */ 197 struct Block { 198 Block *next; 199 Tranx_node nodes[BLOCK_TRANX_NODES]; 200 }; 201 202 /** 203 The 'first_block' is the head of the Block link table; 204 */ 205 Block *first_block; 206 /** 207 The 'last_block' is the rear of the Block link table; 208 */ 209 Block *last_block; 210 211 /** 212 current_block always points the Block in the Block link table in 213 which the last allocated node is. The Blocks before it are all in use 214 and the Blocks after it are all free. 215 */ 216 Block *current_block; 217 218 /** 219 It always points to the last node which has been allocated in the 220 current_block. 221 */ 222 int last_node; 223 224 /** 225 How many Blocks are in the Block link table. 226 */ 227 uint block_num; 228 229 /** 230 Allocate a block and then assign it to current_block. 231 */ allocate_block()232 int allocate_block() 233 { 234 Block *block= (Block *)my_malloc(PSI_INSTRUMENT_ME, sizeof(Block), MYF(0)); 235 if (block) 236 { 237 block->next= NULL; 238 239 if (first_block == NULL) 240 first_block= block; 241 else 242 last_block->next= block; 243 244 /* New Block is always put into the rear */ 245 last_block= block; 246 /* New Block is always the current_block */ 247 current_block= block; 248 ++block_num; 249 return 0; 250 } 251 return 1; 252 } 253 254 /** 255 Free a given Block. 256 @param block The Block will be freed. 257 */ free_block(Block * block)258 void free_block(Block *block) 259 { 260 my_free(block); 261 --block_num; 262 } 263 264 265 /** 266 If there are some free Blocks and the total number of the Blocks in the 267 Block link table is larger than the 'reserved_blocks', Some free Blocks 268 will be freed until the total number of the Blocks is equal to the 269 'reserved_blocks' or there is only one free Block behind the 270 'current_block'. 271 */ free_blocks()272 void free_blocks() 273 { 274 if (current_block == NULL || current_block->next == NULL) 275 return; 276 277 /* One free Block is always kept behind the current block */ 278 Block *block= current_block->next->next; 279 while (block_num > reserved_blocks && block != NULL) 280 { 281 Block *next= block->next; 282 free_block(block); 283 block= next; 284 } 285 current_block->next->next= block; 286 if (block == NULL) 287 last_block= current_block->next; 288 } 289 }; 290 291 /** 292 This class manages memory for active transaction list. 293 294 We record each active transaction with a Tranx_node, each session 295 can have only one open transaction. Because of EVENT, the total 296 active transaction nodes can exceed the maximum allowed 297 connections. 298 */ 299 class Active_tranx 300 :public Trace { 301 private: 302 303 Tranx_node_allocator m_allocator; 304 /* These two record the active transaction list in sort order. */ 305 Tranx_node *m_trx_front, *m_trx_rear; 306 307 Tranx_node **m_trx_htb; /* A hash table on active transactions. */ 308 309 int m_num_entries; /* maximum hash table entries */ 310 mysql_mutex_t *m_lock; /* mutex lock */ 311 312 inline void assert_lock_owner(); 313 314 inline unsigned int calc_hash(const unsigned char *key, size_t length); 315 unsigned int get_hash_value(const char *log_file_name, my_off_t log_file_pos); 316 compare(const char * log_file_name1,my_off_t log_file_pos1,const Tranx_node * node2)317 int compare(const char *log_file_name1, my_off_t log_file_pos1, 318 const Tranx_node *node2) { 319 return compare(log_file_name1, log_file_pos1, 320 node2->log_name, node2->log_pos); 321 } compare(const Tranx_node * node1,const char * log_file_name2,my_off_t log_file_pos2)322 int compare(const Tranx_node *node1, 323 const char *log_file_name2, my_off_t log_file_pos2) { 324 return compare(node1->log_name, node1->log_pos, 325 log_file_name2, log_file_pos2); 326 } compare(const Tranx_node * node1,const Tranx_node * node2)327 int compare(const Tranx_node *node1, const Tranx_node *node2) { 328 return compare(node1->log_name, node1->log_pos, 329 node2->log_name, node2->log_pos); 330 } 331 332 public: 333 Active_tranx(mysql_mutex_t *lock, unsigned long trace_level); 334 ~Active_tranx(); 335 336 /* Insert an active transaction node with the specified position. 337 * 338 * Return: 339 * 0: success; non-zero: error 340 */ 341 int insert_tranx_node(const char *log_file_name, my_off_t log_file_pos); 342 343 /* Clear the active transaction nodes until(inclusive) the specified 344 * position. 345 * If log_file_name is NULL, everything will be cleared: the sorted 346 * list and the hash table will be reset to empty. 347 */ 348 void clear_active_tranx_nodes(const char *log_file_name, 349 my_off_t log_file_pos); 350 351 /* Given a position, check to see whether the position is an active 352 * transaction's ending position by probing the hash table. 353 */ 354 bool is_tranx_end_pos(const char *log_file_name, my_off_t log_file_pos); 355 356 /* Given two binlog positions, compare which one is bigger based on 357 * (file_name, file_position). 358 */ 359 static int compare(const char *log_file_name1, my_off_t log_file_pos1, 360 const char *log_file_name2, my_off_t log_file_pos2); 361 362 }; 363 364 /** 365 The extension class for the master of semi-synchronous replication 366 */ 367 class Repl_semi_sync_master 368 :public Repl_semi_sync_base { 369 Active_tranx *m_active_tranxs; /* active transaction list: the list will 370 be cleared when semi-sync switches off. */ 371 372 /* True when init_object has been called */ 373 bool m_init_done; 374 375 /* This cond variable is signaled when enough binlog has been sent to slave, 376 * so that a waiting trx can return the 'ok' to the client for a commit. 377 */ 378 mysql_cond_t COND_binlog_send; 379 380 /* Mutex that protects the following state variables and the active 381 * transaction list. 382 * Under no cirumstances we can acquire mysql_bin_log.LOCK_log if we are 383 * already holding m_LOCK_binlog because it can cause deadlocks. 384 */ 385 mysql_mutex_t LOCK_binlog; 386 387 /* This is set to true when m_reply_file_name contains meaningful data. */ 388 bool m_reply_file_name_inited; 389 390 /* The binlog name up to which we have received replies from any slaves. */ 391 char m_reply_file_name[FN_REFLEN]; 392 393 /* The position in that file up to which we have the reply from any slaves. */ 394 my_off_t m_reply_file_pos; 395 396 /* This is set to true when we know the 'smallest' wait position. */ 397 bool m_wait_file_name_inited; 398 399 /* NULL, or the 'smallest' filename that a transaction is waiting for 400 * slave replies. 401 */ 402 char m_wait_file_name[FN_REFLEN]; 403 404 /* The smallest position in that file that a trx is waiting for: the trx 405 * can proceed and send an 'ok' to the client when the master has got the 406 * reply from the slave indicating that it already got the binlog events. 407 */ 408 my_off_t m_wait_file_pos; 409 410 /* This is set to true when we know the 'largest' transaction commit 411 * position in the binlog file. 412 * We always maintain the position no matter whether semi-sync is switched 413 * on switched off. When a transaction wait timeout occurs, semi-sync will 414 * switch off. Binlog-dump thread can use the three fields to detect when 415 * slaves catch up on replication so that semi-sync can switch on again. 416 */ 417 bool m_commit_file_name_inited; 418 419 /* The 'largest' binlog filename that a commit transaction is seeing. */ 420 char m_commit_file_name[FN_REFLEN]; 421 422 /* The 'largest' position in that file that a commit transaction is seeing. */ 423 my_off_t m_commit_file_pos; 424 425 /* All global variables which can be set by parameters. */ 426 volatile bool m_master_enabled; /* semi-sync is enabled on the master */ 427 unsigned long m_wait_timeout; /* timeout period(ms) during tranx wait */ 428 429 bool m_state; /* whether semi-sync is switched */ 430 431 /*Waiting for ACK before/after innodb commit*/ 432 ulong m_wait_point; 433 434 void lock(); 435 void unlock(); 436 void cond_broadcast(); 437 int cond_timewait(struct timespec *wait_time); 438 439 /* Is semi-sync replication on? */ is_on()440 bool is_on() { 441 return (m_state); 442 } 443 set_master_enabled(bool enabled)444 void set_master_enabled(bool enabled) { 445 m_master_enabled = enabled; 446 } 447 448 /* Switch semi-sync off because of timeout in transaction waiting. */ 449 void switch_off(); 450 451 /* Switch semi-sync on when slaves catch up. */ 452 int try_switch_on(int server_id, 453 const char *log_file_name, my_off_t log_file_pos); 454 455 public: 456 Repl_semi_sync_master(); ~Repl_semi_sync_master()457 ~Repl_semi_sync_master() {} 458 459 void cleanup(); 460 get_master_enabled()461 bool get_master_enabled() { 462 return m_master_enabled; 463 } set_trace_level(unsigned long trace_level)464 void set_trace_level(unsigned long trace_level) { 465 m_trace_level = trace_level; 466 if (m_active_tranxs) 467 m_active_tranxs->m_trace_level = trace_level; 468 } 469 470 /* Set the transaction wait timeout period, in milliseconds. */ set_wait_timeout(unsigned long wait_timeout)471 void set_wait_timeout(unsigned long wait_timeout) { 472 m_wait_timeout = wait_timeout; 473 } 474 475 /*set the ACK point, after binlog sync or after transaction commit*/ set_wait_point(unsigned long ack_point)476 void set_wait_point(unsigned long ack_point) 477 { 478 m_wait_point = ack_point; 479 } 480 wait_point()481 ulong wait_point() //no cover line 482 { 483 return m_wait_point; //no cover line 484 } 485 486 /* Initialize this class after MySQL parameters are initialized. this 487 * function should be called once at bootstrap time. 488 */ 489 int init_object(); 490 491 /* Enable the object to enable semi-sync replication inside the master. */ 492 int enable_master(); 493 494 /* Disable the object to disable semi-sync replication inside the master. */ 495 void disable_master(); 496 497 /* Add a semi-sync replication slave */ 498 void add_slave(); 499 500 /* Remove a semi-sync replication slave */ 501 void remove_slave(); 502 503 /* It parses a reply packet and call report_reply_binlog to handle it. */ 504 int report_reply_packet(uint32 server_id, const uchar *packet, 505 ulong packet_len); 506 507 /* In semi-sync replication, reports up to which binlog position we have 508 * received replies from the slave indicating that it already get the events. 509 * 510 * Input: 511 * server_id - (IN) master server id number 512 * log_file_name - (IN) binlog file name 513 * end_offset - (IN) the offset in the binlog file up to which we have 514 * the replies from the slave 515 * 516 * Return: 517 * 0: success; non-zero: error 518 */ 519 int report_reply_binlog(uint32 server_id, 520 const char* log_file_name, 521 my_off_t end_offset); 522 523 /* Commit a transaction in the final step. This function is called from 524 * InnoDB before returning from the low commit. If semi-sync is switch on, 525 * the function will wait to see whether binlog-dump thread get the reply for 526 * the events of the transaction. Remember that this is not a direct wait, 527 * instead, it waits to see whether the binlog-dump thread has reached the 528 * point. If the wait times out, semi-sync status will be switched off and 529 * all other transaction would not wait either. 530 * 531 * Input: (the transaction events' ending binlog position) 532 * trx_wait_binlog_name - (IN) ending position's file name 533 * trx_wait_binlog_pos - (IN) ending position's file offset 534 * 535 * Return: 536 * 0: success; non-zero: error 537 */ 538 int commit_trx(const char* trx_wait_binlog_name, 539 my_off_t trx_wait_binlog_pos); 540 541 /*Wait for ACK after writing/sync binlog to file*/ 542 int wait_after_sync(const char* log_file, my_off_t log_pos); 543 544 /*Wait for ACK after commting the transaction*/ 545 int wait_after_commit(THD* thd, bool all); 546 547 /*Wait after the transaction is rollback*/ 548 int wait_after_rollback(THD *thd, bool all); 549 /*Store the current binlog position in m_active_tranxs. This position should 550 * be acked by slave*/ 551 int report_binlog_update(THD *thd, const char *log_file,my_off_t log_pos); 552 553 int dump_start(THD* thd, 554 const char *log_file, 555 my_off_t log_pos); 556 557 void dump_end(THD* thd); 558 559 /* Reserve space in the replication event packet header: 560 * . slave semi-sync off: 1 byte - (0) 561 * . slave semi-sync on: 3 byte - (0, 0xef, 0/1} 562 * 563 * Input: 564 * packet - (IN) the header buffer 565 * 566 * Return: 567 * size of the bytes reserved for header 568 */ 569 int reserve_sync_header(String* packet); 570 571 /* Update the sync bit in the packet header to indicate to the slave whether 572 * the master will wait for the reply of the event. If semi-sync is switched 573 * off and we detect that the slave is catching up, we switch semi-sync on. 574 * 575 * Input: 576 * THD - (IN) current dump thread 577 * packet - (IN) the packet containing the replication event 578 * log_file_name - (IN) the event ending position's file name 579 * log_file_pos - (IN) the event ending position's file offset 580 * need_sync - (IN) identify if flush_net is needed to call. 581 * server_id - (IN) master server id number 582 * 583 * Return: 584 * 0: success; non-zero: error 585 */ 586 int update_sync_header(THD* thd, unsigned char *packet, 587 const char *log_file_name, 588 my_off_t log_file_pos, 589 bool* need_sync); 590 591 /* Called when a transaction finished writing binlog events. 592 * . update the 'largest' transactions' binlog event position 593 * . insert the ending position in the active transaction list if 594 * semi-sync is on 595 * 596 * Input: (the transaction events' ending binlog position) 597 * log_file_name - (IN) transaction ending position's file name 598 * log_file_pos - (IN) transaction ending position's file offset 599 * 600 * Return: 601 * 0: success; non-zero: error 602 */ 603 int write_tranx_in_binlog(const char* log_file_name, my_off_t log_file_pos); 604 605 /* Read the slave's reply so that we know how much progress the slave makes 606 * on receive replication events. 607 */ 608 int flush_net(THD* thd, const char *event_buf); 609 610 /* Export internal statistics for semi-sync replication. */ 611 void set_export_stats(); 612 613 /* 'reset master' command is issued from the user and semi-sync need to 614 * go off for that. 615 */ 616 int after_reset_master(); 617 618 /*called before reset master*/ 619 int before_reset_master(); 620 621 void check_and_switch(); 622 623 mysql_mutex_t LOCK_rpl_semi_sync_master_enabled; 624 }; 625 626 enum rpl_semi_sync_master_wait_point_t { 627 SEMI_SYNC_MASTER_WAIT_POINT_AFTER_BINLOG_SYNC, 628 SEMI_SYNC_MASTER_WAIT_POINT_AFTER_STORAGE_COMMIT, 629 }; 630 631 extern Repl_semi_sync_master repl_semisync_master; 632 extern Ack_receiver ack_receiver; 633 634 /* System and status variables for the master component */ 635 extern my_bool rpl_semi_sync_master_enabled; 636 extern my_bool rpl_semi_sync_master_status; 637 extern ulong rpl_semi_sync_master_wait_point; 638 extern ulong rpl_semi_sync_master_clients; 639 extern ulong rpl_semi_sync_master_timeout; 640 extern ulong rpl_semi_sync_master_trace_level; 641 extern ulong rpl_semi_sync_master_yes_transactions; 642 extern ulong rpl_semi_sync_master_no_transactions; 643 extern ulong rpl_semi_sync_master_off_times; 644 extern ulong rpl_semi_sync_master_wait_timeouts; 645 extern ulong rpl_semi_sync_master_timefunc_fails; 646 extern ulong rpl_semi_sync_master_num_timeouts; 647 extern ulong rpl_semi_sync_master_wait_sessions; 648 extern ulong rpl_semi_sync_master_wait_pos_backtraverse; 649 extern ulong rpl_semi_sync_master_avg_trx_wait_time; 650 extern ulong rpl_semi_sync_master_avg_net_wait_time; 651 extern ulonglong rpl_semi_sync_master_net_wait_num; 652 extern ulonglong rpl_semi_sync_master_trx_wait_num; 653 extern ulonglong rpl_semi_sync_master_net_wait_time; 654 extern ulonglong rpl_semi_sync_master_trx_wait_time; 655 extern unsigned long long rpl_semi_sync_master_request_ack; 656 extern unsigned long long rpl_semi_sync_master_get_ack; 657 658 /* 659 This indicates whether we should keep waiting if no semi-sync slave 660 is available. 661 0 : stop waiting if detected no avaialable semi-sync slave. 662 1 (default) : keep waiting until timeout even no available semi-sync slave. 663 */ 664 extern char rpl_semi_sync_master_wait_no_slave; 665 extern Repl_semi_sync_master repl_semisync_master; 666 667 extern PSI_stage_info stage_waiting_for_semi_sync_ack_from_slave; 668 extern PSI_stage_info stage_reading_semi_sync_ack; 669 extern PSI_stage_info stage_waiting_for_semi_sync_slave; 670 671 void semi_sync_master_deinit(); 672 673 #endif /* SEMISYNC_MASTER_H */ 674