Skip to content

Performance degradation #549

Closed
Closed

Description

@makstech

Some time ago we were performing updates to all our packages, including the dotenv, where we went from v2 to v5. And later we noticed that the CPU usage and all request times have increased substantially, even though we use pretty beefy instances. On 8 core instances, CPU usage increased by >20%.

After some investigation, we found out that the dotenv load times have skyrocketed and memory consumption has gone up quite a lot too. The primary cause of regression was pinned down to this method https://github.com/vlucas/phpdotenv/blob/1a7ea2afc49c3ee6d87061f5a233e3a035d0eae7/src/Parser/EntryParser.php#L46-L60
Where now, to parse one .env line, it needs to create a bunch of classes and then the final entry is at least 2 classes that are kept in memory. Compared to a simple array<string, string> in v2.

Our .env is 2k lines long of which around 400 are empty lines and another 300 are comments.
So we downgraded back to v2 and the difference is huge, especially during peak hours.

For example, in one simple and fast route, during peak hours, the difference between v2 and v5 is around 80ms per request. On a route that has around 65k req/h (1m+ req/day) that makes more than 80 minutes of extra processing time in an hour, just on this route.
Here's an example of that route after the downgrade performed on the 8th of Feb. No other changes to the code or infrastructure were done.
image

I know, the obvious solution would be some sort of caching. But is there really a need for so much more processing and objectifying?

Activity

  1. GrahamCampbell commented on Feb 14, 2023

    @GrahamCampbell
    Collaborator

    Thanks for getting in touch. I am aware that v5 is slower than v2, and one reason for this is they have different features, and v2 has bugs that could only be sanely resolved by using a slower approach. I would be interested to know what PHP version you are using and what the env files are you are loading are looking like. I doubt the use of objects is what is the cause of the slow-down here, but I would certainly consider a PR that improves performance without introducing bugs, along with evidence.

    In general, I would not recommend running this package on every request. Best practice is to use this package to parse your env file, one-time, then cache that across requests. v5 has a modular architecture, enabling this. For example, in Laravel, this package is used to compile a cached configuration file, one time, then that file is "required" by PHP at runtime, so we can make use of opcache, effectively meaning that here is zero IO on each request to load env and config.

  2. GrahamCampbell commented on Feb 14, 2023

    @GrahamCampbell
    Collaborator

    I'd also be interested to know if you are using opcache. Loading 100 classes into fpm should make zero difference to performance, other than on the first request.

  3. makstech commented on Feb 15, 2023

    @makstech
    Author

    We are running PHP 7.4, and yes opcache is enabled.

    what the env files are you are loading are looking like

    Could you please elaborate on this one?
    Just one regular .env file, with a bunch of simple string entries, less than 10 variables that have references, no multi-level references, and no multi-line entries.

  4. GrahamCampbell commented on Aug 23, 2026

    @GrahamCampbell
    Collaborator

    The upcoming v5.7.0 will resolve this. The parser and loader were accumulating their results in a way that made loading O(n²) in the number of entries, which is why large files like yours were hit so much harder than the small ones in our benchmarks, and on top of that, plain and simply quoted values now skip the token state machine entirely. On a 2,000 line file in the same shape as yours (blanks, comments, a handful of references), loading is around 4x faster than v5.6 and scaling is now linear, which closes most of the gap to v2 while keeping all of the v5 parser's correctness fixes. Worth knowing: the memory side of the report didn't reproduce, since load() retains only the returned array, and with opcache the class loading costs a fraction of a millisecond per request. The general advice from earlier in this thread still stands, though: parse once and cache your config rather than reading .env on every request. Thanks for the detailed report.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions