ID:               47712
 Updated by:       and...@php.net
 Reported By:      ninzya at inbox dot lv
 Status:           Feedback
 Bug Type:         MySQL related
 Operating System: Windows XP
 PHP Version:      5.3.0RC2
 Assigned To:      mysql
 New Comment:

Just tried again. 2 million requests, concurrency of 150, which led to
load of 90 on the box - pretty hefty, where a problem should show itself
and again no problem except that in some cases connection to MySQL
couldn't be established, which is to be expected. MySQL was set up to
have 1000 maximal connections, so it is not the bottleneck, just system
resources.


Previous Comments:
------------------------------------------------------------------------

[2009-06-08 12:46:49] and...@php.net

Hi, I switched on the zval cache and tried to reproduce the problem,
with no success.
I tested with Apache2 MPM. `ab` concurrency level starting from 20 up
to 100. Time from 30 to 45 seconds. I got, with your code, only 3 types
of errors. I added mysql_connect() myself
- mysql_connect() fails because cannot have more connections on the
unix socket. Using TCP/IP exhausts the resources earlier for connecting
to MySQL
- mysql_query() tries to connect, although the connection has failed,
typical for mysql_query()! It fails :
-- password problem
-- no resources

I did not see any corruption whatsoever. Difference from you is that I
tested on Linux, as server. ab was running on a Mac connected over a
FastEthernet switch.

Here follows the code I used:

<?php
$conn = mysql_connect("localhost", "root", "root");
mysql_select_db("test");
$result = mysql_query("SELECT a,b FROM stress_test");
if (!$result) {
    echo 'Could not run query: ' . mysql_error();
    exit;
}
/* Use the result, assuming we're done with it afterwards */
$row = mysql_fetch_assoc($result);

/* Now we free up the result and continue on with our script */
mysql_free_result($result);
var_dump($row);
?>

------------------------------------------------------------------------

[2009-06-08 10:36:49] and...@php.net

 Is is possible to provide a package which we can run, even it is not
20-30lines of code, so we can try to reproduce the problem?

In the meanwhile I have committed a change to mysqlnd which I suppose
should lead to the problem disappearing - switching the zval cache off.
Could you try downloading and testing a binary from
http://windows.php.net/snapshots/

New snapshot should be ready in a few hours. I suppose you will need
the VC6 build.

Thank you!
Andrey

------------------------------------------------------------------------

[2009-05-12 09:42:13] ninzya at inbox dot lv

This bug is still present in PHP 5.3.0RC2 and is critical to me.

------------------------------------------------------------------------

[2009-04-19 12:04:11] ninzya at inbox dot lv

Changed bug summary.

My configuration:
  Apache 2.2,
  PHP 5.3.0RC1 as module,
  Windows XP SP3,
  MySQL community server 5.1.33

Bug being hit during high concurrency running apache benchmark: "ab -c
30 -n 10000". See previous posts for details.

------------------------------------------------------------------------

[2009-04-12 15:08:30] ninzya at inbox dot lv

Please excuse me for the delay. I have been figuring out what's the
cause of this bug and finally i have something to come up with.

The problem is in mysql_free_result function. Not in itself, but it's
behavior has changed. Consider the following example from the PHP manual
on mysql_free_result:

Example #1 A mysql_free_result() example
<?php
$result = mysql_query("SELECT id,email FROM people WHERE id = '42'");
if (!$result) {
    echo 'Could not run query: ' . mysql_error();
    exit;
}
/* Use the result, assuming we're done with it afterwards */
$row = mysql_fetch_assoc($result);

/* Now we free up the result and continue on with our script */
mysql_free_result($result);

echo $row['id'];
echo $row['email'];
?>

This code with PHP 5.3.0 with mysqlnd will now fail under high
concurrency, because mysql_free_result will affect $row variable and
allow another threads to use it's zval to store data. I suspect that
mysql_free_result marks the referenced by $row data as 'free' and
another threads pick that zval up and work with it. As soon as you
release result, another thread may corrupt $row variable. Test this
example under high concurrency and you will get different values for
$row['id'] and $row['email']. Run 10000 req. test and some of them will
fail to produce correct output.

In my framework i use mysql_free_result before referencing last fetched
row of result set very often, that's why i hit this bug 'randomly'.

I have another example i just tested, this is part of my framework. The
idea is the same - mysql_free_result is being called before actually
using fetched data array.

  $con =mysql_pconnect( 'localhost', 'root', '');
  mysql_select_db( 'ewe10');
  $q =mysql_query( $sql ='SELECT id, title, keywords, descr,
template_id, `title`, `keywords`, `descr`, `template_id`
            FROM pages
          WHERE node_id =11
            AND alt_name =\'welcome\'
          LIMIT 0, 1;', $con);
  $row =mysql_fetch_assoc( $q);
  mysql_free_result( $q);
  
  if( $row['id'] ===null || $row['template_id'] !=8567 || $row['title']
!='My test page' || $row['keywords'] !='asdasd' || $row['descr']
!='asdasd') {
    trigger_error( 'FAIL!', E_USER_WARNING);
    trigger_error( 'SQL: ' .$sql, E_USER_WARNING);
    ob_start();
    var_dump( $row);
    trigger_error( ob_get_clean(), E_USER_WARNING);
    die('NOT OK');
  }

  die('OK');

SQL for the table:

CREATE TABLE  `ewe10`.`pages` (
  `id` int(10) unsigned NOT NULL AUTO_INCREMENT COMMENT 'unique page
id',
  `node_id` int(10) unsigned NOT NULL COMMENT 'node id in which this
page is',
  `title` varchar(128) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL
COMMENT 'page title',
  `keywords` varchar(128) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL
COMMENT 'page keywords',
  `descr` text CHARACTER SET utf8 COLLATE utf8_bin NOT NULL COMMENT
'page description',
  `template_id` int(10) unsigned DEFAULT NULL COMMENT 'template this
page is using (if NULL, template was deleted from database and this page
is stored as a backup)',
  `type` enum('MAIN','PAGE') CHARACTER SET latin1 NOT NULL DEFAULT
'PAGE' COMMENT 'page type',
  `alt_name` varchar(45) CHARACTER SET latin1 NOT NULL COMMENT 'page
alternative name',
  `date_add` datetime NOT NULL COMMENT 'when page was added',
  PRIMARY KEY (`id`),
  UNIQUE KEY `NODE_ID_ALT_NAME` (`node_id`,`alt_name`) USING BTREE,
  KEY `FK_pages_templates` (`template_id`),
  KEY `TYPE` (`type`),
  KEY `FK_pages_map` (`node_id`),
  KEY `DATE_ADD` (`date_add`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 ROW_FORMAT=FIXED;

the only row in this table is shown on the screenshot i referenced in
previous report.

To address the issue more quickly, i would like to give you a hint -
after calling mysql_free_result there is no possibility to fetch any
more rows, so there's nothing to worry about mysql_fetch_*(), but there
is possibility that the last fetched row may be referenced. This means -
even if mysql_free_result was called, last fetched row must remain
locked in mysqlnd internal zval cache until the variable is
implicitly/explicitly unset.

------------------------------------------------------------------------

The remainder of the comments for this report are too long. To view
the rest of the comments, please view the bug report online at
    http://bugs.php.net/47712

-- 
Edit this bug report at http://bugs.php.net/?id=47712&edit=1

Reply via email to