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:
co-authored by
Claude Opus 5.5
parent
bd550841bd
commit
8eb4caf456
@@ -2180,13 +2180,58 @@ class AcaiAPI
|
|||||||
$childNums[(int) $row['parentNum']][] = $row['num'];
|
$childNums[(int) $row['parentNum']][] = $row['num'];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$before = $byNum;
|
||||||
|
|
||||||
self::_rebuildCategoryBranch([
|
self::_rebuildCategoryBranch([
|
||||||
'branchParent' => 0,
|
'branchParent' => 0,
|
||||||
'records' => &$byNum,
|
'records' => &$byNum,
|
||||||
'childNodes' => $childNums,
|
'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) {
|
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(
|
$db->execute(
|
||||||
"UPDATE `$fullTable`
|
"UPDATE `$fullTable`
|
||||||
SET globalOrder = ?,
|
SET globalOrder = ?,
|
||||||
|
|||||||
Reference in New Issue
Block a user