psalm-plugin-laravel icon indicating copy to clipboard operation
psalm-plugin-laravel copied to clipboard

Endless memory consumption and aborting

Open maximal opened this issue 3 years ago • 21 comments

Describe the bug I have an issue similar to @vladyslavstartsev’s #90. On the project I currently want to refactor this plugin fails with endless memory consumption on one file every time. I limited the Psalm config with the directory containing this file only, but this didn’t help. Without the plugin Psalm runs okay but the results are unusable as they have many Laravel-specific issues.

Impacted Versions

$ composer show | grep -E 'psalm|laravel'
barryvdh/laravel-ide-helper           v2.8.1              Laravel IDE Helper, generates correct PHPDocs for all F...
beyondcode/laravel-websockets         1.9.0               An easy to use WebSocket server
emadadly/laravel-uuid                 v1.3.2              laravel uuid a simple, automatic UUID generator for any...
fruitcake/laravel-cors                v1.0.6              Adds CORS (Cross-Origin Resource Sharing) headers suppo...
laravel/framework                     v8.17.2             The Laravel Framework.
laravel/socialite                     v5.1.2              Laravel wrapper around OAuth 1 & OAuth 2 libraries.
laravel/tinker                        v2.5.0              Powerful REPL for the Laravel framework.
laravel/ui                            v2.3.0              Laravel UI utilities and presets.
laravelcollective/html                v6.2.0              HTML and Form Builders for the Laravel Framework
orklah/psalm-insane-comparison        v1.0.0              Detects possible insane comparison ("string" == 0) to h...
psalm/plugin-laravel                  v1.4.1              A Laravel plugin for Psalm
sentry/sentry-laravel                 1.9.0               Laravel SDK for Sentry (https://sentry.io)
vimeo/psalm                           4.3.1               A static analysis tool for finding errors in PHP applic...

Additional context I can send you the whole project archive (in private only). Specific file which aborts the analysis is a hell of a mess, but in order to fix this it would be perfect to have some handy Psalm recomendations.

maximal avatar Dec 06 '20 04:12 maximal

I have a feeling this is related to a cyclic dependency in the container perhaps. We result to some "dynamic analysis" in some parts of laravel, such as using the container to resolve dependencies.


We use https://github.com/barryvdh/laravel-ide-helper under the hood. Curious, are you able to run php artisan ide-helper:generate manually without running out of memory?

mr-feek avatar Dec 07 '20 03:12 mr-feek

@mr-feek, I generate IDE helpers on every composer update and install. It goes fine.

maximal avatar Dec 07 '20 16:12 maximal

Not sure I can help any more without the source code. Feel free to share it with me (fiachra @ squareup . com) if you are comfortable

mr-feek avatar Dec 07 '20 16:12 mr-feek

@mr-feek, sent.

maximal avatar Dec 07 '20 17:12 maximal

I'm experiencing similar symptoms, memory consumption on a smallish project ramping up and eventually throwing and OutOfMemory Error. Psalm works fine without the plugin.

Interestingly I also have sentry installed like OP so potentially something to do with that?

CharlieTap avatar Dec 07 '20 17:12 CharlieTap

@CharlieTap feel free to share some code to reproduce if you can!

mr-feek avatar Dec 09 '20 03:12 mr-feek

@mr-feek Unfortunately I'm not allowed to share the source, I did try a build today without sentry and it I still had the same issue. Interestingly this time with errors, I ran it several times and encountered different errors but all of which reside in the same area.

These are my deps:

"require": {
    "php": ">=7.4",
    "ext-json": "*",
    "barryvdh/laravel-dompdf": "^0.8.6",
    "facade/ignition": "^2.0",
    "fideloper/proxy": "^4.2",
    "fruitcake/laravel-cors": "^1.0",
    "goldspecdigital/laravel-eloquent-uuid": "^7.0",
    "guzzlehttp/guzzle": "^6.0",
    "intervention/image": "^2.4",
    "laravel/framework": "^7.0",
    "laravel/helpers": "^1.2",
    "laravel/passport": "^8.4.1",
    "laravel/tinker": "^2.0",
    "laravel/ui": "^2.0",
    "league/csv": "^9.6",
    "nunomaduro/collision": "^4.1",
    "phenx/php-font-lib": "^0.5.2",
    "phenx/php-svg-lib": "^0.3.3",
    "predis/predis": "^1.1",
    "fzaninotto/faker": "^1.9.1",
    "mockery/mockery": "^1.3.1"
  },
  "require-dev": {
    "oscarafdev/migrations-generator": "^2.0",
    "phpunit/phpunit": "^8.5",
    "psalm/plugin-laravel": "^1.4",
    "vimeo/psalm": "^4.3"
  },

And the below contains a few stack traces from the issue

/var/www # ./vendor/bin/psalm Scanning files... Analyzing files...


░░E░PHP Fatal error:  Allowed memory size of 8589934592 bytes exhausted (tried to allocate 20480 bytes) in /var/www/vendor/vimeo/psalm/src/Psalm/Type/Atomic.php on line 529

   Symfony\Component\ErrorHandler\Error\FatalError

  Allowed memory size of 8589934592 bytes exhausted (tried to allocate 20480 bytes)

  at vendor/vimeo/psalm/src/Psalm/Type/Atomic.php:529
PHP Fatal error:  Allowed memory size of 8589934592 bytes exhausted (tried to allocate 20480 bytes) in /var/www/vendor/nunomaduro/collision/src/Highlighter.php on line 134
/var/www # ./vendor/bin/psalm
Scanning files...
Analyzing files...

░░E░PHP Fatal error:  Allowed memory size of 8589934592 bytes exhausted (tried to allocate 13520000 bytes) in /var/www/vendor/vimeo/psalm/src/Psalm/Type/Atomic/GenericTrait.php on line 36

   Symfony\Component\ErrorHandler\Error\FatalError

  Allowed memory size of 8589934592 bytes exhausted (tried to allocate 13520000 bytes)

  at vendor/vimeo/psalm/src/Psalm/Type/Atomic/GenericTrait.php:36
    32|         if ($this instanceof TNamedObject && $this->extra_types) {
    33|             $extra_types = '&' . implode('&', $this->extra_types);
    34|         }
    35|
  > 36|         return $this->value . '<' . substr($s, 0, -2) . '>' . $extra_types;
    37|     }
    38|
    39|     public function getId(bool $nested = false): string
    40|     {


   Whoops\Exception\ErrorException

  Allowed memory size of 8589934592 bytes exhausted (tried to allocate 13520000 bytes)

  at vendor/vimeo/psalm/src/Psalm/Type/Atomic/GenericTrait.php:36
    32|         if ($this instanceof TNamedObject && $this->extra_types) {
    33|             $extra_types = '&' . implode('&', $this->extra_types);
    34|         }
    35|
  > 36|         return $this->value . '<' . substr($s, 0, -2) . '>' . $extra_types;
    37|     }
    38|
    39|     public function getId(bool $nested = false): string
    40|     {

      +1 vendor frames
  2   [internal]:0
      Whoops\Run::handleShutdown()
/var/www # ./vendor/bin/psalm
Scanning files...
Analyzing files...

░░E░PHP Fatal error:  Allowed memory size of 8589934592 bytes exhausted (tried to allocate 13520000 bytes) in /var/www/vendor/vimeo/psalm/src/Psalm/Type/Atomic/GenericTrait.php on line 36

   Symfony\Component\ErrorHandler\Error\FatalError

  Allowed memory size of 8589934592 bytes exhausted (tried to allocate 13520000 bytes)

  at vendor/vimeo/psalm/src/Psalm/Type/Atomic/GenericTrait.php:36
    32|         if ($this instanceof TNamedObject && $this->extra_types) {
    33|             $extra_types = '&' . implode('&', $this->extra_types);
    34|         }
    35|
  > 36|         return $this->value . '<' . substr($s, 0, -2) . '>' . $extra_types;
    37|     }
    38|
    39|     public function getId(bool $nested = false): string
    40|     {


   Whoops\Exception\ErrorException

  Allowed memory size of 8589934592 bytes exhausted (tried to allocate 13520000 bytes)

  at vendor/vimeo/psalm/src/Psalm/Type/Atomic/GenericTrait.php:36
    32|         if ($this instanceof TNamedObject && $this->extra_types) {
    33|             $extra_types = '&' . implode('&', $this->extra_types);
    34|         }
    35|
  > 36|         return $this->value . '<' . substr($s, 0, -2) . '>' . $extra_types;
    37|     }
    38|
    39|     public function getId(bool $nested = false): string
    40|     {

      +1 vendor frames
  2   [internal]:0
      Whoops\Run::handleShutdown()

CharlieTap avatar Dec 09 '20 11:12 CharlieTap

Sorry, accidentally closed the issue.

Any news on this?

maximal avatar Dec 27 '20 23:12 maximal

I'm disconnecting over the holidays, I'll take a look again next week

mr-feek avatar Dec 28 '20 19:12 mr-feek

It also hangs for me for chained eloquent queries:

         Model.where(..._)
            ->join(...)
            ->join(...)
            ->join(...)
            ->join(...)
            ->get();            

Hangs for me. If I remove one of the joins it works again. Looks like it have a max of 5 chained queries. If you have more than that it will just hang

luisgrases avatar Jan 26 '21 02:01 luisgrases

@mr-feek, just wondering if you had time to look into this issue?

maximal avatar Mar 25 '21 23:03 maximal

@maximal sorry, but no. I don't really use laravel at work anymore at the moment, so I haven't been working at resolving any of these bugs. Happy to merge any PRs that resolve issues though

mr-feek avatar Mar 30 '21 20:03 mr-feek

@maximal @CharlieTap @luisgrases Can you report if the problem still occurs with latest vimeo/psalm (4.8.1) and psalm/plugin-laravel (1.4.9)? If it does, please share your current output of: composer show | grep -E 'psalm|laravel'

caugner avatar Jul 13 '21 10:07 caugner

@caugner, on my test project the problem persists. On my real project I changed the queries’ structure to pass Psalm’s checks.

$ composer show | grep -E 'psalm|laravel'
barryvdh/laravel-ide-helper           v2.9.1              Laravel IDE Helper, generates correct PHPDocs for all Facade classes, to improve auto-completion.
beyondcode/laravel-websockets         1.12.0              An easy to use WebSocket server
emadadly/laravel-uuid                 v1.3.2              laravel uuid a simple, automatic UUID generator for any model based on Laravel.
fruitcake/laravel-cors                v1.0.6              Adds CORS (Cross-Origin Resource Sharing) headers support in your Laravel application
laravel/framework                     v8.50.0             The Laravel Framework.
laravel/socialite                     v5.2.3              Laravel wrapper around OAuth 1 & OAuth 2 libraries.
laravel/tinker                        v2.6.1              Powerful REPL for the Laravel framework.
laravel/ui                            v2.3.0              Laravel UI utilities and presets.
laravelcollective/html                v6.2.1              HTML and Form Builders for the Laravel Framework
orklah/psalm-insane-comparison        v1.0.3              Detects possible insane comparison ("string" == 0) to help migrate to PHP8
psalm/plugin-laravel                  v1.4.9              A Laravel plugin for Psalm
sentry/sentry-laravel                 1.9.0               Laravel SDK for Sentry (https://sentry.io)
vimeo/psalm                           4.8.1               A static analysis tool for finding errors in PHP applications

maximal avatar Jul 14 '21 08:07 maximal

@caugner, I could share the test project with you if you wish.

maximal avatar Jul 19 '21 18:07 maximal

I've got the same issue as the others now

It always comes from the same file as the others:

  at vendor/vimeo/psalm/src/Psalm/Type/Atomic/GenericTrait.php:36
    32|         if ($this instanceof TNamedObject && $this->extra_types) {
    33|             $extra_types = '&' . implode('&', $this->extra_types);
    34|         }
    35|
  > 36|         return $this->value . '<' . substr($s, 0, -2) . '>' . $extra_types;
    37|     }
    38|
    39|     public function getId(bool $nested = false): string
    40|     {

Is it an issue with query joins?

tm1000 avatar Aug 06 '21 23:08 tm1000

This fully resolved for me once I declared my model properties (https://github.com/barryvdh/laravel-ide-helper#automatic-phpdocs-for-models) and additionally set throwExceptionOnError="true" and fixed all errors that were thrown.

Then I was able to remove throwExceptionOnError="true" and all is good

Im thinking the loop is mainly in the models. Once I declared the properties then Psalm doesnt have to try to infer anything.

tm1000 avatar Aug 10 '21 21:08 tm1000

I've gone through five projects now where typing out the models (like above) fixed this in every case.

tm1000 avatar Sep 17 '21 17:09 tm1000

Same problem for us with the models folder. @tm1000 please could you explain better how you solved it ? What do you mean by "typing out" ? Can you tell us how you solved it?

borgogelli avatar Nov 23 '21 14:11 borgogelli

@borgogelli read my comment right before my last comment as I fully detailed how to fix it. You need to declare phpdoc model properties. As of today at my company this still isn't an issue as long as we keep the phpdoc properties up to date.

The endless memory consumption is this plugins attempt at trying to infer types.

tm1000 avatar Nov 23 '21 14:11 tm1000

FYI this just happened again in a coworker's instance of Laravel. It was because he typehinted Illuminate\Database\Eloquent\Model on a property. In laravel 8 and lower there is a lot of usage of magic methods. This causes Psalm (utilizing this plugin) to go into a spiral trying to infer types against everything and then run out of memory.

Good news is that you can do a couple of things:

  1. Use psalm --debug-by-line to identify the line that Psalm get's stuck on
  2. In that area try to more specifically define your types. In this situation adding /** @var SpecificModel1|SpecificModel2 **/ solved the issue
  3. Use ide helper to generate model properties. See https://github.com/barryvdh/laravel-ide-helper#automatic-phpdocs-for-models (use the -W option as well to write them directly into your models)

tm1000 avatar Mar 10 '22 20:03 tm1000

@tm1000 @maximal @borgogelli @luisgrases

Good news everyone: in release v2.5.0 we located and fixed one important memory consumption issue. Additionally, we have replaced some custom code by correct subs. There is even more potential to optimize memory consumption, but probably these steps already solved your issues guys, can you please test using latest releases (2.5+)?

If the problem persists, Ideally we need to have a small snippet that causes it - so we can add it to a testing suite and check our optimization results. Volunteers are very welcome

alies-dev avatar Feb 01 '23 08:02 alies-dev

YAY!! :tada:

maximal avatar Feb 01 '23 10:02 maximal

@lptn, seems like it works fine now (given the codebase where previous-version plugin hangs).

I think we can close the issue now.

Thank you guys!

maximal avatar Feb 03 '23 13:02 maximal

@maximal Great! Please feel free to file more issues here, especially if you think it may affect other people.

alies-dev avatar Feb 03 '23 13:02 alies-dev