[php] allowed memory size exhausted - strings te groot?

Pagina: 1
Acties:

Onderwerpen


  • marty
  • Registratie: Augustus 2002
  • Laatst online: 27-03-2023
Ik heb zelf een email client gemaakt die redelijk goed werkt (al zeg ik het zelf :))
Tenminste...alles verliep soepel, totdat er vandaag iemand een mailtje met 10 plaatjes kreeg die in totaal zo'n 1.6MB vertegenwoordigen.

Omdat de mails van de mailserver verwijderd worden (en in de database opgeslagen) en ik toch nog een beetje huiverig ben voor fouten maak ik van iedere mail (alvorens ik 'm van de mailserver laat verwijderen) een backup. Op die manier kan ik altijd nog iets terughalen mocht er iets verkeerd gaan. (de mails worden namelijk in verwerkte form in de mail-tabel opgeslagen.

stukje code dat de backup doet:
PHP:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
$query = "INSERT INTO mail_backup(
            mail_id,
            str_header,
            str_structure,
            obj_header,
            obj_structure)
        VALUES (
            '".$this_id."',
            '".$full_header."',
            '".$full_structure."',
            '".addslashes(serialize($headers))."',
            '".addslashes(serialize($structure))."')";
if (!$result = mysql_query($query))
{
    mail("mijn@email.nl", "Error met opslaan van backup-mail", $query."\n".mysql_error());
    $display_message .= "het opslaan van de backup is mislukt (".$this_id.")<br>\n";
}


Nou trad daar in eerste instantie ook de fout op:
Fatal error: Allowed memory size of 9823800 bytes exhausted at (null):0 (tried to allocate 2301875 bytes) in /home/virtual/site1/fst/var/www/html/database/mail/functions.php on line 313
die 313 is toevallig de regel waar die mail() plaatsvindt. Het backuppen mislukt dus ook. De mail zelf is echter wel succesvol geparsed, opgeslagen en de attachments ook (die koppel ik los en sla ik op de harde schijf van de server op)

Bij herhaling blijkt alleen dat die exhaust random plaatsvindt met random groottes (omdat het backuppen mislukt wordt de mail niet van de server verwijderd namelijk).
Ik heb boven regel 313 even een regel toegevoegd, waarbij ik de query zelf heb weggelaten in de mail, en alleen de error naar mezelf toemail en die zegt dan vervolgens:
MySQL server has gone away

Nouja...ik weet eigenlijk niet zo goed wat ik hier mee moet. Wat kennelijk het geval is, is dat php het niet trekt dat die strings zo groot zijn waarmee gewerkt wordt.
Maar hoe kan ik dit dan op een goede manier backuppen?

  • RedRose
  • Registratie: Juni 2001
  • Niet online

RedRose

Icebear

Ik snap dit niet helemaal:
PHP:
1
2
3
            '".$full_structure."', 
            '".addslashes(serialize($headers))."', 
            '".addslashes(serialize($structure))."')"; 
$structure en $full_structure zijn deels hetzelfde? Zo ja dan kan je dat misschien beter scheiden of in één keer in de database zetten. Verder vermoed ik dat MySQL ook redelijk op zijn muil gaat. Is het een snelle server met veel geheugencapaciteit of niet? Weet je heel zeker dat je de plaatjes niet ook in de database opslaat? Probeer eens of je $full_structure of $structure kan echo-en.

Als het script 9 mb aan geheugen nodig heeft, dan denk ik dat je ongeveer 7x de data van dat mailtje in ongeveer 7x verschillende strings hebt staan. Misschien kan je daar ook eens naar kijken?

Ik zou in ieder geval niet het geheugenmaximum opschroeven dat php mag gebruiken.

Sundown Circus


  • marty
  • Registratie: Augustus 2002
  • Laatst online: 27-03-2023
RedRose schreef op 24 September 2003 @ 16:46:
Ik snap dit niet helemaal:
PHP:
1
2
3
            '".$full_structure."', 
            '".addslashes(serialize($headers))."', 
            '".addslashes(serialize($structure))."')"; 
$structure en $full_structure zijn deels hetzelfde? Zo ja dan kan je dat misschien beter scheiden of in één keer in de database zetten.
de eerste is een object, de tweede een string. Ik wil het object namelijk ook bewaren om geen gegevens te verliezen.
Verder vermoed ik dat MySQL ook redelijk op zijn muil gaat. Is het een snelle server met veel geheugencapaciteit of niet?
Dat vermoeden heb ik ook heel erg :)
En ja, het is een snelle server met aardig wat geheugencapaciteit. Er draait ook maar 1 domein op.
Weet je heel zeker dat je de plaatjes niet ook in de database opslaat?
Ik weet heel zeker dat ik dat wel doe :) (in de backup, that is - niet in de mail-tabel)
Het is immers een backup, dus die plaatjes wil ik er ook bij. en die zitten gewoon in de rauwe string van de email
Probeer eens of je $full_structure of $structure kan echo-en.
Daar zit het probleem niet. Dit script draait al een tijdje en heeft inmiddels al ruim 1400 succesvolle backups gemaakt. (ja, dat kan dus)
Als het script 9 mb aan geheugen nodig heeft, dan denk ik dat je ongeveer 7x de data van dat mailtje in ongeveer 7x verschillende strings hebt staan. Misschien kan je daar ook eens naar kijken?
PHP:
1
2
3
4
5
6
7
        $headers    = imap_header($inbox, $x);
        $structure  = imap_fetchstructure($inbox, $x);

        # [... knip ...]

        $full_header    = addslashes(imap_fetchheader($inbox, $x));
        $full_structure = addslashes(imap_body($inbox, $x));


Daarna gebruik ik ze alleen nog in het stukje wat ik in m'n eerste post heb gezet

  • GiLuX
  • Registratie: Juni 1999
  • Laatst online: 18-11-2022
ik neem aan dat je dit al had gevonden,
lijkt er erder op een timeout dan een datalimit

http://www.mysql.com/doc/en/Gone_away.html

"I disagree with what you are saying, but I will defend to the death your right to say it." -- not clear who


  • RedRose
  • Registratie: Juni 2001
  • Niet online

RedRose

Icebear

marty schreef op 24 september 2003 @ 17:23:
de eerste is een object, de tweede een string. Ik wil het object namelijk ook bewaren om geen gegevens te verliezen.
Maar zijn ze allebei hetzelfde? Dan hoef je ze niet allebei op te slaan lijkt mij. :)
Dat vermoeden heb ik ook heel erg :) En ja, het is een snelle server met aardig wat geheugencapaciteit. Er draait ook maar 1 domein op.

Ik weet heel zeker dat ik dat wel doe :) (in de backup, that is - niet in de mail-tabel)
Het is immers een backup, dus die plaatjes wil ik er ook bij. en die zitten gewoon in de rauwe string van de email
Daar zit het probleem niet. Dit script draait al een tijdje en heeft inmiddels al ruim 1400 succesvolle backups gemaakt. (ja, dat kan dus)
Ja ok, maar bij mails die groot zijn gaat het dan dus mis. ;)
PHP:
1
2
3
4
5
6
7
8
9
$headers    = imap_header($inbox, $x);
$structure = imap_fetchstructure($inbox, $x);

# [... knip ...]

$full_header    = addslashes
(imap_fetchheader($inbox, $x));
$full_structure = addslashes
(imap_body($inbox, $x));

Daarna gebruik ik ze alleen nog in het stukje wat ik in m'n eerste post heb gezet
Ok je hebt dus twee problemen:
• Het PHP-script zelf timed-out;
• MySQL timed out (zie ook GiLuX);

Probeer in ieder geval om de grote strings maar 1 keer in zijn totaal te hebben (dan wel het object, dan wel die andere string). Unset dus meteen die strings die je niet meer nodig hebt. Als je geheugen wordt volgepropt tot 9Mb terwijl de data van het mailtje 1,6 Mb is, dan weet je dat je een paar van die strings te veel hebt. ;)

Ander idee: sla de strings die je gaat backuppen niet op in MySQL, maar in het filesystem zelf. Je kan je reeds in MySQL opgeslagen mailtjes dan immers als referentie gebruiken naar de als bestanden opgeslagen backups? Ik denk dat dat in ieder geval ook al weer een stukje sneller gaat. :)

Sundown Circus


  • marty
  • Registratie: Augustus 2002
  • Laatst online: 27-03-2023
Oke....ik ben inmiddels afgestapt van het opslaan in mysql. Ik vond dat eigenlijk handiger, omdat ik dan de rauwe emails heel simpel met een query op de 'geparste' email kon koppelen...maar ok. Als dit strax werkt is het toch alleen maar voor noodgevallen (hopelijk), dus echt vaak gebruiken zal ik het niet.

Ik heb m'n functie aangepast en unset nu gelijk alles dat groot kan zijn en niet meer gebruikt wordt. Hij loop alleen nog steeds vast op een class die ik geschreven heb
Ik gebruik deze class om een email uit elkaar te trekken: het tekstgedeelte, html gedeelte en de attachments

PHP:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
class disectMail
{
     // message types
    var $type = array("text", "multipart", "message", "application", "audio", "image", "video", "other");
     // message encodings
    var $encoding = array("7bit", "8bit", "binary", "base64", "quoted-printable", "other");

    var $body       = array();
    var $attachments    = array();
    var $a_index        = 0;
    var $e_index        = 0;

    function disectMail($inbox, $x)
    {
        $i = 1;
        while ($fetchbody = imap_fetchbody($inbox, $x, $i))
        {
            $this->parsePart(imap_bodystruct($inbox, $x, $i), $fetchbody);
            $j = 1;
            while($fetchbody = imap_fetchbody($inbox, $x, $i.".".$j))
            {
                $this->parsePart(imap_bodystruct($inbox, $x, $i.".".$j), $fetchbody);
                $k = 1;
                while($fetchbody = imap_fetchbody($inbox, $x, $i.".".$j.".".$k))
                {
                    $this->parsePart(imap_bodystruct($inbox, $x, $i.".".$j.".".$k), $fetchbody);
                    $k++;
                }
                $j++;
            }
            $i++;
        }
    }

    function parsePart($bodystruct, $fetchbody)
    {
        $type       = $this->type[$bodystruct->type];
        $subtype    = $bodystruct->subtype;
        $encoding   = $this->encoding[$bodystruct->encoding];

        if ($bodystruct->ifdisposition == 1)
            $disposition    = $bodystruct->disposition;
        elseif (isset($disposition))    // ifdisposition must be 0, we unset any previously set disposition
            unset($disposition);

         // if true, this is a message and we need to display it
        if (($type == "text" || $type == "message") && !isset($disposition))
        {
            if (strtolower($subtype) == "html")
                $this->body['html'] = $this->decode($fetchbody, $encoding);
            else
                $this->body['text'] = $this->decode($fetchbody, $encoding);
        }
        elseif (isset($disposition))
        {
            $params = $bodystruct->dparameters;
            if (is_array($params))
            {
                foreach ($params as $p)
                {
                    if($p->attribute == "FILENAME")
                    {
                        $this->attachments[$this->a_index]['name'] = $p->value;
                        $this->attachments[$this->a_index]['size'] = $bodystruct->size;
                        $this->attachments[$this->a_index]['content'] = $this->decode($fetchbody, $encoding);
                        $this->a_index++;
                        break;
                    }
                }
            }
            if ($fp = fopen("temp/att_".$att_name, "w+"))
            {
                fputs($fp, $this->decode($fetchbody, $encoding));
                fclose($fp);
            }
            
        }
        elseif($type != "multipart")
        {
            $params = $bodystruct->parameters;
            foreach ($params as $p)
            {
                if($p->attribute == "NAME")
                {
                    $this->embed[$this->e_index]['id']  = preg_replace("(<||>)", "", $bodystruct->id);
                    $this->embed[$this->e_index]['name']    = $p->value;
                    $this->embed[$this->e_index]['content'] = $this->decode($fetchbody, $encoding);
                    $this->e_index++;
                    break;
                }
            }   

        }
    }

    function decode($string, $enc)
    {
        switch($enc)
        {
            case "quoted-printable":
                $output = imap_qprint($string);
                break;
            case "base64":
                $output = imap_base64($string);
                break;
            default:
                $output = $string;
                break;
        }
        return $output;
    }
}


Bij een grote email (heb om te testen ff iets naar mezelf gestuurd met een attachment van 3,5MB) loopt ie nu standaard op regel 104 vast (met die exhausted melding).

Maar ik heb geen idee hoe ik dit nog verder kan optimaliseren.


oh, die 3 while loops (16 t/m 31) is niet echt netjes...dat moet eigenlijk vervangen worden door een recursieve functie maar dat wilde niet lukken

  • marty
  • Registratie: Augustus 2002
  • Laatst online: 27-03-2023
k, ik heb het zelf op kunnen lossen.

in m'n functie heb ik eerst alle headerinformatie zitten parsen, en vervolgens $header ge-unset. daarna pas dat object aangemaakt, de nuttige informatie uit dat object in strings en arrays gestopt en het object weer ge-unset
en in die class heb ik die $fetchbody nog op drie plaatsen ge-unset en $string ook nog aan het einde van de decode functie.

and that did the trick.

heb alleen wel het gevoel dat het aan een zijde draadje hangt....
nouja..ik merk het wel.

als iemand nog een nuttige opmerking heeft over 3 while loops en hoe die recursief te maken (zie dus ook [rml][ php] mail en (sub)parts[/rml]) dan hoor ik dat graag :)

Acties:
  • 0 Henk 'm!

  • marty
  • Registratie: Augustus 2002
  • Laatst online: 27-03-2023
pindakaas.....
marty schreef op 25 September 2003 @ 23:50:
heb alleen wel het gevoel dat het aan een zijde draadje hangt....
dat deed het dus ook.
Een attachment van 4.2MB Bleek te groot :'(

Dus...als iemand nog suggesties heeft over hoe ik deze code kan optimaliseren dan hoor ik dat heeeel graag, want zou het nu dus echt niet meer weten.
Het moet toch gewoon mogelijk zijn emails met grote attachments te verwerken??
Pagina: 1