Actualmente estoy tratando de mejorar un par de módulos con respecto al rendimiento.
Algunos de ustedes pueden conocer el uso del walk()
método en la colección, que es muy útil para evitar pasar directamente por los productos.
Además de eso y gracias a @Vinai, también se puede utilizar el delete()
método de recopilación .
Pero noté que los archivos nativos de Magento 1 no siempre usan ninguno de esos métodos para la eliminación.
Uno de los peores códigos que he visto es el massDelete()
método desde app/code/core/Mage/Adminhtml/controllers/Catalog/ProductController.php
donde los productos se cargan en un bucle antes de la eliminación .
foreach ($productIds as $productId) {
$product = Mage::getSingleton('catalog/product')->load($productId);
Mage::dispatchEvent('catalog_controller_product_delete', array('product' => $product));
$product->delete();
}
Así que realicé algunas pruebas de rendimiento, agregué algunas llamadas de registro para verificar el tiempo necesario y el uso de memoria para la eliminación de 100 productos.
Prueba 1: walk
método
Reemplacé el código original pegado anteriormente con este código:
$collection = Mage::getResourceModel('catalog/product_collection')
->addAttributeToSelect('entity_id')
->addIdFilter($productIds)
->walk('delete');
Y mis resultados son los siguientes en mi servidor de desarrollo de mierda (promedio basado en 10 pruebas):
- Código original: 19,97 segundos, 15,84 MB utilizados
- Código personalizado: 17,12 segundos, 15,45 MB utilizados
Entonces, para la eliminación de 100 productos, mi código personalizado es 3 segundos más rápido y usa 0.4MB menos.
Prueba 2: uso del delete()
método de recolección
Reemplacé el código original con este:
$collection = Mage::getResourceModel('catalog/product_collection')
->addAttributeToSelect('entity_id')
->addIdFilter($productIds)
->delete();
Y alucinante aquí están los resultados:
- Código original: 19,97 segundos, 15,84 MB utilizados
- Código personalizado: 1,24 segundos, 6,34 MB utilizados
Entonces, para la eliminación de 100 productos, mi código personalizado es 18 segundos más rápido y usa 9 MB menos.
Como se indicó en los comentarios, parece que este método no activa los eventos de Magento (después de la carga, después de eliminar) ni el vaciado del índice / caché.
Pregunta
Entonces mi pregunta es: ¿hay alguna razón por la cual el equipo central de Magento no utilizó el walk('delete')
evento o mejor el delete()
método de recolección en lugar de cargar los productos en un bucle (lo cual todos sabemos es una muy mala práctica)?
El objetivo principal es tener en cuenta estos puntos clave en el caso del desarrollo de un módulo: ¿hay casos particulares en los que no se puede usar el método walk
/ colección delete()
?
EDITAR: la razón definitivamente no se debe al catalog_controller_product_delete
envío del evento, ya que el mismo código se puede encontrar en varios lugares (verifique los massDelete
métodos) en el núcleo de Magento. He utilizado el ejemplo de productos para resaltar el rendimiento, ya que generalmente son las entidades más grandes.
fuente
getSingleton()
como medida de rendimiento, en lugar del uso obvio de la colección. Ah, y también es posible activar el evento con una colección, pero no con elwalk()
acceso directo.delete()
realiza una consulta DELETE en lugar de cargar la colección y eliminar cada producto. Con eso realmente perderás los eventos.Respuestas:
Nota al margen, ¡pero debe considerar usar el Varien Profiler para esto!
Si bien no dudo que su cambio mejoraría el rendimiento, sería útil proporcionar los resultados "anteriores" para comparar las mejoras.
Bueno, sabemos por otras preguntas en este foro lo siguiente:
Por lo tanto, sugeriría que el ejemplo que ha encontrado es probablemente una de las muchas gemas potencialmente ocultas en el código que se escribió hace mucho tiempo y / o por un desarrollador menos experimentado. Al igual que gran parte del código central (¡y el código de la comunidad!), Habría sido probado en un pequeño conjunto de datos y no probado en batalla, por lo que es posible que el rendimiento no haya sido monitoreado de cerca.
¿Es su mejora beneficiosa y está más estrechamente alineada con las mejores prácticas que el código original? Si. Sin embargo, usted como desarrollador de Magento [1.x] de la comunidad no tiene la capacidad de contribuir con las mejoras sugeridas, como lo hace con Magento 2, por lo que mi sugerencia sería implementar esto en un módulo local si lo requiere para el rendimiento en una de sus tiendas , o ignórelo si no lo está afectando pero lo ha notado mientras investigaba.
Como actualización de la edición de su pregunta, estoy seguro de que sabe que el método de caminata en Varien_Data_Collection acepta una devolución de llamada arbitraria, por lo que podrá usarlo para cualquier cosa que desee. Para enviar el evento en el ejemplo original, puede hacerlo con la función de caminar, así como con la eliminación.
La única razón por la que podría imaginar que sería útil cargar el producto antes de eliminarlo es que los observadores adjuntos a ese evento pueden necesitar un conjunto de datos completo no disponible sin cargar primero el producto. Si ese es el caso, explicaría por qué usan un singleton en lugar de un modelo para minimizar al menos los gastos generales del objeto.
fuente
catalog_controller_product_delete
Creo que lo están haciendo para disparar el evento que está siendo utilizado por Mage_Tag.catalog_product_delete_before
ocatalog_product_delete_after
significaría que esto era innecesario, aunque creo. Me pregunto si este evento en particular también se usa para el registro de acciones de administrador.fuente
massDelete()
acción deCustomerController.php
Creo que la eliminación masiva debería funcionar como eliminar un solo producto (completamente cargado).
Porque
$collection->delete()
la respuesta ya está dada. Si no se disparadeleter_before
,delete_after
podría romper algunas extensiones y omitir algunos observadores utilizados en el núcleo.$collection->walk('delete')
posiblemente funcionaría, pero aún tiene la desventaja de que los datos del producto no están completos. Esto también puede romper observadores personalizados si confían en datos adicionales, por ejemplo, un objeto de stock.Creo que, si cambia
->addAttributeToSelect('entity_id')
a->addAttributeToSelect('*')
y añadir->setFlag('require_stock_items', true)
(añadir los datos de saldos a los productos) no se obtienen mejores resultados a continuación "bucle-delete".Parece un mal estilo, pero creo que es adecuado para ambas acciones de eliminación masiva.
Yo uso
walk()
ydelete()
para modelos personalizados también, pero sé que no hay observadores oentity_id
es suficiente. Solo por mencionar,walk()
funcionaría con todos los eventos utilizados en el núcleo, porque solo se usan$product->getId()
, pero no se sabe de observadores de terceros.fuente