perf(AcaiAPI): updateCategoryMetadata escribe solo las filas que cambian, en una transaccion

Antes reescribia la tabla entera con un UPDATE por fila en autocommit. Medido
en una copia de provincias_y_poblaciones (8.168 filas, InnoDB), resultado
identico al anterior (CRC de todas las filas): recalcular sin cambios 0,93 s
-> 0,04 s; mover un pueblo dentro de su provincia 1,39 s -> 0,13 s; subir una
provincia arriba del todo 2,37 s -> 1,28 s.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Acai Forge
2026-10-20 18:55:16 +00:00
co-authored by Claude Opus 5.5
parent bd550841bd
commit 8eb4caf456
+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 = ?,