Problema ImportExport com o novo destruidor de Varien_Image_Adapter_Gd2 na versão 1.9.2.0

23

Alguém pode explicar, para que é utilizado o seguinte código introduzido entre o Magento CE 1.9.1.0 e 1.9.2.0?

class Varien_Image_Adapter_Gd2:

public function __construct()
{
    // Initialize shutdown function
    register_shutdown_function(array($this, 'destruct'));
}

/**
 * Destroy object image on shutdown
 */
public function destruct()
{
    @imagedestroy($this->_imageHandler);
}

Após a adição dessas duas funções, nossa importação de imagens da galeria de produtos com a interface ImportExport parou de funcionar. O erro ocorre devido a um limite de memória (que acaba sendo o limite máximo de tamanho de arquivo aberto).

Minha ideia é que os arquivos abertos pela importação não sejam fechados corretamente.

Vi também que algumas destruct()funções vazias foram introduzidas ( Mage_ImportExport_Model_Import_Adapter_Abstract) - mas estendê-las para corresponder à lógica dos pais não ajuda.

Achim Rosenhagen
fonte

Respostas:

14

Parece que eles tentaram destruir o recurso de imagem, mas introduziram um vazamento de memória. Não consigo pensar em uma razão válida para esse código, para ser sincero, mas posso explicar o que foi alterado:

Originalmente, imagedestroy()teria sido chamado no desctrutor__destruct()

function __destruct()
{
    @imagedestroy($this->_imageHandler);
}

O destruidor é chamado sempre que o coletor de lixo do PHP destrói objetos não utilizados (ou seja, objetos na memória que não são mais referenciados).

Agora, imagedestroy()é chamado em uma função de desligamento e, como esse é um retorno de chamada para um método do Varien_Image_Adapter_Gd2objeto, ele não pode nem ser coletado como lixo até o final. Dessa forma, todos os recursos de imagem permanecem abertos até a execução do script terminar.

Fabian Schmengler
fonte
Obrigado pela explicação - foi nisso que pensei. Portanto, no geral, esse código introduzido torna a maioria das importações inúteis no 1.9.2. nos meus olhos. Espero que isso seja corrigido em breve. Algum conselho sobre onde abrir um relatório de bug?
Achim Rosenhagen
6

Tendo os mesmos problemas com o meu Magento 1.9.2.0 ...

Eu só consigo fazer isso alterando Varien_Image_Adapter_Gd2 da /lib/Varien/Image/Adapter/Gd2.phpseguinte maneira:

public function __construct()
{
    // Initialize shutdown function
    // register_shutdown_function(array($this, 'destruct'));
}

/**
 * Destroy object image on shutdown
 */
public function __destruct()
{
    @imagedestroy($this->_imageHandler);
}
  • remover linha com register_shutdown_function (ou comentar)
  • alterar o nome da função destruct para __destruct

Eu configurei o memory_limit de volta para 1G (anteriormente eu levantei até 32GB) e agora funciona ...

Este projeto implementa o referido procedimento de maneira amigável ao modman. Basta instalá-lo com o compositor e pronto.

dkr
fonte
Isso realmente não responde à pergunta. Se você tiver uma pergunta diferente, faça-a clicando em Fazer pergunta . Você também pode adicionar uma recompensa para chamar mais atenção para essa pergunta quando tiver reputação suficiente .
Rajeev K Tomy 15/10
Sim, isso não responde à pergunta, mas ajudará as pessoas que precisam de uma solução temporária e sem discussão
dkr
corrigido um problema com o consumo de memória durante a importação. Interessante, o Magento testa de alguma forma o que eles estão lançando?
klipach
Isso resolve não apenas o problema de importação. Isso resolve uma grande quantidade de memória consumida pelo processo que cria / recria o cache e redimensiona versões para todas as imagens de produtos. Se eu enviar imagens png nos meus produtos, sem esse "hack", não funcionarei e obtenho muitos erros de memória esgotada.
precisa saber é o seguinte
Hoje eu encontrei essa sugestão. Eu o implementei e o vazamento de memória se foi. Então eu criei este github.com/borasocom-team/magento-gd2-memoryleak para instalá-lo de uma maneira limpa.
Dr. Gianluigi Zane Zanettini
5

Foi parte da correção de problemas de segurança com desserialização. Métodos mágicos como __destruct têm problemas inerentes à serialização.

Vimos explorações propostas que estavam usando serialização e __destruct para criar arquivos no sistema de arquivos - e essa alteração (você verá alterações mais semelhantes em outros lugares) foi feita para evitar isso.

Isso causa vazamento de memória ou apenas usa mais memória até o script terminar?

/security/77549/is-php-unserialize-exploitable-without-any-interesting-methods

Piotr Kaminski
fonte
Obrigado pelo contexto. Essa alteração específica foi feita para impedir uma exploração específica ou apenas para ter certeza?
Fabian Schmengler
E não, ele provavelmente só faz o script consumir mais memória, não uma fuga de memória real
Fabian Schmengler
Isso causa um grande vazamento de memória, especialmente ao importar imagens, pois mantém todos os arquivos de imagem abertos até o final do processamento da importação. Dessa forma, só podemos importar aproximadamente 50 produtos (antes disso, podemos fazer uma importação de> 2k sem aparência). Eu executei meu teste em uma VM local com 8G de RAM e os arquivos de origem têm aproximadamente 300 KB. Antes da alteração, a memória usada pelo PHP renomeia em 1k durante toda a importação.
Achim Rosenhagen
fschmengler é certo - ele pode não ser um 'vazamento de memória', mas o consumo sobe thraight a colina ;-)
Achim Rosenhagen
1
@ Alex, obrigado pelo conselho. Eu inverti o remendo. Agora, o vazamento de memória se foi, mas não há solução para o futuro.
Arne15:
4

Então, eu levantei um bug com o Magento, incluindo uma "solução" que deveria lidar com os problemas de uso de memória no processo de importação de imagens.

A solução pode ser encontrada no github em https://github.com/sitewards/import_image_memory_leak_fix, mas a idéia básica é.

Corrigindo o Mage_Catalog_Helper_Image::validateUploadFilepara realmente chamar o destructmétodo no processador de imagem. Infelizmente, parece que o padrão Varien_Imagenão lida com um, destructpor isso tivemos que adicionar nossa própria classe.

<?php
/**
 * @category    Sitewards
 * @package     Sitewards_ImportImageMemoryLeakFix
 * @copyright   Copyright (c) Sitewards GmbH (http://www.sitewards.com/)
 */
class Sitewards_ImportImageMemoryLeakFix_Model_Destructable_Image extends Varien_Image
{
    /**
     * Constructor,
     * difference from original constructor - we register a destructor here.
     *
     * @param string $sFileName
     * @param Varien_Image_Adapter $oAdapter Default value is GD2
     */
    public function __construct($sFileName = null, $oAdapter = Varien_Image_Adapter::ADAPTER_GD2)
    {
        parent::__construct($sFileName, $oAdapter);

        // Initialize shutdown function
        register_shutdown_function(array($this, 'destruct'));
    }

    /**
     * Destroy object image on shutdown
     */
    public function destruct()
    {
        $oAdapter = $this->_getAdapter();
        if (method_exists($oAdapter, 'destruct')) {
            $oAdapter->destruct();
        } else {
            Mage::log('Image can not be destructed properly, adapter doesn\'t support the method.');
        }
    }
}

E então uma reescrita do ajudante.

<?xml version="1.0"?>
<config>
    <modules>
        <Sitewards_ImportImageMemoryLeakFix>
            <version>0.1.0</version>
        </Sitewards_ImportImageMemoryLeakFix>
    </modules>
    <global>
        <models>
            <sitewards_importimagememoryleakfix>
                <class>Sitewards_ImportImageMemoryLeakFix_Model</class>
            </sitewards_importimagememoryleakfix>
        </models>
        <helpers>
            <catalog>
                <rewrite>
                    <image>Sitewards_ImportImageMemoryLeakFix_Helper_Catalog_Helper_Image</image>
                </rewrite>
            </catalog>
        </helpers>
    </global>
</config>

E a nova função chama a nova classe de imagem destrutível.

<?php
/**
 * @category    Sitewards
 * @package     Sitewards_ImportImageMemoryLeakFix
 * @copyright   Copyright (c) Sitewards GmbH (http://www.sitewards.com/)
 */
class Sitewards_ImportImageMemoryLeakFix_Helper_Catalog_Helper_Image extends Mage_Catalog_Helper_Image
{
    /**
     * Check - is this file an image
     *
     * Difference from original method - we destroy the image object here,
     * i.e. we are not wasting memory, without that fix product import with images
     * easily goes over 4Gb on memory with just couple hundreds of products.
     *
     * @param string $sFilePath
     *
     * @return bool
     * @throws Mage_Core_Exception
     */
    public function validateUploadFile($sFilePath) {
        if (!getimagesize($sFilePath)) {
            Mage::throwException($this->__('Disallowed file type.'));
        }

        /** @var Sitewards_ImportImageMemoryLeakFix_Model_Destructable_Image $oImageProcessor */
        $oImageProcessor = Mage::getModel('sitewards_importimagememoryleakfix/destructable_image', $sFilePath);
        $sMimeType       = $oImageProcessor->getMimeType();
        $oImageProcessor->destruct();

        return $sMimeType !== null;
    }
}
David Manners
fonte