Files
Sun1602/Server/DatabaseProxy/QueryObjects/DBHandler.cpp
T
2022-10-26 12:25:11 +08:00

592 lines
19 KiB
C++

#include "stdafx.h"
#include "./DBHandler.h"
#include "./DBUnit.h"
#include "PacketHandler/PacketHandler.h"
//==================================================================================================
DBHandler g_DBHandler;
//
ChildServerSession* DBHandler::ground_session_static = NULL;
//==================================================================================================
// CHANGES: f101118.1L, added ground session handling
namespace util { namespace internal {
;
struct GroundSession : public ChildServerSession
{
GroundSession();
virtual ~GroundSession();
// ServerSession interface
virtual void OnRedirect(DWORD network_index, MSG_SERVER_TYPE* const first_contact_msg);
//
void BindDBUnit();
void UnbindDBUnit();
bool binded_;
};
inline GroundSession::GroundSession()
: binded_(false)
{
}
GroundSession::~GroundSession()
{
}
void GroundSession::OnRedirect(DWORD network_index, MSG_SERVER_TYPE* const first_contact_msg)
{
__UNUSED((network_index, first_contact_msg));
}
void GroundSession::BindDBUnit()
{
// session_index
db_unit_id_ = g_DBHandler.AllocateDBUnit(TEMP_SERVER, 0);
assert(db_unit_id_ != 0);
binded_ = true;
}
void GroundSession::UnbindDBUnit()
{
// NOTE: f101118.1L, nothing to do because this object share DBHandler
}
}}; //end of namespace
//==================================================================================================
DBHandler::DBHandler()
: initialized_(false)
{
ZeroMemory(pod_field_start_, static_cast<size_t>(pod_field_end_ - pod_field_start_));
}
DBHandler::~DBHandler()
{
Release();
}
//==================================================================================================
void
DBHandler::Init(const char* database_name, const char* user_id, const char* password)
{
if (!FlowControl::FCAssert(initialized_ == false)) {
__debugbreak();
return;
};
//_NA000000_DBQUERY_REVERSAL_STATE_PREVENTION
id_generator_ = new DBUnitIDGenerator;
id_generator_->Create(1, _countof(db_unit_array_));
ZeroMemory(db_connectors_, sizeof(db_connectors_));
;{
strncpy(database_name_, database_name, _countof(database_name_));
database_name_[_countof(database_name_) - 1] = '\0';
strncpy(user_id_, user_id, _countof(user_id_));
user_id_[_countof(user_id_) - 1] = '\0';
strncpy(password_, password, _countof(password_));
password_[_countof(password_) - 1] = '\0';
};
//
ground_session_static = new util::internal::GroundSession;
ground_session_static->Init();
initialized_ = true;
}
void
DBHandler::Release()
{
;{ //_NA000000_DBQUERY_REVERSAL_STATE_PREVENTION
const nsQuery::DBConnectNode* it = db_connectors_,
* end = &db_connectors_[_countof(db_connectors_)];
for ( ; it != end; ++it)
{
SolarDatabaseInDBP* solar_database = it->solar_database_;
if (solar_database == NULL) {
continue;
};
solar_database->Release();
delete solar_database;
}
ZeroMemory(db_connectors_, sizeof(db_connectors_));
};
if (ground_session_static != NULL)
{
ground_session_static->Release();
SAFE_DELETE(ground_session_static);
}
//
SAFE_DELETE(id_generator_);
//
ZeroMemory(pod_field_start_, static_cast<size_t>(pod_field_end_ - pod_field_start_));
//
initialized_ = false;
}
bool
DBHandler::DBQuery(DWORD db_unit_id, Query* query)
{
DBUnit* const db_unit = FindDBUnit(db_unit_id);
if (db_unit == NULL)
{
#ifdef __NA000000_080709_DBQUERY_FAULT_DETECTION__
SUNLOG(eCRITICAL_LOG, _T("--Fail DBQuery key %u/tpQuery %s"), db_unit_id, query->GetQuery());
#endif
return false;
};
//
int connector_index = db_unit->GetDBConnectorIndex();
if (!FlowControl::FCAssert(connector_index > 0 &&
connector_index < _countof(db_connectors_)))
{
return false;
}
const nsQuery::DBConnectNode& db_connector = db_connectors_[connector_index];
assert(db_connector.solar_database_);
// DBUnit::DBQuery replacement
++db_unit->number_of_queries_;
query->SetVoidObject(db_unit);
bool query_success = db_connector.solar_database_->DBQuery(query);
if (FlowControl::FCAssert(query_success) == false) {
--db_unit->number_of_queries_;
}
return query_success;
}
bool DBHandler::AlignEmptyChannelConnectorIndex(int* connector_index)
{
nsQuery::DBConnectNode* it = &db_connectors_[db_connectors_->eIndex_Channel1],
* const end = &db_connectors_[db_connectors_->eIndex_ChannelEnd];
for ( ; it != end; ++it)
{
if (it->link_agent_ != false) {
continue;
}
break;
}
if (it == end) {
// full
*connector_index = db_connectors_->eIndex_None;
return false;
};
//
SolarDatabaseInDBP* solar_database = NULL;
if (AllocateSolarDatabase(&solar_database) == false)
{
SUNLOG(eCRITICAL_LOG, "solar_database_->Initialize()IsFailed");
*connector_index = db_connectors_->eIndex_None;
return false;
}
//
it->index_ = static_cast<uint8_t>(it - db_connectors_); // equals enumeration value
it->link_agent_ = true;
it->solar_database_ = solar_database;
*connector_index = it->index_;
return true;
}
bool DBHandler::AlignEmptyUserConnectorIndex(int* connector_index)
{
// find node that consider load balance
nsQuery::DBConnectNode* it = &db_connectors_[db_connectors_->eIndex_User1],
* const end = &db_connectors_[db_connectors_->eIndex_Counts];
nsQuery::DBConnectNode* lowest_node = it;
for ( ; it != end; ++it)
{
if (lowest_node->number_of_linked_ > it->number_of_linked_) {
lowest_node = it;
}
}
it = lowest_node;
//
if (it->solar_database_ == NULL)
{
SolarDatabaseInDBP* solar_database = NULL;
if (AllocateSolarDatabase(&solar_database) == false)
{
SUNLOG(eCRITICAL_LOG, "solar_database_->Initialize()IsFailed");
*connector_index = db_connectors_->eIndex_None;
return false;
}
it->index_ = static_cast<uint8_t>(it - db_connectors_); // equals enumeration value
it->solar_database_ = solar_database;
};
//
++it->number_of_linked_;
*connector_index = it->index_;
return true;
}
bool DBHandler::AllocateSolarDatabase(SolarDatabaseInDBP** result_solar_database)
{
SolarDatabaseInDBP* solar_database = new SolarDatabaseInDBP;
SolarDatabaseDesc db_desc;
;{
db_desc.database_module_type = DBCInterfaceType_ODBC;
db_desc.query_pool_size = 5000;
db_desc.query_processor_num = 1;
db_desc.database_ip = "";
db_desc.database_name = const_cast<char*>(database_name_);
db_desc.user_name = const_cast<char*>(user_id_);
db_desc.user_password = const_cast<char*>(password_);
db_desc.fnptr_error_message = &DBHandler::CallBackMessageSession;
db_desc.fnptr_query_result = &DBHandler::CallbackDBResult;
db_desc.fnptr_error_message_ex = &DBHandler::CallBackMessageSessionEx;
};
//
bool success = solar_database->Initialize(db_desc, true);
if (success == false) {
*result_solar_database = NULL;
solar_database->Release();
delete solar_database;
return false;
}
*result_solar_database = solar_database;
return true;
}
ChildServerSession* DBHandler::GetSharedSession()
{
using util::internal::GroundSession;
GroundSession* ground_session = static_cast<GroundSession*>(ground_session_static);
assert(ground_session);
if (ground_session->binded_) {
return ground_session;
}
ground_session->BindDBUnit();
return ground_session;
};
// 커넥션 연결시
DWORD DBHandler::AllocateDBUnit(eSERVER_TYPE server_type, DWORD session_index)
{
const DWORD kNullHandleID = 0;
//
BOOST_STATIC_ASSERT(db_connectors_->eIndex_Counts == kNumberOfDBConnector);
DWORD db_unit_id = id_generator_->GetKey();
if (db_unit_id == kNullHandleID) {
return kNullHandleID;
};
//
int db_connector_index = db_connectors_->eIndex_None;
;{
switch (server_type)
{
case AGENT_SERVER:
if (AlignEmptyChannelConnectorIndex(&db_connector_index) == false) {
return kNullHandleID;
}
break;
case GAME_SERVER:
case FIELD_SERVER:
case BATTLE_SERVER:
// associate agentserver
break;
default:
{
db_connector_index = db_connectors_->eIndex_Shared;
if (db_connectors_[db_connector_index].solar_database_ == NULL)
{
SolarDatabaseInDBP* solar_database = NULL;
if (AllocateSolarDatabase(&solar_database) == false)
{
SUNLOG(eCRITICAL_LOG,
_T("|["__FUNCTION__"]|斤口=can't allocate solardatabase"));
return kNullHandleID;
}
db_connectors_[db_connector_index].solar_database_ = solar_database;
}
};
break;
};
};
DBUnit* const db_unit = &db_unit_array_[db_unit_id - 1];
if (db_unit->Init(db_unit_id, db_connector_index, server_type, session_index) == false) {
return kNullHandleID;
};
//
return db_unit_id;
//
}
// 커넥션 해제시
void
DBHandler::DeallocateDBUnit(DWORD db_unit_id)
{
DBUnit* const db_unit = FindDBUnit(db_unit_id);
if (!FlowControl::FCAssert(db_unit)) {
return;
};
//
const int connector_index = db_unit->db_connector_index_;
const eSERVER_TYPE server_type = static_cast<eSERVER_TYPE>(db_unit->server_type_);
//
if (db_unit->RequestRelease()) {
id_generator_->RestoreKey(db_unit_id);
};
// garbage data = { db_unit };
//
if (server_type == GAME_SERVER)
{
// not associated with agent
if (connector_index == 0) {
return;
}
}
else
{
if (!FlowControl::FCAssert(connector_index > 0 &&
connector_index < db_connectors_->eIndex_ChannelEnd))
{
return;
};
};
//
nsQuery::DBConnectNode* const db_connector = &db_connectors_[connector_index];
if (server_type == AGENT_SERVER)
{
db_connector->link_agent_ = false;
// will be changed related with game servers (like that field, battle) on reconnect agent
};
//
return;
}
void DBHandler::UpdateDBConnectorIndex(DWORD db_unit_id, int db_connector_index)
{
DBUnit* const db_unit = FindDBUnit(db_unit_id);
if (!FlowControl::FCAssert(db_unit)) {
return;
};
if (db_connector_index == 0) {
return;
};
const int db_connector_index_in_dbunit = db_unit->db_connector_index_;
if (db_connector_index_in_dbunit != 0 && db_connector_index_in_dbunit == db_connector_index) {
return;
}
if (!FlowControl::FCAssert(db_connector_index >= db_connectors_->eIndex_Channel1 &&
db_connector_index < _countof(db_connectors_)))
{
return;
}
db_unit->db_connector_index_ = static_cast<uint8_t>(db_connector_index);
}
DWORD DBHandler::UpdateDBUnitForUser(DBUser* const db_user, DWORD db_unit_id)
{
const DWORD kNullHandleID = 0;
ChildServerSession* const server_session = db_user->GetServerSession();
DWORD session_index = server_session ? server_session->GetSessionIndex() : 0;
//
enum { eUserState_Clear, eUserState_New, eUserState_Change, eUserState_Close };
const int user_state = (session_index && db_unit_id) ? eUserState_Change
: (session_index) ? eUserState_New
: (db_unit_id) ? eUserState_Close
: eUserState_Clear;
//
if (user_state == eUserState_Clear) {
return kNullHandleID;
};
//
DBUnit* db_unit = NULL;
//
if (user_state == eUserState_Change)
{
db_unit = FindDBUnit(db_unit_id);
db_unit->session_index_ = session_index;
db_unit->server_type_ = static_cast<uint8_t>(server_session->GetServerType());
return db_unit_id;
};
//
int db_connector_index = db_connectors_->eIndex_None;
if (user_state == eUserState_Close)
{
db_unit = FindDBUnit(db_unit_id);
db_connector_index = db_unit->db_connector_index_;
if (db_unit->RequestRelease()) {
if (FlowControl::FCAssert(db_connector_index >= db_connectors_->eIndex_User1 &&
db_connector_index < db_connectors_->eIndex_Counts))
{
if (db_connectors_[db_connector_index].number_of_linked_) {
--db_connectors_[db_connector_index].number_of_linked_;
}
}
id_generator_->RestoreKey(db_unit_id);
};
return kNullHandleID;
};
//
if (user_state == eUserState_New)
{
db_unit_id = id_generator_->GetKey();
if (FlowControl::FCAssert(db_unit_id != kNullHandleID) == false) {
return kNullHandleID;
}
db_unit = &db_unit_array_[db_unit_id - 1];
// TODO: alloc new db connector
if (AlignEmptyUserConnectorIndex(&db_connector_index) == false) {
id_generator_->RestoreKey(db_unit_id);
return kNullHandleID;
}
db_unit->Init(db_unit_id, db_connector_index,
server_session->GetServerType(), session_index);
};
return db_unit_id;
}
bool DBHandler::Update()
{
bool disconnected_or_condition = false;
const nsQuery::DBConnectNode* it = db_connectors_,
* end = &db_connectors_[_countof(db_connectors_)];
for ( ; it != end; ++it)
{
SolarDatabaseInDBP* solar_database = it->solar_database_;
if (solar_database == NULL) {
continue;
};
solar_database->Update();
// NOTE: f110427.5L, do shutdown process if a handler disconnected with odbc.
if (solar_database->IsConnected() == false) {
disconnected_or_condition = true;
};
}
return (disconnected_or_condition == false);
}
void
DBHandler::Display()
{
SUNLOG(eCRITICAL_LOG, _T("Remains DB Unit Resources : %d"), id_generator_->GetSize());
}
DWORD
DBHandler::GetQueuingQueryNum() const
{
DWORD number_of_total_queueing_queries = 0;
const nsQuery::DBConnectNode* it = db_connectors_,
* end = &db_connectors_[_countof(db_connectors_)];
for ( ; it != end; ++it)
{
SolarDatabaseInDBP* solar_database = it->solar_database_;
if (solar_database == NULL) {
continue;
};
number_of_total_queueing_queries += solar_database->GetBufferedQueryNum();
}
return number_of_total_queueing_queries;
};
// NOTE: f110628.1L, added the specialied busy update query counter to support a load banalcing
DWORD DBHandler::GetNumberOfDBConnectionQueuingQueries(DBUser* const db_user) const
{
DWORD db_unit_id = db_user->db_unit_id();
DBUnit* const db_unit = this->FindDBUnit(db_unit_id);
if (db_unit == NULL) {
assert(!"invalid db unit id");
return 0;
};
const int db_connector_index_in_dbunit = db_unit->db_connector_index_;
if (!FlowControl::FCAssert(db_connector_index_in_dbunit >= db_connectors_->eIndex_User1 &&
db_connector_index_in_dbunit < _countof(db_connectors_)))
{
return 0;
}
int number_of_pending_queries = db_connectors_[\
db_connector_index_in_dbunit].solar_database_->GetNumberOfPendingQueries();
return number_of_pending_queries;
}
//==================================================================================================
// callback
void DBHandler::CallbackDBResult(DWORD protocol, QueryResult* query_result)
{
// same context as logic thread
DBUnit* const db_unit = reinterpret_cast<DBUnit*>(query_result->GetVoidObject());
DBHandler* const this_ = &g_DBHandler;
//
;{
const DWORD delay_time = query_result->GetQueryTime();
if (delay_time > 1000)
{
SUNLOG(eCRITICAL_LOG, "QueryHighDelay:[%u=%usec]%s",
delay_time, delay_time / 1000, query_result->GetQuery());
}
};
//
ChildServerSession* server_session = \
static_cast<ChildServerSession*>(g_DBProxyServer.FindSession(db_unit->session_index_));
//
MSG_DBPROXY_RESULT result_msg;
;{
result_msg.m_byCategory = static_cast<BYTE>(LOWORD(protocol));
result_msg.m_byProtocol = static_cast<BYTE>(HIWORD(protocol));
result_msg.m_pData = query_result;
};
//
if (!server_session) {
server_session = this_->ground_session_static;
};
//
PacketHandler::Instance()->ParsePacket(PACKET_HANDLER_DBR,
server_session, &result_msg, sizeof(result_msg));
//
if (db_unit->number_of_queries_) {
--db_unit->number_of_queries_;
}
if (db_unit->number_of_queries_ == 0 &&
db_unit->unit_state_.value == db_unit->unit_state_.eState_Closing)
{
uint16_t db_unit_id = db_unit->db_unit_id_;
int db_connector_index = db_unit->db_connector_index_;
if (FlowControl::FCAssert(db_connector_index > this_->db_connectors_->eIndex_None &&
db_connector_index < this_->db_connectors_->eIndex_Counts))
{
nsQuery::DBConnectNode* const db_connector = &this_->db_connectors_[db_connector_index];
if (db_connector->number_of_linked_) {
--db_connector->number_of_linked_;
}
}
db_unit->RequestRelease();
this_->id_generator_->RestoreKey(db_unit_id);
return;
}
}
void DBHandler::CallBackMessageSession(char* message)
{
// diff with logic thread context
SUNCONNLOG(eCRITICAL_LOG, "[DBMessage]:%s", message);
}
void DBHandler::CallBackMessageSessionEx(WORD db_index, QueryResult* query_result)
{
// diff with logic thread context
DBHandler* const this_ = &g_DBHandler;
DBUnit* db_unit = reinterpret_cast<DBUnit*>(query_result->GetVoidObject());
const char* sqlerror_msg = query_result->GetErrorMsg();
const char* sqlstate_msg = query_result->GetSqlState();
SUNCONNLOG(eCRITICAL_LOG, "[Index:%u,DBIndex:%u]:%s,SQLSTATE:%s,NativeError:%u,%s",
db_unit->GetSessionIndex(),
db_index, sqlerror_msg, sqlstate_msg, query_result->GetNativeError(),
query_result->GetQuery());
//
const char* found = strstr(sqlerror_msg, "[DBNETLIB]");
if (found != NULL || (strncmp(sqlstate_msg, "08S01", 5) == 0))
{
SolarDatabaseInDBP* solar_database = \
this_->db_connectors_[db_unit->db_connector_index_].solar_database_;
solar_database->SetConnectState(false);
}
}