Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(agent): Fixes warning from mysqli when explaining slow SQL queries (GH issue 875) #881

Merged
merged 3 commits into from
Apr 25, 2024

Conversation

mfulb
Copy link
Contributor

@mfulb mfulb commented Apr 24, 2024

In GH issue #875 it was reported a warning is generated when using prepared statements with mysqli. The issue included excellent instructions on how to reproduce the issue. This PR addresses the cause of the warning.

To understand the cause of the warning requires understanding how the agent handles mysqli::stmt objects. When a SQL statement is prepared via mysqli the agent will store the SQL string in a global hashmap called mysqli_queries if the SQL is considered explainable (determined via nr_php_explain_mysql_query_is_explainable()), otherwise it is just ignored. Later when a slow SQL query is detected the SQL string is retrieved and used to explain the query.

An additional factor is when a mysqli::stmt object is released it goes into a free pool PHP keeps which allows it to quickly hand out an allocated section of memory when a new object is created. Each object has a object ID (referenced as a handle in the agent for the relevant code). When an the allocated object is reused from the free pool it retains the same object ID.

The agent used the object ID (handle) as the key for storing the prepared SQL string for explainable SQL strings. Now what if an explainable query comes through, the string is stored, and then that object is released. A new query is prepared with a string that is NOT explainable and gets the object container with the same object ID as the explainable one just released. The string stored in mysqli_queries using that object ID as the key is now stale. If the new, unexplainable query is slow then the agent will pull this stale string and try to run an explain with it - leading to the warning.

The fix is to clear any query string for a given object ID if the query string is unexplainable.

Integration tests were added (based on the reproduction case) that test for a regression of this fix.

mfulb added 3 commits April 24, 2024 10:16
    In Github issue 875 it was reported that the agent causes a warning
    to be generated when using the mysqli extension.  The cause of the
    warning was the agent reusing a stale SQL string associated with a
    previous mysqli::prepare statement.  This commit ensures that stale
    SQL strings are cleared when this state is detected.
@newrelic-php-agent-bot
Copy link

newrelic-php-agent-bot commented Apr 24, 2024

Test Suite Status Result
Multiverse 9/9 passing
SOAK 56/56 passing

*/
metadata = nr_php_mysqli_query_find(handle);
if (NULL == metadata) {
return NR_FAILURE;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this an error condition (the comment above suggests it is not)? Does nr_php_mysqli_query_clear_query need to return any value? It's return value is not checked in nr_php_prepared_prepare_general where it's used.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A good question - was just mirroring the API for the set() function. In the current use case of this function it is not important to know if any data was actually cleared - just that if it exists it was cleared. It is possible the handle has never been used before so there would be no data to clear. So this particular case (lines 362-364) is not a failure but a consequence of a reasonable, possible state of the agent.

But the NR_FAILURE return code does give the caller the context that there was no existing data for the handle which might be useful in a future, different use?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

NR_FAILURE definitely brings in connotations that something went wrong. However, as you say, this is a normal condition and hence, NR_FAILURE seems wrong here. If there was no data, it's as if it's already been cleared. If it's desired to separate result codes that indicate that nothing got cleared vs data was cleared vs clearing data failed, a different return value should be used for all cases.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This PR can be merged with the code as is.

Copy link
Member

@lavarou lavarou left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks! +1 for great tests' descriptions!

@lavarou lavarou added this to the next-release milestone Apr 24, 2024
@lavarou lavarou linked an issue Apr 24, 2024 that may be closed by this pull request
@ZNeumann
Copy link
Contributor

I would prefer if the PR title explained the fix in addition to the issue number, rather than just refer to the issue

@mfulb mfulb changed the title fix(agent): Fixes warning from GH issue 875 fix(agent): Fixes warning from mysqli when explaining slow SQL queries (GH issue 875) Apr 25, 2024
@mfulb
Copy link
Contributor Author

mfulb commented Apr 25, 2024

I would prefer if the PR title explained the fix in addition to the issue number, rather than just refer to the issue

Is this better?

@mfulb mfulb merged commit ac8271c into dev Apr 25, 2024
62 checks passed
@mfulb mfulb deleted the fixes/mysqli_gh_issue_875 branch April 25, 2024 14:31
mfulb added a commit that referenced this pull request Apr 25, 2024
A EXPECT_SLOW_SQLS specification was left out of one test from PR #881 .
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

MySQLi statement execute ArgumentCountError ("number of parameters")
4 participants