Skip to content

ext/mysqli: mysqli double increments num_active_persistent when using real_connect #22631

Description

@bavis-m

Description

The following code:

<?php

$use_real_connect = true;

$host = "p:localhost";
$user = "root";
$password = "password";

for ($i = 0; $i < 100; $i++)
{
  if ($use_real_connect)
  {
    $mysql = mysqli_init();
    mysqli_real_connect($mysql, $host, $user, $password);
  }
  else
  {
    $mysql = mysqli_connect($host, $user, $password);
  }
  mysqli_close($mysql);
}

$stats = mysqli_get_links_stats();
var_dump($stats);

Resulted in this output:

array(3) {
  ["total"]=>
  int(0)
  ["active_plinks"]=>
  int(99)
  ["cached_plinks"]=>
  int(1)
}

But I expected this output instead:

array(3) {
  ["total"]=>
  int(0)
  ["active_plinks"]=>
  int(0)
  ["cached_plinks"]=>
  int(1)
}

The code gives the correct output if you turn off the $use_real_connect flag.

This is caused by the persistent branch of mysqli_common_connect incrementing num_inactive_persistent (mysqli_nonapi.c:179 in tag php-8.5.8) when a persistent connection is reused based on the connection hash, then jumping to the end: label, and incrementing it again under this condition:

// mysqli_nonapi.c:296
if (persistent && (new_connection || is_real_connect)) {
    MyG(num_active_persistent)++;
}

Because mysql_close has already been called on the persisted connection, we get a total increase in active persistent connections by 1.

I'm not entirely sure why || is_real_connect is needed in that check, it looks to me like new_connection still covers the persistent case.

EDIT: The consequence of this issue is that if you make a persistent connection a ton of times (say in a long-running php-fpm process), and then make a different connection that doesn't yet exist in the connections hash table, you will always fail with the error Too many open persistent links with a massively huge number of active persistent connections. Note that this does not actually leak connections to the server, you still are capped properly by at most mysqli.max_persistent.

PHP Version

PHP 8.5.8 (cli) (built: Jul  6 2026 16:48:54) (NTS)
Copyright (c) The PHP Group
Built by https://github.com/docker-library/php
Zend Engine v4.5.8, Copyright (c) Zend Technologies
    with Zend OPcache v8.5.8, Copyright (c), by Zend Technologies

Operating System

Debian 13.5

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions