Merge perf/category-metadata-tx: updateCategoryMetadata solo escribe lo que cambia, en una transacción

This commit is contained in:
Jordan Diaz
2026-10-20 20:49:56 +00:00
+45
View File
@@ -2180,13 +2180,58 @@ class AcaiAPI
$childNums[(int) $row['parentNum']][] = $row['num'];
}
$before = $byNum;
self::_rebuildCategoryBranch([
'branchParent' => 0,
'records' => &$byNum,
'childNodes' => $childNums,
]);
// Solo las filas que cambian, y todas en UNA transaccion. Antes era un
// UPDATE por fila de la tabla entera en autocommit: en InnoDB cada uno
// es un commit a disco (8.168 filas = 1,9 s; en una transaccion, 0,7 s),
// y mover un nodo deja casi siempre intacta buena parte del arbol.
// START TRANSACTION por SQL y no con PDO: `Db` no expone la conexion, y
// anadirle un metodo aqui romperia las webs con un Db.php propio (el
// sync de web-base respeta los ficheros personalizados). En MyISAM es
// inocuo.
$changed = [];
foreach ($byNum as $num => $cat) {
if (self::_categoryRowChanged($before[$num], $cat)) $changed[$num] = $cat;
}
if (!$changed) return;
$db->execute("START TRANSACTION");
try {
self::_writeCategoryRows($db, $fullTable, $changed);
$db->execute("COMMIT");
} catch (\Throwable $e) {
$db->execute("ROLLBACK");
throw $e;
}
}
/** ¿Cambia alguno de los campos que recalcula el arbol? PDO devuelve
* tipos nativos y el recalculo mezcla int y string: se compara como
* texto, salvo NULL, que solo es igual a NULL. */
private static function _categoryRowChanged(array $before, array $after)
{
foreach (['globalOrder', 'siblingOrder', 'depth', 'lineage', 'breadcrumb'] as $field) {
$a = isset($before[$field]) ? $before[$field] : null;
$b = isset($after[$field]) ? $after[$field] : null;
if ($a === null || $b === null) {
if ($a !== $b) return true;
} elseif ((string) $a !== (string) $b) {
return true;
}
}
return false;
}
private static function _writeCategoryRows($db, $fullTable, array $rows)
{
foreach ($rows as $num => $cat) {
$db->execute(
"UPDATE `$fullTable`
SET globalOrder = ?,