MySQL Plugin implementation--plug-in invocation

Source: Internet
Author: User
Tags ack

First, the dynamic plugin in MySQL

When I first thought about the problem of MySQL semi-synchronous replication, why did the MySQL runtime install the semi-synchronous plug-in and turn it on, and how did he make the transaction stop and wait. When installing the plug-in, a. So dynamic library is loaded, which is the implementation of the plugin. Then the MySQL source code should need the corresponding framework to invoke these plugins, how does this framework work?

Second, find the answer from the source code

First, we need to know where the plug-in is called from. As an example of a semi-synchronous plug-in: It is well known that by setting the Rpl_semi_sync_master_wait_point value after turning on the half-sync plugin, you can determine where the MySQL main library waits for an ACK from the library, and After_sync after the Binlog is flushed to disk. The After_commit is after the transaction is committed. Then after the MySQL transaction refreshes to Binlog and after the transaction commits, it should be necessary to have one place to invoke the semi-synchronous plug-in interface to wait.

First, enter the MySQL transaction commit function, and when the Binlog is turned on, MySQL is submitted by mysql_bin_log::ordered_commit. To ensure that data is not lost, we usually use the After_sync sync point, if you are looking for a call in this sync point semi-synchronous plug-in, then you need to refresh Binlog in this function and look for it before the transaction is formally committed. In fact, after refreshing the binlog, you can find such a function call:

int MYSQL_BIN_LOG::ordered_commit(THD *thd, bool all, bool skip_commit){  ···  if (flush_error == 0 && sync_error == 0)      sync_error= call_after_sync_hook(commit_queue);  ···}

It seems that the function name is related to the semi-synchronization, so enter the function:

static inline int call_after_sync_hook(THD *queue_head){  const char *log_file= NULL;  my_off_t pos= 0;  if (NO_HOOK(binlog_storage))    return 0;  DBUG_ASSERT(queue_head != NULL);  for (THD *thd= queue_head; thd != NULL; thd= thd->next_to_commit)    if (likely(thd->commit_error == THD::CE_NONE))      thd->get_trans_fixed_pos(&log_file, &pos);  if (DBUG_EVALUATE_IF("simulate_after_sync_hook_error", 1, 0) ||      RUN_HOOK(binlog_storage, after_sync, (queue_head, log_file, pos)))  {    sql_print_error("Failed to run ‘after_sync‘ hooks");    return ER_ERROR_ON_WRITE;  }  return 0;}

This function takes a queue of threads to be submitted as parameters and lets those threads wait for an ACK from the library, so what is the specific invocation? This is the Run_hook:

RUN_HOOK(binlog_storage, after_sync, (queue_head, log_file, pos))

View Run_hook Source Discovery This is a macro:

#define RUN_HOOK(group, hook, args)               (group ##_delegate->is_empty() ?                 0 : group ##_delegate->hook args)

Replace the above call with a literal substitution of the macro definition to get the actual call:

binlog_storage_delegate->is_empty() ? 0 : binlog_storage_delegate->after_sync(queue_head, log_file, pos)

That is, it is actually called the function of Binlog_storage_delegate point object to achieve the function of semi-synchronous, if the return value of Is_empty () is true then do nothing, otherwise call after_sync function to wait. Find Binlog_storage_delegate the definition of this pointer, you can find it in rpl_handler.cc, which is a global pointer variable, type Binlog_storage_delegate *:

//rpl_handler.ccBinlog_storage_delegate *binlog_storage_delegate;

Further find out where this variable is initialized can find the Delegates_init function:

int delegates_init(){  ······  transaction_delegate= new (place_trans_mem) Trans_delegate;  if (!transaction_delegate->is_inited())  {    sql_print_error("Initialization of transaction delegates failed. "                    "Please report a bug.");    return 1;  }  binlog_storage_delegate= new (place_storage_mem) Binlog_storage_delegate;  if (!binlog_storage_delegate->is_inited())  {    sql_print_error("Initialization binlog storage delegates failed. "                    "Please report a bug.");    return 1;  }  server_state_delegate= new (place_state_mem) Server_state_delegate;  ······}

This function specifically initializes a variety of xxx_delegate types of pointers, assigns them objects, and actually continues to trace the call stack to find the following function call

mysqld_main()    |    |init_server_components()    |    |delegates_init()

So it can be seen that these pointers are actually initialized when MySQL is started. Next, the synchronous wait point of the semi-synchronous plug-in is still used as an example to see how the call is implemented.

Third, the observer pattern in the plugin

Still with the above waiting for the call from the library ACK as an example, already know that the Binlog_storage_delegate class is actually implemented, then look at the Binlog_storage_delegate code:

class Binlog_storage_delegate  :public Delegate {public:  Binlog_storage_delegate()  : Delegate(#ifdef HAVE_PSI_INTERFACE             key_rwlock_Binlog_storage_delegate_lock#endif             )  {}  typedef Binlog_storage_observer Observer;  int after_flush(THD *thd, const char *log_file,                  my_off_t log_pos);  int after_sync(THD *thd, const char *log_file,                 my_off_t log_pos);};

There is a after_sync function in this class, that is, this function is actually called by the run_hook above. Binlog_storage_delegate inherits a base class delegate:

Class Delegate {public:typedef list<observer_info> observer_info_list;  typedef list_iterator<observer_info> Observer_info_iterator;  int add_observer (void *observer, St_plugin_int *plugin) {...  } int remove_observer (void *observer, St_plugin_int *plugin) {...  } inline Observer_info_iterator Observer_info_iter () {return observer_info_iterator (observer_info_list);    } inline bool Is_empty () {dbug_print ("Debug", ("Is_empty:%d", Observer_info_list.is_empty ()));  return Observer_info_list.is_empty ();    } inline int Read_lock () {if (!inited) return TRUE;  Return Mysql_rwlock_rdlock (&lock);    } inline int Write_lock () {if (!inited) return TRUE;  Return Mysql_rwlock_wrlock (&lock);    } inline int unlock () {if (!inited) return TRUE;  Return Mysql_rwlock_unlock (&lock);  } inline bool Is_inited () {return inited; } Delegate (#ifdef have_psi_interface psi_rwlock_key key#endif) {   inited= FALSE; #ifdef have_psi_interface if (Mysql_rwlock_init (key, &lock)) return; #else if (mysql_rwlock    _init (0, &lock)) return, #endif init_sql_alloc (key_memory_delegate, &memroot, 1024, 0);  Inited= TRUE;    } ~delegate () {inited= FALSE;    Mysql_rwlock_destroy (&lock);  Free_root (&memroot, MYF (0));  }private:observer_info_list observer_info_list;  mysql_rwlock_t lock;  Mem_root Memroot; bool inited;};

The details of some functions are omitted, and the main members are first seen. Observer_info_list is a linked list that stores members of the Observer_info type, while also having a read-write lock lock for protection. The other member functions are functions that manipulate the list (for example, add_observer to add members) and lock-unlock. Notice that there is a is_empty function, which is used to determine whether the linked list is empty. The is_empty called in Run_hook is the function.

Then go back to the subclass Binlog_storage_delegate, and we already know that After_sync's wait is actually done by Binlog_storage_delegate's After_sync function. Next go to the source code of this function and see how this function is implemented

int Binlog_storage_delegate::after_sync(THD *thd,                                        const char *log_file,                                        my_off_t log_pos){  DBUG_ENTER("Binlog_storage_delegate::after_sync");  DBUG_PRINT("enter", ("log_file: %s, log_pos: %llu",                       log_file, (ulonglong) log_pos));  Binlog_storage_param param;  param.server_id= thd->server_id;  DBUG_ASSERT(log_pos != 0);  int ret= 0;  FOREACH_OBSERVER(ret, after_sync, thd, (&param, log_file, log_pos));  DEBUG_SYNC(thd, "after_call_after_sync_observer");  DBUG_RETURN(ret);}

Except for the various debugging information, this function actually just calls the Foreach_observer macro:

FOREACH_OBSERVER(ret, after_sync, thd, (&param, log_file, log_pos));

Continue to view the definition of this macro:

#define FOREACH_OBSERVER (R, F, THD, args)/* Use a struct to make sure that they is a  llocated adjacent, check delete_dynamic (). */Prealloced_array<plugin_ref, 8> plugins (psi_          not_instrumented);                                                            Read_lock ();                      Observer_info_iterator iter= Observer_info_iter ();                                            Observer_info *info= iter++;                                                                         for (; info; info= iter++) { Plugin_ref plugin= my_plugin_lock (0, &i                                     Nfo->plugin);                                                                         if (!plugin) { /* Plugin is not intiAlized or deleted, this isn't an error */R= 0;                                                                Break                                              } plugins.push_back (plugin); if ((Observer *) info->observer)->f && ((obse                                                                         RVer *) info->observer)->f args) {                                                                   R= 1;                             Sql_print_error ("Run function" #f "' in plugin '%s ' failed", INFO-&GT;PLUGIN_INT-&GT;NAME.STR);                                                                Break                                                              }                                                                     }         Unlock (); /* Unlock plugins should be do after we released the Delegate lock to avoid possible deadlock if this is the L AST user of the plugin, and when we unlock the plugin, it'll try to deinitialize the plugin, which would try to L  Ock the Delegate in order to remove the observers.                                                     */if (!plugins.empty ()) Plugin_unlock_list (0, &plugins[0], plugins.size ());

This macro is long, we first ignore the other code for the plug-in management, focus on traversing the list of the For loop. We'll simplify this loop:

Observer_info_iterator iter= Observer_info_iter ();                                            Observer_info *info= iter++;                                                     for (; info; info= iter++) {                                                                      ······ if ((Observer *) info->observer)->f && ((Observer *) Info->observe                                                                   R)->f args) {r= 1; Sql_print_error ("Run function" "#f" ' in plugin '%s ' FA                            Iled ", INFO-&GT;PLUGIN_INT-&GT;NAME.STR);                                                                 Break                                                   }                                                                      }                     ······ 

As you can see, this for loop traverses the entire list, takes a pointer to each element and converts it to a pointer of type observer, and then calls the F member function with the passed args as a parameter. We can still replace the macro with a literal, with the foreach_observer called in Binlog_storage_delegate::after_sync as an example, the external call is:

FOREACH_OBSERVER(ret, after_sync, thd, (&param, log_file, log_pos));

In the internal for loop, a call encapsulated with each element's pointer can be translated as:

if (((Observer *)info->observer)->after_sync &&     ((Observer *)info->observer)->after_sync ((&param, log_file, log_pos)))

Assuming that the After_sync function member exists for the object pointed to by the info pointer, it is called with args as a parameter to the function. So that means that binlog_storage_delegate::after_sync is actually used to call the member function of the after_sync of each observer object stored in its linked list. While the action waiting for an ACK in a semi-synchronous plug-in is actually further implemented by the Observer object, you can see its definition in the Binlog_storage_delegate class:

typedef Binlog_storage_observer Observer;

The reason for this definition here is to foreach_observer this macro to use the Observer name uniformly. Then it is actually called the Binlog_storage_observer member function. Continue to view the definition of binlog_storage_observer:

typedef struct Binlog_storage_observer {  uint32 len;  /**     This callback is called after binlog has been flushed     This callback is called after cached events have been flushed to     binary log file but not yet synced.     @param param Observer common parameter     @param log_file Binlog file name been updated     @param log_pos Binlog position after update     @retval 0 Sucess     @retval 1 Failure  */  int (*after_flush)(Binlog_storage_param *param,                     const char *log_file, my_off_t log_pos);  int (*after_sync)(Binlog_storage_param *param,                     const char *log_file, my_off_t log_pos);} Binlog_storage_observer;/**   Binlog storage observer parameters */typedef struct Binlog_storage_param {  uint32 server_id;} Binlog_storage_param;

As can be seen from the above, Binlog_storage_observer is actually a class containing two function pointers, the final implementation of the wait from the library ACK is the inside of the function pointer.

So the question then becomes exactly where does the binlog_storage_observer instance from Binlog_storage_delegate come from? Which function does the function pointer in Binlog_storage_observer point to exactly?

Back in the Binlog_storage_delegate class, this class has a Add_observer function to add objects to the list, which should be called to add. Find a call to it to find the add Binlog_storage_observer to the list of functions Register_binlog_storage_observer:

//rpl_handlerint register_binlog_storage_observer(Binlog_storage_observer *observer, void *p){  DBUG_ENTER("register_binlog_storage_observer");  int result= binlog_storage_delegate->add_observer(observer, (st_plugin_int *)p);  DBUG_RETURN(result);}

and add to the observer is also register_binlog_storage_observer parameters, then follow the call chain to continue to look up, and finally can be found in the source of the semi-synchronous plug-in Semi_sync_master_plugin_ init function:

static int semi_sync_master_plugin_init(void *p){#ifdef HAVE_PSI_INTERFACE  init_semisync_psi_keys();#endif  my_create_thread_local_key(&THR_RPL_SEMI_SYNC_DUMP, NULL);  if (repl_semisync.initObject())    return 1;  if (ack_receiver.init())    return 1;  if (register_trans_observer(&trans_observer, p))    return 1;  if (register_binlog_storage_observer(&storage_observer, p))    return 1;  if (register_binlog_transmit_observer(&transmit_observer, p))    return 1;  return 0;}

This function registers the various xxxx_obeserver of the semi-synchronous plug-in and adds it to the Xxxx_delegate list for invocation. Still concerned about our previous binlog_storage_observe, the Register_binlog_storage_observer function parameters mentioned above are derived from a variable called Storage_observer, This variable is an example of Binlog_storage_observe. We'll keep tracking. This is a global variable defined in the source of the semi-synchronous copy plug-in:

//semisync_master_plugin.ccBinlog_storage_observer storage_observer = {  sizeof(Binlog_storage_observer), // len  repl_semi_report_binlog_update, // report_update  repl_semi_report_binlog_sync,   // after_sync};

The After_sync pointer to the Repl_semi_report_binlog_sync is the semi-synchronous plug-in about waiting for the specific implementation of the library ACK, which is not specifically in-depth semi-synchronous implementation.

Follow the source after a big circle, here to do a review. In fact, the process from the plug-in is called (and called Xxxx_observer's name) can be seen that this is a typical observer pattern, Run_hook call, if the corresponding xxxx_delegate have corresponding xxxx_observer observers, Call these observers ' callback functions, and do nothing if they are not.

Finally, we use the semi-synchronous plug-in to wait for the function of the ACK from the library to summarize a sequence diagram during a plug-in invocation:

MySQL Plugin implementation--plug-in invocation

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.