function SlaveConnection() { if (!class_exists('cmodule') || !class_exists('csqlwhere')) { return null; } if (!CModule::IncludeModule('cluster')) { return false; } $arSlaves = CClusterSlave::GetList(); if (empty($arSlaves)) { return false; } $max_slave_delay = COption::GetOptionInt("cluster", "max_slave_delay", 10); if (isset($_SESSION["BX_REDIRECT_TIME"])) { $redirect_delay = time() - $_SESSION["BX_REDIRECT_TIME"] + 1; if ($redirect_delay > 0 && $redirect_delay < $max_slave_delay) { $max_slave_delay = $redirect_delay; } } $total_weight = 0; foreach ($arSlaves as $i => $slave) { if (defined("BX_CLUSTER_GROUP") && BX_CLUSTER_GROUP != $slave["GROUP_ID"]) { unset($arSlaves[$i]); } elseif ($slave["ROLE_ID"] == "SLAVE") { $arSlaveStatus = CClusterSlave::GetStatus($slave["ID"], true, false, false); if ($arSlaveStatus['Seconds_Behind_Master'] > $max_slave_delay || $arSlaveStatus['Last_SQL_Error'] != '' || $arSlaveStatus['Last_IO_Error'] != '' || $arSlaveStatus['Slave_SQL_Running'] === 'No') { unset($arSlaves[$i]); } else { $total_weight += $slave["WEIGHT"]; } } else { $total_weight += $slave["WEIGHT"]; } } $found = false; foreach ($arSlaves as $slave) { if (mt_rand(0, $total_weight) < $slave["WEIGHT"]) { $found = $slave; break; } } if (!$found || $found["ROLE_ID"] != "SLAVE") { return false; //use main connection } else { ob_start(); $conn = CDatabase::GetDBNodeConnection($found["ID"], true); ob_end_clean(); if (is_object($conn)) { return $conn; } else { self::$arNodes[$found["ID"]]["ONHIT_ERROR"] = true; CClusterDBNode::SetOffline($found["ID"]); return false; //use main connection } } }
public function Query($strSql, $bIgnoreErrors = false, $error_position = "", $arOptions = array()) { global $DB; $this->DoConnect(); $this->db_Error = ""; if ($this->DebugToFile || $DB->ShowSqlStat) { $start_time = microtime(true); } //We track queries for DML statements //and when there is no one we can choose //to run query against master connection //or replicated one $connectionPool = \Bitrix\Main\Application::getInstance()->getConnectionPool(); if ($connectionPool->isMasterOnly()) { //We requested to process all queries //by master connection } elseif ($this->bModuleConnection) { //In case of dedicated module database //were is nothing to do } elseif (isset($arOptions["fixed_connection"])) { //We requested to process this query //by current connection } elseif ($this->bNodeConnection) { //It is node so nothing to do } else { if (isset($arOptions["ignore_dml"])) { $connectionPool->ignoreDml(true); } $connection = $connectionPool->getSlaveConnection($strSql); if (isset($arOptions["ignore_dml"])) { $connectionPool->ignoreDml(false); } if ($connection !== null) { if (!isset($this->obSlave)) { $nodeId = $connection->getNodeId(); ob_start(); $conn = CDatabase::GetDBNodeConnection($nodeId, true); ob_end_clean(); if (is_object($conn)) { $this->obSlave = $conn; } else { self::$arNodes[$nodeId]["ONHIT_ERROR"] = true; CClusterDBNode::SetOffline($nodeId); } } if (is_object($this->obSlave)) { return $this->obSlave->Query($strSql, $bIgnoreErrors, $error_position, $arOptions); } } } $result = $this->QueryInternal($strSql); if ($this->DebugToFile || $DB->ShowSqlStat) { /** @noinspection PhpUndefinedVariableInspection */ $exec_time = round(microtime(true) - $start_time, 10); if ($DB->ShowSqlStat) { $DB->addDebugQuery($strSql, $exec_time, $connectionPool->isSlavePossible() ? $this->node_id : -1); } if ($this->DebugToFile) { $this->startSqlTracker()->writeFileLog($strSql, $exec_time, "CONN: " . $this->getThreadId()); } } if (!$result) { $this->db_Error = $this->GetError(); $this->db_ErrorSQL = $strSql; if (!$bIgnoreErrors) { AddMessage2Log($error_position . " MySql Query Error: " . $strSql . " [" . $this->db_Error . "]", "main"); if ($this->DebugToFile) { $this->startSqlTracker()->writeFileLog("ERROR: " . $this->db_Error, 0, "CONN: " . $this->getThreadId()); } if ($this->debug || @session_start() && $_SESSION["SESS_AUTH"]["ADMIN"]) { echo $error_position . "<br><font color=#ff0000>MySQL Query Error: " . htmlspecialcharsbx($strSql) . "</font>[" . htmlspecialcharsbx($this->db_Error) . "]<br>"; } $error_position = preg_replace("#<br[^>]*>#i", "\n", $error_position); SendError($error_position . "\nMySQL Query Error:\n" . $strSql . " \n [" . $this->db_Error . "]\n---------------\n\n"); if (file_exists($_SERVER["DOCUMENT_ROOT"] . BX_PERSONAL_ROOT . "/php_interface/dbquery_error.php")) { include $_SERVER["DOCUMENT_ROOT"] . BX_PERSONAL_ROOT . "/php_interface/dbquery_error.php"; } elseif (file_exists($_SERVER["DOCUMENT_ROOT"] . "/bitrix/modules/main/include/dbquery_error.php")) { include $_SERVER["DOCUMENT_ROOT"] . "/bitrix/modules/main/include/dbquery_error.php"; } else { die("MySQL Query Error!"); } die; } return false; } $res = new CDBResult($result); $res->DB = $this; if ($DB->ShowSqlStat) { $res->SqlTraceIndex = count($DB->arQueryDebug) - 1; } return $res; }
/** * Returns module database connection. * Can be used only if module supports sharding. * * @param string $module_id * @param bool $bModuleInclude * @return bool|CDatabase */ public static function GetModuleConnection($module_id, $bModuleInclude = false) { $node_id = COption::GetOptionString($module_id, "dbnode_id", "N"); if (is_numeric($node_id)) { if ($bModuleInclude) { $status = COption::GetOptionString($module_id, "dbnode_status", "ok"); if ($status === "move") { return false; } } $moduleDB = CDatabase::GetDBNodeConnection($node_id, $bModuleInclude); if (is_object($moduleDB)) { $moduleDB->bModuleConnection = true; return $moduleDB; } //There was an connection error if ($bModuleInclude && CModule::IncludeModule('cluster')) { CClusterDBNode::SetOffline($node_id); } //TODO: unclear what to return when node went offline //in the middle of the hit. return false; } else { return $GLOBALS["DB"]; } }