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 */
completion_handler(Handler & h)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.
do_complete(void * owner,operation * base,const asio::error_code &,std::size_t)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    */
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 
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    */
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    */
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    */
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   */
232   int allocate_block()
233   {
234     Block *block= (Block *)my_malloc(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    */
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    */
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 
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   }
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   }
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? */
440   bool is_on() {
441     return (m_state);
442   }
443 
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();
457   ~Repl_semi_sync_master() {}
458 
459   void cleanup();
460 
461   bool get_master_enabled() {
462     return m_master_enabled;
463   }
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. */
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*/
476   void set_wait_point(unsigned long ack_point)
477   {
478     m_wait_point = ack_point;
479   }
480 
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