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

Implement CLIENT_DEPRECATE_EOF for mariadb-connector-c #131

Closed

Conversation

bibstha
Copy link

@bibstha bibstha commented Mar 30, 2020

Fix for CONC-461

mariadb-connector-c client now sends CLIENT_DEPRECATE_EOF flag to the server.

There are some major changes on how the server responds. Mainly the way packets are sent back from server changes which are documented in https://dev.mysql.com/worklog/task/?id=7766

I will try to add as much comment on the code changes as possible.

Note:

Most of the changes have been studied on the worklog, looking into the packets through wireshark and how mysql client behaves.

@@ -23,6 +23,8 @@
#include <mysql.h>
#include <ma_hash.h>

#define MAX_PACKET_LENGTH (256L*256L*256L-1)
Copy link
Author

Choose a reason for hiding this comment

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

@@ -188,11 +189,14 @@ void net_get_error(char *buf, size_t buf_len,
*****************************************************************************/

ulong
ma_net_safe_read(MYSQL *mysql)
ma_net_safe_read(MYSQL *mysql, my_bool *is_data_packet)
Copy link
Author

Choose a reason for hiding this comment

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

*/

int
unpack_field(const MYSQL *mysql, MA_MEM_ROOT *alloc, my_bool default_value,
Copy link
Author

Choose a reason for hiding this comment

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

This is extracted as is from unpack_fields function below so it can be called directly when reading packet metadata similar to mysql source code here

* and is called by both
* mthd_my_read_query_result and mthd_stmt_read_prepare_response
*/
MYSQL_FIELD *mthd_my_read_metadata_ex(MYSQL *mysql, MA_MEM_ROOT *alloc,
Copy link
Author

Choose a reason for hiding this comment

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

Similar to mysql client

return result;
}

MYSQL_FIELD *mthd_my_read_metadata(MYSQL *mysql, ulong field_count, uint field)
Copy link
Author

Choose a reason for hiding this comment

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

The reason mthd_my_read_metadata_ex and mthd_my_read_metadata are two methods: similar to how it is implemented in mysql client. mthd_my_read_metadata_ex takes MA_MEM_ROOT so it can be called from mthd_stmt_read_prepare_response

@@ -4341,41 +4488,41 @@ struct st_mariadb_api MARIADB_API=
*/
struct st_mariadb_methods MARIADB_DEFAULT_METHODS = {
/* open a connection */
mthd_my_real_connect,
mthd_my_real_connect, // db_connect
Copy link
Author

Choose a reason for hiding this comment

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

Adding the actual names so it's easier to grep.

p+=2;
stmt->upsert_status.server_status= stmt->mysql->server_status= uint2korr(p);
/* save status info */
if (stmt->mysql->server_capabilities & CLIENT_DEPRECATE_EOF && !is_data_packet)
Copy link
Author

Choose a reason for hiding this comment

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

}
if (packet_len < 8 && *pos == 254) /* EOF */
else
Copy link
Author

Choose a reason for hiding this comment

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

The else part is as is as before. Only the upper if conditional is different.


if (is_data_packet)
{
// TODO: <metadata><row(s)><OK> not supported right now
Copy link
Author

Choose a reason for hiding this comment

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

Note that I'm still unsure how to reproduce it here. Non of the existing tests capture it yet.

@@ -1869,6 +1925,48 @@ int stmt_read_execute_response(MYSQL_STMT *stmt)
if (!stmt->mysql)
return(1);

if ((mysql->server_capabilities & CLIENT_DEPRECATE_EOF))
Copy link
Author

Choose a reason for hiding this comment

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

Ref

@bibstha
Copy link
Author

bibstha commented Apr 6, 2020

@9E0R9 do you have some time to look into this?

@9EOR9
Copy link
Collaborator

9EOR9 commented Apr 15, 2020

Thanks for your pull request - I will review it next week.

@9EOR9
Copy link
Collaborator

9EOR9 commented Apr 20, 2020

Can you please stash multiple commits into one commit? That makes reviewing much easier. Thans

With CLIENT_DEPRECATE_EOF, there are few changes how the server sends
back the packets. The protocol changes are explained in Worklog
https://dev.mysql.com/worklog/task/?id=7766
@bibstha
Copy link
Author

bibstha commented Apr 20, 2020

Done, I squashed them all into a single one.

@9EOR9
Copy link
Collaborator

9EOR9 commented Apr 26, 2020

From a technical standpoint the code looks more or less ok, but from legal it doesn't:

There is a lot of code (and even comments) just copied from Oracle/MySQL libmysql which is under GPL license and therefore not compatible with LGPL license of MariaDB Connector/C.

@bibstha
Copy link
Author

bibstha commented Apr 28, 2020

@9E0R9 I see, I'll approach the code in a different way than mysql.

@ioquatix
Copy link

I'm playing around with this feature, what are the benefits of CLIENT_DEPRECATE_EOF?

@bibstha
Copy link
Author

bibstha commented Jul 8, 2020

Hi @ioquatix,
The main benefit for CLIENT_DEPRECATE_EOF is that each mysql response now supports additional information in the form of OK_packets, ie: session tracking information. More info on session tracking info

SessionTracking is not just for pre-defined data, but you can write plugins to send back custom data too. At work, we're using this to send back performance metrics from inside the database.

PS: We also added support for session-tracking to mysql2 gem in brianmario/mysql2#1092 (merged into master, not yet released.)

@vaintroub
Copy link
Contributor

vaintroub commented Jul 8, 2020

The MySQL implementation was done by someone without much experience in MySQL protocol, and is even conceptually broken w.r.t old features. For example, the "stored procedure output parameters" from 5.5, is now broken because it is not possible to determine from the result set whether it is this special "output parameters" result set, from the metadata, until the whole result set is read. Because somebody wanted optimize out the 9 bytes in the result set metadata.

CLIENT_DEPRECATE_EOF decided to encode key-value in its own, unique way, dismissing the 20 years old notion of "result set" in the protocol and misused OK packet for this purpose. It was definitely a low quality design work by newly hired MySQL trainee by Oracle. Someone needs to stop these people from screwing the protocol.

@bibstha
Copy link
Author

bibstha commented Jul 8, 2020

@vaintroub that's interesting insight regarding compatibility.

Given that is how the protocol works now, and most other connectors have it implemented, and without CLIENT_DEPRECATE_EOF, the connector cannot access session tracking information, do you see any other way forward?

@vaintroub
Copy link
Contributor

I do not know about the good way forward. Luckily in this situation I'm almost a bystander/observer.

But the snarky remark I told, is the same I told maybe back in 2014, when MySQL trainee developers successfully conquered their first challenge of the protocol butchering.

What now?

We're debating to implement a MariaDB protocol extension, for our server, and our clients which would workaround what this CLIENT_DEPRECATE_EOF broke. We need extension for https://jira.mariadb.org/browse/MDEV-19237 anyway, so maybe we can include the status flags that newbie developers throw away in intermediate EOF (2 of the whooping 9 bytes they spared per result set)

I guess most of the client APIs are doing OK without that extra trailing stuff at the end of OK packet. They did it for decades, and they can work with servers not supporting it. There is no notion of "client change state notification" in any of standard database APIs I know of. This stuff is mostly special needs for proxies, I think

@vaintroub
Copy link
Contributor

@bibstha , now what do you need this capability for, exactly? Can you replace it with something more well-formed, e.g a SELECT for a variable you're interested in, after the "COMMIT". In case you're concerned about network roundtrips, semicolon batching is your friend

@bibstha
Copy link
Author

bibstha commented Jul 10, 2020

These projects:

both already support the CLIENT_DEPRECATE_EOF. Wouldn't it simplify therefore to also have support in the c-connector? Maybe a flag to enable disable the support on client side?

The primary use-case for us to have this support is to be able to read session-tracking data. We are very much interested to gather query-metadata (specially performance) and return it in-line (realtime) so we can monitor / build systems that analyze queries behavior. The best way to do this for now is to have it return in the OK_packet as a session tracking variable.

We haven't considered batching queries yet, that's something we can look into. But the larger issue is still how you guys decide to move forward with the protocol support.

There is no notion of "client change state notification" in any of standard database APIs I know of

We're debating to implement a MariaDB protocol extension

IMO having a way to send back metadata of a query is very very useful. If not session_tracking, while you guys discuss protocol extension, please consider someway of sending back the metadata.

@9EOR9
Copy link
Collaborator

9EOR9 commented Jan 23, 2022

Closing PR - we will not implement DEPRECATE_EOF due to broken design (see CONC-461)

@9EOR9 9EOR9 closed this Jan 23, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
4 participants