def wait_for_slave_gtid(server, gtids, timeout=0): """Wait until a slave executes GITDs. The function `SELECT WAIT_UNTIL_SQL_THREAD_AFTER_GTIDS` is called until the slave catches up. If the timeout period expires prior to achieving the condition the :class:`~mysql.fabric.errors.TimeoutError` exception is raised. If any thread is stopped, the :class:`~mysql.fabric.errors.DatabaseError` exception is raised. :param server: MySQL Server. :param gtids: Gtid information. :param timeout: Timeout for waiting for slave to catch up. """ # Check servers for GTID support if not server.gtid_enabled: raise _errors.ProgrammingError( "Global Transaction IDs are not supported.") res = server.exec_stmt(_GTID_WAIT, {"params": (gtids, timeout)}) if res is None or res[0] is None or res[0][0] is None: raise _errors.DatabaseError("Error waiting for slave to catch up. " "GTID (%s)." % (gtids, )) elif res[0][0] == -1: raise _errors.TimeoutError("Error waiting for slave to catch up. " "GTID (%s)." % (gtids, )) assert (res[0][0] > -1)
def get_num_gtid(gtids, server_uuid=None): """Return the number of transactions represented in GTIDs. By default this function considers any server in GTIDs. So if one wants to count transactions from a specific server, the parameter server_uuid must be defined. :param gtids: Set of transactions. :param server_uuid: Which server one should consider where None means all. """ sid = None difference = 0 for gtid in gtids.split(","): # Exctract the server_uuid and the trx_ids. trx_ids = None if gtid.find(":") != -1: sid, trx_ids = gtid.split(":") else: if not sid: raise _errors.ProgrammingError("Malformed GTID (%s)." % (gtid, )) trx_ids = gtid # Ignore differences if server_uuid is set and does # not match. if server_uuid and str(server_uuid).upper() != sid.upper(): continue # Check the difference. difference += 1 if trx_ids.find("-") != -1: lgno, rgno = trx_ids.split("-") difference += int(rgno) - int(lgno) return difference
def _do_enqueue_procedures(self, within_procedure, actions, lockable_objects): """Schedule a set of procedures. """ procedures = None executor = ExecutorThread.executor_object() if not executor: if within_procedure: raise _errors.ProgrammingError( "One can only create a new job from a job.") procedures, jobs = self._create_jobs(actions, lockable_objects) assert (len(set(procedures)) == len(set(jobs))) # There is no need to catch exceptions at this point. They will # be automatically caught by the caller which is usually the # XML-RPC session thread. _checkpoint.register(jobs, False) self.__scheduler.enqueue_procedures(procedures) else: current_job = executor.current_job current_procedure = current_job.procedure if within_procedure: procedures, jobs = self._create_jobs(actions, lockable_objects, current_procedure.uuid) assert (set([job.procedure for job in jobs]) == set(procedures) == set([current_procedure])) current_job.append_jobs(jobs) else: procedures, jobs = self._create_jobs(actions, lockable_objects) assert (len(set(procedures)) == len(set(jobs))) current_job.append_procedures(procedures) assert (procedures is not None) return procedures
def wait_for_procedure(self, procedure): """Wait until the procedure finishes the execution of all its jobs. """ if ExecutorThread.executor_object(): raise _errors.ProgrammingError( "One cannot wait for the execution of a procedure from " "a job.") procedure.wait()
def _do_reschedule_procedure(self, proc_uuid, actions, lockable_objects): """Recovers a procedure after a failure by rescheduling it. """ if ExecutorThread.executor_object(): raise _errors.ProgrammingError( "One cannot reschedule a procedure from a job.") procedures, jobs = self._create_jobs(actions, lockable_objects, proc_uuid) self.__scheduler.enqueue_procedures(procedures) assert (set([job.procedure for job in jobs]) == set(procedures)) assert (set([job.procedure.uuid for job in jobs ]) == set([procedure.uuid for procedure in procedures])) assert (procedures is not None) return procedures
def sync_slave_with_master(slave, master, timeout=0): """Synchronizes a slave with a master. See :func:`wait_for_slave_gtid`. This function can block if the master fails and all transactions are not fetched. :param slave: Reference to a slave (MySQL Server). :param master: Reference to the master (MySQL Server). :param timeout: Timeout for waiting for slave to catch up. """ # Check servers for GTID support if not slave.gtid_enabled or not master.gtid_enabled: raise _errors.ProgrammingError( "Global Transaction IDs are not supported.") master_gtids = master.get_gtid_status() master_gtids = master_gtids[0].GTID_EXECUTED.strip(",") wait_for_slave_gtid(slave, master_gtids, timeout)
def exec_mysql_stmt(cnx, stmt_str, options=None): """Execute a statement for the client and return a result set or a cursor. This is the singular method to execute queries. If something goes wrong while executing a statement, the exception :class:`~mysql.fabric.errors.DatabaseError` is raised. :param cnx: Database connection. :param stmt_str: The statement (e.g. query, updates, etc) to execute. :param options: Options to control behavior: - params - Parameters for statement. - columns - If true, return a rows as named tuples (default is False). - raw - If true, do not convert MySQL's types to Python's types (default is True). - fetch - If true, execute the fetch as part of the operation (default is True). :return: Either a result set as list of tuples (either named or unnamed) or a cursor. """ if cnx is None: raise _errors.DatabaseError("Invalid database connection.") options = options or {} params = options.get('params', ()) columns = options.get('columns', False) fetch = options.get('fetch', True) raw = options.get('raw', False) if raw and columns: raise _errors.ProgrammingError( "No raw cursor available returning named tuple") _LOGGER.debug("Statement ({statement}, Params({parameters}).".format( statement=stmt_str.replace('\n', '').replace('\r', ''), parameters=params)) cur = None try: cur = cnx.cursor(raw=raw, named_tuple=columns) cur.execute(stmt_str, params) except Exception as error: if cnx.unread_result: cnx.get_rows() if cur: cur.close() errno = getattr(error, 'errno', None) raise _errors.DatabaseError( "Command (%s, %s) failed accessing (%s). %s." % (stmt_str, params, mysql_address_from_cnx(cnx), error), errno) assert (cur is not None) if fetch: results = None try: if cnx.unread_result: results = cur.fetchall() except mysql.connector.errors.InterfaceError as error: raise _errors.DatabaseError( "Command (%s, %s) failed fetching data from (%s). %s." % (stmt_str, params, mysql_address_from_cnx(cnx), error)) finally: cur.close() return results return cur