async def _execute_trans(self, sql: TCL) -> result.Result: try: if sql.is_start: if self._trans: raise Exception("Transactions cannot be nested.") self._trans = self._create_trans(sql.trans_args) await self._trans.begin(sql) return result.OK(0, 0, 0, 0, '', False) elif sql.is_commit: if self._trans: await self._trans.commit(sql) await self._trans.close() self._trans = None return result.OK(0, 0, 0, 0, '', False) elif sql.is_rollback: if self._trans: await self._trans.rollback(sql) await self._trans.close() self._trans = None return result.OK(0, 0, 0, 0, '', False) else: return result.Error(1000, "unknown what to do.") except Exception as e: await self._trans.close() self._trans = None return result.Error(1000, str(e))
async def rollback(self, sql: TCL) -> Optional[result.Result]: r = await self.a2pc_client.rollback(self.xid) if r.status == 0: self.status = TransStatus.END return result.OK(0, 0, 0, 0, "", False) else: return result.Error(r.status, r.msg)
async def _execute_ending(self, r: List[Union[result.Result, A2PCResponse]], node: List[DBTableStrategyBackend], sql: DMLW): lock = r[-1] r = r[:-1] ending_sql = "COMMIT" return_v = None if not isinstance(lock, A2PCResponse): ending_sql = "ROLLBACK" return_v = result.Error( 1000, "{} acquire lock fail: {}".format(str(sql.raw), str(lock))) elif lock.status != 0: logger.warning("{} acquire lock fail: {}".format( str(sql.raw), lock.msg)) ending_sql = "ROLLBACK" return_v = result.Error( 1000, "{} acquire lock fail: {}".format(str(sql.raw), lock.msg)) if any([isinstance(i, result.Error) for i in r]): errors = [i for i in r if isinstance(i, result.Error)] for i in errors: logger.warning("{} error with code[{}],msg: {}".format( str(sql.raw), i.error_code, i.message)) ending_sql = "ROLLBACK" return_v = errors[0] c = [] for i in node: c.append(self._execute_ending_sql(ending_sql, i)) # 如果在 rollback 的时候异常,抛错给 app,即使数据库没有 rollback, # 也会在超时机制下回滚。 backend 遇到异常会关闭 session 触发 mysql # 的超时 # 如果在 commit 的时候出现异常,返回错误给客户端,客户端可以重试, # 成功就走正常流程,或者选择回滚,回滚就走事务回滚流程 await asyncio.gather(*c) if ending_sql == "ROLLBACK": return return_v # type: ignore return result.OK(1, 0, 2, 0, "", False)
def read_result(self, _result: Optional[MySQLResult] = None) -> \ result.Result: if _result is None: _result = self._result if _result.description is None: r = result.OK( _result.affected_rows, # type: ignore _result.insert_id, # type: ignore _result.server_status, # type: ignore _result.warning_count, # type: ignore _result.message, # type: ignore _result.has_next) # type: ignore return r descriptions = [] for i in _result.fields: desc = result.ResultDescription(i.catalog, i.db, i.table_name, i.org_table, i.name, i.org_name, i.charsetnr, i.length, i.type_code, i.flags, i.scale) descriptions.append(desc) return result.ResultSet(_result.field_count, descriptions, list(_result.rows)) # type: ignore