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.

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.


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.


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


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;
    }
}
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.