laravel / framework

The Laravel Framework.
https://laravel.com
MIT License
32.45k stars 11k forks source link

Memory leak when using Http facade #51109

Closed rreynier closed 6 months ago

rreynier commented 6 months ago

Laravel Version

10.48.8

PHP Version

8.3.4

Database Driver & Version

No response

Description

When using the Http facade to make http requests, there appears to be a memory leak. I wrote a simple phpunit test on a clean Laravel install (10.48.8) like this:

for($i=1; $i < 1000; $i++) {
    Http::get('https://google.com');
    dump(memory_get_peak_usage() / 1000000 . 'MB');
}

In the terminal I see: image

I have scaled this up and memory just slowly rises, there does not seem to be a top off point (until you run out of memory with php).

I have also tried adding manual gc_collect_cycles() calls just in case without any help.

Steps To Reproduce

Write a unit test with:

namespace Tests\Unit;

use Illuminate\Support\Facades\Http;
use Tests\TestCase;

class ExampleTest extends TestCase
{
    public function testHttpPost()
    {
        Http::fake(['google.com' => Http::response(['ok' => 'ok'])]); // OPTIONAL

        for($i=1; $i < 10000; $i++) {
            Http::get('https://google.com');
            dump(memory_get_peak_usage() / 1000000 . 'MB');
        }
    }
}

Observe slow memory creep.

rreynier commented 6 months ago

Its worth noting that this is particularly problematic for long running processes like workers.

github-actions[bot] commented 6 months ago

Thank you for reporting this issue!

As Laravel is an open source project, we rely on the community to help us diagnose and fix issues as it is not possible to research and fix every issue reported to us via GitHub.

If possible, please make a pull request fixing the issue you have described, along with corresponding tests. All pull requests are promptly reviewed by the Laravel team.

Thank you!

timacdonald commented 6 months ago

I'm unable to replicate this issue on a fresh Laravel install.

composer create-project laravel/laravel http-test
cd http-test
mkdir -p app/Console/Commands
cat << 'EOF' > app/Console/Commands/HttpTest.php
<?php

namespace App\Console\Commands;

use Illuminate\Console\Command;
use Illuminate\Support\Facades\Http;

class HttpTest extends Command
{
    protected $signature = 'app:http-test';

    public function handle()
    {
        while (true) {
            Http::get('https://tim.macdonald.au/assets/css/style.css?id=aefb07c1238deb82d60c')->throw();

            gc_collect_cycles();

            dump(memory_get_peak_usage() / 1_000_000 . 'MB');
        }
    }
}
EOF
php artisan app:http-test
Screenshot 2024-04-18 at 10 19 39

I would say this is either a Laravel 10 issue or something specific to your application.

timacdonald commented 6 months ago

If you could create a minimal reproduction for this and we take a closer look?

laravel new bug-report --github="--public"
rreynier commented 6 months ago

Hmm! I was able to reproduce this on a completly fresh copy of laravel on version 11 also. I create a clean clone (git clone git@github.com:laravel/framework.git), checked out 11.x, copied .env.example, created keys, ran composer install, added the test... and mine instantly leaks. I'm going to have a coworker try this tomorrow though. Will keep you posted in what I find.

rreynier commented 6 months ago

I just did a bit more testing, it looks like the memory leak kinda starts randomly, in my case around the 185th iteration.


for($i=1; $i < 10000; $i++) {
    Http::get('https://google.com');
    gc_collect_cycles();
    dump("loop $i | " . memory_get_peak_usage() / 1000000 . 'MB');
}

Sample output:

vendor/bin/phpunit
PHPUnit 11.1.2 by Sebastian Bergmann and contributors.

Runtime:       PHP 8.3.4
Configuration: /Users/roeland/Projects/laravel/phpunit.xml

"loop 1 | 23.619352MB" // tests/Unit/ExampleTest.php:17
"loop 2 | 23.757112MB" // tests/Unit/ExampleTest.php:17
"loop 3 | 23.757112MB" // tests/Unit/ExampleTest.php:17
"loop 4 | 23.757112MB" // tests/Unit/ExampleTest.php:17
"loop 5 | 23.757112MB" // tests/Unit/ExampleTest.php:17
"loop 6 | 23.757112MB" // tests/Unit/ExampleTest.php:17
"loop 7 | 23.757112MB" // tests/Unit/ExampleTest.php:17
"loop 8 | 23.757112MB" // tests/Unit/ExampleTest.php:17
"loop 9 | 23.757112MB" // tests/Unit/ExampleTest.php:17
"loop 10 | 23.757112MB" // tests/Unit/ExampleTest.php:17
"loop 11 | 23.757112MB" // tests/Unit/ExampleTest.php:17
"loop 12 | 23.757112MB" // tests/Unit/ExampleTest.php:17
"loop 13 | 23.757112MB" // tests/Unit/ExampleTest.php:17
"loop 14 | 23.757112MB" // tests/Unit/ExampleTest.php:17
"loop 15 | 23.757112MB" // tests/Unit/ExampleTest.php:17
"loop 16 | 23.757112MB" // tests/Unit/ExampleTest.php:17
"loop 17 | 23.757112MB" // tests/Unit/ExampleTest.php:17
"loop 18 | 23.757112MB" // tests/Unit/ExampleTest.php:17
"loop 19 | 23.757112MB" // tests/Unit/ExampleTest.php:17
"loop 20 | 23.757112MB" // tests/Unit/ExampleTest.php:17
"loop 21 | 23.757112MB" // tests/Unit/ExampleTest.php:17
"loop 22 | 23.757112MB" // tests/Unit/ExampleTest.php:17
"loop 23 | 23.757112MB" // tests/Unit/ExampleTest.php:17
"loop 24 | 23.757112MB" // tests/Unit/ExampleTest.php:17
"loop 25 | 23.757112MB" // tests/Unit/ExampleTest.php:17
"loop 26 | 23.757112MB" // tests/Unit/ExampleTest.php:17
"loop 27 | 23.757112MB" // tests/Unit/ExampleTest.php:17
"loop 28 | 23.757112MB" // tests/Unit/ExampleTest.php:17
"loop 29 | 23.757112MB" // tests/Unit/ExampleTest.php:17
"loop 30 | 23.757112MB" // tests/Unit/ExampleTest.php:17
"loop 31 | 23.757112MB" // tests/Unit/ExampleTest.php:17
"loop 32 | 23.757112MB" // tests/Unit/ExampleTest.php:17
"loop 33 | 23.757112MB" // tests/Unit/ExampleTest.php:17
"loop 34 | 23.757112MB" // tests/Unit/ExampleTest.php:17
"loop 35 | 23.757112MB" // tests/Unit/ExampleTest.php:17
"loop 36 | 23.757112MB" // tests/Unit/ExampleTest.php:17
"loop 37 | 23.757112MB" // tests/Unit/ExampleTest.php:17
"loop 38 | 23.757112MB" // tests/Unit/ExampleTest.php:17
"loop 39 | 23.757112MB" // tests/Unit/ExampleTest.php:17
"loop 40 | 23.757112MB" // tests/Unit/ExampleTest.php:17
"loop 41 | 23.757112MB" // tests/Unit/ExampleTest.php:17
"loop 42 | 23.757112MB" // tests/Unit/ExampleTest.php:17
"loop 43 | 23.757112MB" // tests/Unit/ExampleTest.php:17
"loop 44 | 23.757112MB" // tests/Unit/ExampleTest.php:17
"loop 45 | 23.757112MB" // tests/Unit/ExampleTest.php:17
"loop 46 | 23.757112MB" // tests/Unit/ExampleTest.php:17
"loop 47 | 23.757112MB" // tests/Unit/ExampleTest.php:17
"loop 48 | 23.757112MB" // tests/Unit/ExampleTest.php:17
"loop 49 | 23.757112MB" // tests/Unit/ExampleTest.php:17
"loop 50 | 23.757112MB" // tests/Unit/ExampleTest.php:17
"loop 51 | 23.757112MB" // tests/Unit/ExampleTest.php:17
"loop 52 | 23.757112MB" // tests/Unit/ExampleTest.php:17
"loop 53 | 23.757112MB" // tests/Unit/ExampleTest.php:17
"loop 54 | 23.757112MB" // tests/Unit/ExampleTest.php:17
"loop 55 | 23.757112MB" // tests/Unit/ExampleTest.php:17
"loop 56 | 23.757112MB" // tests/Unit/ExampleTest.php:17
"loop 57 | 23.757112MB" // tests/Unit/ExampleTest.php:17
"loop 58 | 23.757112MB" // tests/Unit/ExampleTest.php:17
"loop 59 | 23.757112MB" // tests/Unit/ExampleTest.php:17
"loop 60 | 23.757112MB" // tests/Unit/ExampleTest.php:17
"loop 61 | 23.757112MB" // tests/Unit/ExampleTest.php:17
"loop 62 | 23.757112MB" // tests/Unit/ExampleTest.php:17
"loop 63 | 23.757112MB" // tests/Unit/ExampleTest.php:17
"loop 64 | 23.757112MB" // tests/Unit/ExampleTest.php:17
"loop 65 | 23.757112MB" // tests/Unit/ExampleTest.php:17
"loop 66 | 23.757112MB" // tests/Unit/ExampleTest.php:17
"loop 67 | 23.757112MB" // tests/Unit/ExampleTest.php:17
"loop 68 | 23.757112MB" // tests/Unit/ExampleTest.php:17
"loop 69 | 23.757112MB" // tests/Unit/ExampleTest.php:17
"loop 70 | 23.757112MB" // tests/Unit/ExampleTest.php:17
"loop 71 | 23.757112MB" // tests/Unit/ExampleTest.php:17
"loop 72 | 23.757112MB" // tests/Unit/ExampleTest.php:17
"loop 73 | 23.757112MB" // tests/Unit/ExampleTest.php:17
"loop 74 | 23.757112MB" // tests/Unit/ExampleTest.php:17
"loop 75 | 23.757112MB" // tests/Unit/ExampleTest.php:17
"loop 76 | 23.757112MB" // tests/Unit/ExampleTest.php:17
"loop 77 | 23.757112MB" // tests/Unit/ExampleTest.php:17
"loop 78 | 23.757112MB" // tests/Unit/ExampleTest.php:17
"loop 79 | 23.757112MB" // tests/Unit/ExampleTest.php:17
"loop 80 | 23.757112MB" // tests/Unit/ExampleTest.php:17
"loop 81 | 23.757112MB" // tests/Unit/ExampleTest.php:17
"loop 82 | 23.757112MB" // tests/Unit/ExampleTest.php:17
"loop 83 | 23.757112MB" // tests/Unit/ExampleTest.php:17
"loop 84 | 23.757112MB" // tests/Unit/ExampleTest.php:17
"loop 85 | 23.757112MB" // tests/Unit/ExampleTest.php:17
"loop 86 | 23.757112MB" // tests/Unit/ExampleTest.php:17
"loop 87 | 23.757112MB" // tests/Unit/ExampleTest.php:17
"loop 88 | 23.757112MB" // tests/Unit/ExampleTest.php:17
"loop 89 | 23.757112MB" // tests/Unit/ExampleTest.php:17
"loop 90 | 23.757112MB" // tests/Unit/ExampleTest.php:17
"loop 91 | 23.757112MB" // tests/Unit/ExampleTest.php:17
"loop 92 | 23.757112MB" // tests/Unit/ExampleTest.php:17
"loop 93 | 23.757112MB" // tests/Unit/ExampleTest.php:17
"loop 94 | 23.757112MB" // tests/Unit/ExampleTest.php:17
"loop 95 | 23.757112MB" // tests/Unit/ExampleTest.php:17
"loop 96 | 23.757112MB" // tests/Unit/ExampleTest.php:17
"loop 97 | 23.757112MB" // tests/Unit/ExampleTest.php:17
"loop 98 | 23.757112MB" // tests/Unit/ExampleTest.php:17
"loop 99 | 23.757112MB" // tests/Unit/ExampleTest.php:17
"loop 100 | 23.757112MB" // tests/Unit/ExampleTest.php:17
"loop 101 | 23.757112MB" // tests/Unit/ExampleTest.php:17
"loop 102 | 23.757112MB" // tests/Unit/ExampleTest.php:17
"loop 103 | 23.757112MB" // tests/Unit/ExampleTest.php:17
"loop 104 | 23.757112MB" // tests/Unit/ExampleTest.php:17
"loop 105 | 23.757112MB" // tests/Unit/ExampleTest.php:17
"loop 106 | 23.757112MB" // tests/Unit/ExampleTest.php:17
"loop 107 | 23.757112MB" // tests/Unit/ExampleTest.php:17
"loop 108 | 23.757112MB" // tests/Unit/ExampleTest.php:17
"loop 109 | 23.757112MB" // tests/Unit/ExampleTest.php:17
"loop 110 | 23.757112MB" // tests/Unit/ExampleTest.php:17
"loop 111 | 23.757112MB" // tests/Unit/ExampleTest.php:17
"loop 112 | 23.757112MB" // tests/Unit/ExampleTest.php:17
"loop 113 | 23.757112MB" // tests/Unit/ExampleTest.php:17
"loop 114 | 23.757112MB" // tests/Unit/ExampleTest.php:17
"loop 115 | 23.757112MB" // tests/Unit/ExampleTest.php:17
"loop 116 | 23.757112MB" // tests/Unit/ExampleTest.php:17
"loop 117 | 23.757112MB" // tests/Unit/ExampleTest.php:17
"loop 118 | 23.757112MB" // tests/Unit/ExampleTest.php:17
"loop 119 | 23.757112MB" // tests/Unit/ExampleTest.php:17
"loop 120 | 23.757112MB" // tests/Unit/ExampleTest.php:17
"loop 121 | 23.757112MB" // tests/Unit/ExampleTest.php:17
"loop 122 | 23.757112MB" // tests/Unit/ExampleTest.php:17
"loop 123 | 23.757112MB" // tests/Unit/ExampleTest.php:17
"loop 124 | 23.757112MB" // tests/Unit/ExampleTest.php:17
"loop 125 | 23.757112MB" // tests/Unit/ExampleTest.php:17
"loop 126 | 23.757112MB" // tests/Unit/ExampleTest.php:17
"loop 127 | 23.757112MB" // tests/Unit/ExampleTest.php:17
"loop 128 | 23.757112MB" // tests/Unit/ExampleTest.php:17
"loop 129 | 23.757112MB" // tests/Unit/ExampleTest.php:17
"loop 130 | 23.757112MB" // tests/Unit/ExampleTest.php:17
"loop 131 | 23.757112MB" // tests/Unit/ExampleTest.php:17
"loop 132 | 23.757112MB" // tests/Unit/ExampleTest.php:17
"loop 133 | 23.757112MB" // tests/Unit/ExampleTest.php:17
"loop 134 | 23.757112MB" // tests/Unit/ExampleTest.php:17
"loop 135 | 23.757112MB" // tests/Unit/ExampleTest.php:17
"loop 136 | 23.757112MB" // tests/Unit/ExampleTest.php:17
"loop 137 | 23.757112MB" // tests/Unit/ExampleTest.php:17
"loop 138 | 23.757112MB" // tests/Unit/ExampleTest.php:17
"loop 139 | 23.757112MB" // tests/Unit/ExampleTest.php:17
"loop 140 | 23.757112MB" // tests/Unit/ExampleTest.php:17
"loop 141 | 23.757112MB" // tests/Unit/ExampleTest.php:17
"loop 142 | 23.757112MB" // tests/Unit/ExampleTest.php:17
"loop 143 | 23.757112MB" // tests/Unit/ExampleTest.php:17
"loop 144 | 23.757112MB" // tests/Unit/ExampleTest.php:17
"loop 145 | 23.757112MB" // tests/Unit/ExampleTest.php:17
"loop 146 | 23.757112MB" // tests/Unit/ExampleTest.php:17
"loop 147 | 23.757112MB" // tests/Unit/ExampleTest.php:17
"loop 148 | 23.757112MB" // tests/Unit/ExampleTest.php:17
"loop 149 | 23.757112MB" // tests/Unit/ExampleTest.php:17
"loop 150 | 23.757112MB" // tests/Unit/ExampleTest.php:17
"loop 151 | 23.757112MB" // tests/Unit/ExampleTest.php:17
"loop 152 | 23.757112MB" // tests/Unit/ExampleTest.php:17
"loop 153 | 23.757112MB" // tests/Unit/ExampleTest.php:17
"loop 154 | 23.757112MB" // tests/Unit/ExampleTest.php:17
"loop 155 | 23.757112MB" // tests/Unit/ExampleTest.php:17
"loop 156 | 23.757112MB" // tests/Unit/ExampleTest.php:17
"loop 157 | 23.757112MB" // tests/Unit/ExampleTest.php:17
"loop 158 | 23.757112MB" // tests/Unit/ExampleTest.php:17
"loop 159 | 23.757112MB" // tests/Unit/ExampleTest.php:17
"loop 160 | 23.757112MB" // tests/Unit/ExampleTest.php:17
"loop 161 | 23.757112MB" // tests/Unit/ExampleTest.php:17
"loop 162 | 23.757112MB" // tests/Unit/ExampleTest.php:17
"loop 163 | 23.757112MB" // tests/Unit/ExampleTest.php:17
"loop 164 | 23.757112MB" // tests/Unit/ExampleTest.php:17
"loop 165 | 23.757112MB" // tests/Unit/ExampleTest.php:17
"loop 166 | 23.757112MB" // tests/Unit/ExampleTest.php:17
"loop 167 | 23.757112MB" // tests/Unit/ExampleTest.php:17
"loop 168 | 23.757112MB" // tests/Unit/ExampleTest.php:17
"loop 169 | 23.757112MB" // tests/Unit/ExampleTest.php:17
"loop 170 | 23.757112MB" // tests/Unit/ExampleTest.php:17
"loop 171 | 23.757112MB" // tests/Unit/ExampleTest.php:17
"loop 172 | 23.757112MB" // tests/Unit/ExampleTest.php:17
"loop 173 | 23.757112MB" // tests/Unit/ExampleTest.php:17
"loop 174 | 23.757112MB" // tests/Unit/ExampleTest.php:17
"loop 175 | 23.757112MB" // tests/Unit/ExampleTest.php:17
"loop 176 | 23.757112MB" // tests/Unit/ExampleTest.php:17
"loop 177 | 23.757112MB" // tests/Unit/ExampleTest.php:17
"loop 178 | 23.757112MB" // tests/Unit/ExampleTest.php:17
"loop 179 | 23.757112MB" // tests/Unit/ExampleTest.php:17
"loop 180 | 23.757112MB" // tests/Unit/ExampleTest.php:17
"loop 181 | 23.757112MB" // tests/Unit/ExampleTest.php:17
"loop 182 | 23.757112MB" // tests/Unit/ExampleTest.php:17
"loop 183 | 23.757112MB" // tests/Unit/ExampleTest.php:17
"loop 184 | 23.75716MB" // tests/Unit/ExampleTest.php:17
"loop 185 | 23.75716MB" // tests/Unit/ExampleTest.php:17
"loop 186 | 23.757288MB" // tests/Unit/ExampleTest.php:17
"loop 187 | 23.757656MB" // tests/Unit/ExampleTest.php:17
"loop 188 | 23.758632MB" // tests/Unit/ExampleTest.php:17
"loop 189 | 23.759MB" // tests/Unit/ExampleTest.php:17
"loop 190 | 23.759MB" // tests/Unit/ExampleTest.php:17
"loop 191 | 23.759128MB" // tests/Unit/ExampleTest.php:17
"loop 192 | 23.759496MB" // tests/Unit/ExampleTest.php:17
"loop 193 | 23.760472MB" // tests/Unit/ExampleTest.php:17
"loop 194 | 23.76084MB" // tests/Unit/ExampleTest.php:17
"loop 195 | 23.761208MB" // tests/Unit/ExampleTest.php:17
"loop 196 | 23.761208MB" // tests/Unit/ExampleTest.php:17
"loop 197 | 23.761336MB" // tests/Unit/ExampleTest.php:17
"loop 198 | 23.762312MB" // tests/Unit/ExampleTest.php:17
"loop 199 | 23.76268MB" // tests/Unit/ExampleTest.php:17
"loop 200 | 23.763048MB" // tests/Unit/ExampleTest.php:17
"loop 201 | 23.763416MB" // tests/Unit/ExampleTest.php:17
"loop 202 | 23.763784MB" // tests/Unit/ExampleTest.php:17
"loop 203 | 23.763784MB" // tests/Unit/ExampleTest.php:17
"loop 204 | 23.76452MB" // tests/Unit/ExampleTest.php:17
"loop 205 | 23.764888MB" // tests/Unit/ExampleTest.php:17
"loop 206 | 23.764888MB" // tests/Unit/ExampleTest.php:17
"loop 207 | 23.765016MB" // tests/Unit/ExampleTest.php:17
"loop 208 | 23.765384MB" // tests/Unit/ExampleTest.php:17
"loop 209 | 23.765752MB" // tests/Unit/ExampleTest.php:17
"loop 210 | 23.76612MB" // tests/Unit/ExampleTest.php:17
"loop 211 | 23.766488MB" // tests/Unit/ExampleTest.php:17
"loop 212 | 23.767464MB" // tests/Unit/ExampleTest.php:17
"loop 213 | 23.767832MB" // tests/Unit/ExampleTest.php:17
"loop 214 | 23.767832MB" // tests/Unit/ExampleTest.php:17
"loop 215 | 23.768568MB" // tests/Unit/ExampleTest.php:17
"loop 216 | 23.768568MB" // tests/Unit/ExampleTest.php:17
"loop 217 | 23.769304MB" // tests/Unit/ExampleTest.php:17
"loop 218 | 23.769304MB" // tests/Unit/ExampleTest.php:17
"loop 219 | 23.769432MB" // tests/Unit/ExampleTest.php:17
"loop 220 | 23.770408MB" // tests/Unit/ExampleTest.php:17
"loop 221 | 23.770776MB" // tests/Unit/ExampleTest.php:17
"loop 222 | 23.771144MB" // tests/Unit/ExampleTest.php:17
"loop 223 | 23.771512MB" // tests/Unit/ExampleTest.php:17
"loop 224 | 23.771512MB" // tests/Unit/ExampleTest.php:17
"loop 225 | 23.772248MB" // tests/Unit/ExampleTest.php:17
"loop 226 | 23.772616MB" // tests/Unit/ExampleTest.php:17
"loop 227 | 23.772984MB" // tests/Unit/ExampleTest.php:17
"loop 228 | 23.773352MB" // tests/Unit/ExampleTest.php:17
"loop 229 | 23.773352MB" // tests/Unit/ExampleTest.php:17
"loop 230 | 23.774088MB" // tests/Unit/ExampleTest.php:17
"loop 231 | 23.774088MB" // tests/Unit/ExampleTest.php:17
"loop 232 | 23.774216MB" // tests/Unit/ExampleTest.php:17
"loop 233 | 23.775192MB" // tests/Unit/ExampleTest.php:17
"loop 234 | 23.775192MB" // tests/Unit/ExampleTest.php:17
...

When I use Http::fake() and loop through thousands its a bit more obvious since you get the results so quickly.

timacdonald commented 6 months ago

Okay, I can see this kick into gear around ~218th loop (using a while loop).

timacdonald commented 6 months ago

On further investigation:

Update to report actual memory used and reporting via echo.

<?php

namespace App\Console\Commands;

use Illuminate\Console\Command;
use Illuminate\Support\Facades\Http;

class HttpTest extends Command
{
    protected $signature = 'app:http-test';

    public function handle()
    {
        while (true) {
            Http::get('https://tim.macdonald.au/assets/css/style.css?id=aefb07c1238deb82d60c')->throw();

            gc_collect_cycles();

            echo memory_get_usage() / 1_000_000 . 'MB'.PHP_EOL;
        }
    }
}

After the first report, every subsequent echo reported exactly the same value with 1,200+ invocations.

I'm also seeing that memory_get_peak_usage is stable with this change to echo as well.

Looks like it might be something to do with the underlying dumper.

rreynier commented 6 months ago

Huh, yah there seems to be a couple areas where something is leaking.

I have updated my test to look like this:

class ExampleTest extends TestCase
{
    public function testHttpPost()
    {
        Http::fake(['bythepixel.com' => Http::response(['ok' => 'ok'])]);

        $startMemoryUsage = memory_get_usage();
        $startMemoryPeakUsage = memory_get_peak_usage();

        for($i=1; $i < 10000; $i++) {
            Http::get('https://bythepixel.com');
            gc_collect_cycles();
        }

        $endMemoryUsage = memory_get_usage();
        $endMemoryPeakUsage = memory_get_peak_usage();

        echo "\nStart memory usage: {$this->formatMB($startMemoryUsage)}";
        echo "\nStart memory peak usage: {$this->formatMB($startMemoryPeakUsage)}";
        echo "\nEnd memory usage: {$this->formatMB($endMemoryUsage)}";
        echo "\nEnd memory peak usage: {$this->formatMB($endMemoryPeakUsage)}";
    }

    private function formatMB($number): string
    {
        return $number / 1000000 . 'MB';
    }
}

I ran test with Http::fake() and without. Here are my results:

Test Start Start Peak End End Peak
100 with fake() 22.1004 22.141704 23.683008 23.7084
1000 with fake() 22.1004 22.141704 26.750112 26.775504
10000 with fake() 22.1004 22.141704 57.987424 58.072368
20000 with fake() 22.1004 22.141704 92.6446 92.811464
100 to bythepixel.com 21.817192 21.9164 23.351848 23.63004
1000 to bythepixel.com 21.817192 21.9164 23.351848 23.63004
2000 to bythepixel.com 21.817192 21.9164 23.351848 23.63004

My conclusion from this little experiment is that dump() and Http::fake() have memory issues. But actual usage of the Facade is JUST FINE!

Thanks for helping me look into this!

timacdonald commented 6 months ago

No worries. Was kinda fun digging into this one.

PrintNow commented 4 months ago

@rreynier @timacdonald

Maybe you need to do it manually close the stream and any underlying resources: https://github.com/laravel/framework/blob/980e8d5cd0bfe72f2cd878a785e8d2c22eefaf18/src/Illuminate/Http/Client/Response.php#L243-L254

before:

for($i=1; $i < 1000; $i++) {
    Http::get('https://google.com');
    dump(memory_get_peak_usage() / 1000000 . 'MB');
}

after:

    $response = Http::get('https://google.com');
    // do something...
    $response->close();

    dump(memory_get_peak_usage() / 1000000 . 'MB');

by: https://nowtime.cc/php/1854.html