Skip to content

Possible Memory Leak with SSL-enabled MySQL connections #8979

Closed
@orware

Description

@orware

Description

This was an issue that was primarily uncovered when creating a database connection testing utility that measured the time it took to connect to a MySQL database repeatedly within an infinite loop so that the utility could run indefinitely as a process managed by supervisord.

Leaving multiple instances of this utility running on a server (each testing connection times for a different database) for an extended period would eventually result in the entire server becoming unresponsive and I eventually realized it appeared to be due to a memory leak of some sort.

After some investigation, it appears that the memory usage for PHP MySQL connections (both using the mysqli and PDO MySQL options) increases when you are connecting to MySQL databases that use SSL.

In local testing, switching my utility program to connect to a non-SSL MySQL database did not result in the same sort of memory increases over time, leading me to the conclusion that this is likely only an issue specific for SSL-enabled MySQL connections.

It's possible that the memory usage increase over time is expected for some reason, but it felt unexpected so I feel this is more of a memory leak bug that can hopefully be addressed eventually for PHP users.

The main difference between creating the two types of connections (either for mysqli or the PDO MySQL option) is that I am passing in a path to a certificate file for the connection to be established correctly so I'm not sure if the extensions somehow maintain some extra memory every time it loads in that certificate and doesn't release it when the connection is closed, or if there is something else specific to SSL-enabled connections that is resulting in the extra memory usage not being freed.

For the SSL-enabled MySQL connections my utility resulted in output like this:

The script starts out with this memory usage around iteration 1 of the utility:
After PDO Connection Attempt:
    The script is now using: 476392 B of memory.
    The script is now using: 465 KB of memory.
    Peak usage: 466 KB of memory.

And memory usage to the following after about 130 iterations of the utility:
After PDO Connection Attempt:
    The script is now using: 617384 B of memory.
    The script is now using: 603 KB of memory.
    Peak usage: 603 KB of memory.

But I expected this output instead, where memory usage remains pretty much consistent for non-SSL enabled MySQL connections:

For the non-SSL enabled connections this was iteration 1:
After PDO Connection Attempt:
    The script is now using: 473328 B of memory.
    The script is now using: 462 KB of memory.
    Peak usage: 463 KB of memory.

And after 400+ iterations the memory usage was still basically the same:
After PDO Connection Attempt:
    The script is now using: 473584 B of memory.
    The script is now using: 462 KB of memory.
    Peak usage: 463 KB of memory.

This issue likely wouldn't be noticeable for the majority of PHP users since PHP's short lived connections would ensure the memory is freed at the end of the request, but it would come into play more so for any users making use of a cloud MySQL database connection, which typically rely on SSL for security, and connect to that database via a process that might run continuously, which would still be a small number of users overall.

Testing instructions:

You may use the following link and save a local version to test with:
https://3v4l.org/BOnq5

Grab the latest cacert.pem file from:
https://curl.se/docs/caextract.html
(and place it in the same folder as the file above)

And then lastly create a config.ini file in the same folder with the following contents:

ca_root_path="./cacert.pem"

[database]
host=localhost
username=root
password=
database=garbage-collection
port=3306
use_ssl=0

;[database]
;host=
;username=
;password=
;database=garbage-collection
;port=3306
;use_ssl=1

[common-parameters]
number_of_iterations=0
iteration_delay=1
show_output=1

The first [database] section is intended for a local database that would be used for testing a non-SSL enabled database connection while the second [database] section is intended for an SSL-enabled connection and you can simply comment out one of the sections depending on what you want to test.

After creating a local garbage-collection database you should be able to confirm the "happy path" of using a non-SSL enabled database connection using the script and running it from the command line and noting that memory usage does not seem to increase in that scenario.

Then you can test out an SSL-enabled database pretty easily using PlanetScale which would likely be the quickest / free option with the Hobby plan that's available, although I also tried out Digital Ocean's Managed MySQL database option as well and confirmed the same behavior shared above as well for it's SSL-enabled database connections (I had to switch out the cacert.pem usage for their provided ca-certificate.crt file and adjust a few other pieces of information such as the port number when testing out the Digital Ocean option).

If there's anything else I can assist with please let me know!

PHP Version

PHP 8.1.5

Operating System

Confirmed on Windows 11 and Ubuntu 20.04

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions