fix(Core/Travel): Chunk all saveNodeStore phases (deletes, nodes, links)

This commit is contained in:
bash 2026-05-17 13:05:33 +02:00
parent 793d3f8fdc
commit ac19374ed8

View File

@ -2018,70 +2018,96 @@ void TravelNodeMap::saveNodeStore()
hasToSave = false; hasToSave = false;
// Phase 1: deletes + nodes + links in a single transaction. ~4400 constexpr uint32 STMTS_PER_TX = 500; // bounded transaction size
// nodes + ~18000 links = ~22000 prepared statements, comfortably
// within MySQL transaction limits.
PlayerbotsDatabaseTransaction trans = PlayerbotsDatabase.BeginTransaction();
trans->Append(PlayerbotsDatabase.GetPreparedStatement(PLAYERBOTS_DEL_TRAVELNODE)); // Phase 1: deletes in their own transaction.
trans->Append(PlayerbotsDatabase.GetPreparedStatement(PLAYERBOTS_DEL_TRAVELNODE_LINK)); {
trans->Append(PlayerbotsDatabase.GetPreparedStatement(PLAYERBOTS_DEL_TRAVELNODE_PATH)); PlayerbotsDatabaseTransaction delTrans = PlayerbotsDatabase.BeginTransaction();
delTrans->Append(PlayerbotsDatabase.GetPreparedStatement(PLAYERBOTS_DEL_TRAVELNODE));
delTrans->Append(PlayerbotsDatabase.GetPreparedStatement(PLAYERBOTS_DEL_TRAVELNODE_LINK));
delTrans->Append(PlayerbotsDatabase.GetPreparedStatement(PLAYERBOTS_DEL_TRAVELNODE_PATH));
PlayerbotsDatabase.CommitTransaction(delTrans);
}
std::unordered_map<TravelNode*, uint32> saveNodes; std::unordered_map<TravelNode*, uint32> saveNodes;
std::vector<TravelNode*> anodes = TravelNodeMap::instance().getNodes(); std::vector<TravelNode*> anodes = TravelNodeMap::instance().getNodes();
for (uint32 i = 0; i < anodes.size(); i++) // Phase 2: node inserts, chunked at STMTS_PER_TX per transaction.
{ {
TravelNode* node = anodes[i]; PlayerbotsDatabaseTransaction nodeTrans = PlayerbotsDatabase.BeginTransaction();
uint32 inTx = 0;
for (uint32 i = 0; i < anodes.size(); i++)
{
TravelNode* node = anodes[i];
std::string name = node->getName(); std::string name = node->getName();
name.erase(remove(name.begin(), name.end(), '\''), name.end()); name.erase(remove(name.begin(), name.end(), '\''), name.end());
PlayerbotsDatabasePreparedStatement* stmt = PlayerbotsDatabase.GetPreparedStatement(PLAYERBOTS_INS_TRAVELNODE); PlayerbotsDatabasePreparedStatement* stmt = PlayerbotsDatabase.GetPreparedStatement(PLAYERBOTS_INS_TRAVELNODE);
stmt->SetData(0, i); stmt->SetData(0, i);
stmt->SetData(1, name); stmt->SetData(1, name);
stmt->SetData(2, node->GetMapId()); stmt->SetData(2, node->GetMapId());
stmt->SetData(3, node->getX()); stmt->SetData(3, node->getX());
stmt->SetData(4, node->getY()); stmt->SetData(4, node->getY());
stmt->SetData(5, node->getZ()); stmt->SetData(5, node->getZ());
stmt->SetData(6, node->isLinked()); stmt->SetData(6, node->isLinked());
trans->Append(stmt); nodeTrans->Append(stmt);
saveNodes.insert(std::make_pair(node, i)); saveNodes.insert(std::make_pair(node, i));
if (++inTx >= STMTS_PER_TX)
{
PlayerbotsDatabase.CommitTransaction(nodeTrans);
nodeTrans = PlayerbotsDatabase.BeginTransaction();
inTx = 0;
}
}
PlayerbotsDatabase.CommitTransaction(nodeTrans);
} }
LOG_INFO("playerbots", ">> Saved {} travelNodes.", anodes.size()); LOG_INFO("playerbots", ">> Saved {} travelNodes.", anodes.size());
// Phase 3: link inserts, chunked at STMTS_PER_TX per transaction.
uint32 paths = 0; uint32 paths = 0;
for (uint32 i = 0; i < anodes.size(); i++)
{ {
TravelNode* node = anodes[i]; PlayerbotsDatabaseTransaction linkTrans = PlayerbotsDatabase.BeginTransaction();
uint32 inTx = 0;
for (auto& link : *node->getLinks()) for (uint32 i = 0; i < anodes.size(); i++)
{ {
TravelNodePath* path = link.second; TravelNode* node = anodes[i];
PlayerbotsDatabasePreparedStatement* stmt = for (auto& link : *node->getLinks())
PlayerbotsDatabase.GetPreparedStatement(PLAYERBOTS_INS_TRAVELNODE_LINK); {
stmt->SetData(0, i); TravelNodePath* path = link.second;
stmt->SetData(1, saveNodes.find(link.first)->second);
stmt->SetData(2, static_cast<uint8>(path->getPathType()));
stmt->SetData(3, path->getPathObject());
stmt->SetData(4, path->getDistance());
stmt->SetData(5, path->getSwimDistance());
stmt->SetData(6, path->getExtraCost());
stmt->SetData(7, path->getCalculated());
stmt->SetData(8, path->getMaxLevelCreature()[0]);
stmt->SetData(9, path->getMaxLevelCreature()[1]);
stmt->SetData(10, path->getMaxLevelCreature()[2]);
trans->Append(stmt);
paths++; PlayerbotsDatabasePreparedStatement* stmt =
PlayerbotsDatabase.GetPreparedStatement(PLAYERBOTS_INS_TRAVELNODE_LINK);
stmt->SetData(0, i);
stmt->SetData(1, saveNodes.find(link.first)->second);
stmt->SetData(2, static_cast<uint8>(path->getPathType()));
stmt->SetData(3, path->getPathObject());
stmt->SetData(4, path->getDistance());
stmt->SetData(5, path->getSwimDistance());
stmt->SetData(6, path->getExtraCost());
stmt->SetData(7, path->getCalculated());
stmt->SetData(8, path->getMaxLevelCreature()[0]);
stmt->SetData(9, path->getMaxLevelCreature()[1]);
stmt->SetData(10, path->getMaxLevelCreature()[2]);
linkTrans->Append(stmt);
paths++;
if (++inTx >= STMTS_PER_TX)
{
PlayerbotsDatabase.CommitTransaction(linkTrans);
linkTrans = PlayerbotsDatabase.BeginTransaction();
inTx = 0;
}
}
} }
PlayerbotsDatabase.CommitTransaction(linkTrans);
} }
PlayerbotsDatabase.CommitTransaction(trans);
// Phase 2: path points in chunked transactions. Previously all // Phase 2: path points in chunked transactions. Previously all
// ~1.5M point inserts went into a single mega-transaction which // ~1.5M point inserts went into a single mega-transaction which
// exceeded MySQL's packet/transaction limits and partial-committed, // exceeded MySQL's packet/transaction limits and partial-committed,