-
Notifications
You must be signed in to change notification settings - Fork 145
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
Preparing statements without socket connection is very slow #132
Comments
Hi! I'm on However it is reproducable on my linux box with I'll investigate further. |
Oh, that's interesting! Can you reproduce with MySQL (i.e., not MariaDB) on Linux, if you have access to that? What about MariaDB on macOS? |
I can reproduce with Cloud SQL on GCP, running fairly stock MySQL 5.7 |
I've done some more digging on this in a slightly different context, and found that this kind of 40ms delay can generally be directly attributed to Nagle's algorithm (the The way to fix this would likely be to always set I haven't yet had a chance to test out whether this fixes the issue outlined above, but will give it a shot once I have the time. |
I can confirm that the following diff reduces the latency of the original code from 40ms per prepare to 20µs per prepare: diff --git a/src/io/tcp.rs b/src/io/tcp.rs
index a33ceef..19f264b 100644
--- a/src/io/tcp.rs
+++ b/src/io/tcp.rs
@@ -115,6 +115,7 @@ impl<T: ToSocketAddrs> MyTcpBuilder<T> {
stream.set_read_timeout(read_timeout)?;
stream.set_write_timeout(write_timeout)?;
stream.set_keepalive_ms(keepalive_time_ms)?;
+ stream.set_nodelay(true)?;
Ok(stream)
})
} |
When a command (or part of a command) is much smaller than the network MTU, a (well-known) problem occurs between delayed ACKs and Nagle's algorithm. Specifically, Nagle tries to maximize packet size, and will avoid sending a non-full packet unless there are no currently unacked packets. Delayed ACKs delays sending ACKs to (again) minimize the number of packets sent over the network, and instead coalesce many ACKs over a prolonged period of time. When sending small messages, these interact poorly: Nagle will effectively not let you send a packet until the delayed ACK timeout expires on the other host. The standard dictates that the delayed ACK timeout is at most 500ms, but in Linux, the actual delays are dictated by TCP_DELACK_MIN/MAX (see https://elixir.bootlin.com/linux/latest/ident/TCP_DELACK_MIN), and the minimum is usually set to 40ms. These 40ms will appear as an artificial delay to some commands, without the server or client doing any useful work. This commit enables TCP_NODELAY by default, which turns off Nagle's algorithm. Applications that want to maximize throughput despite the latency cost can disable the option through the new `tcp_nodelay` method on `OptsBuilder`. Fixes blackbeam#132.
See #134 for a proposed fix. |
Consider the following program which just prepares a lot of queries in a loop (the statement cache is turned off to force the prepare so we can benchmark it):
The prepare seems to always take about 40ms, while the CPU of both the server and the client sits idle. The client is usually waiting in:
In contrast, using
query
(instead ofprepare
) takes ~30µs. That's a three orders of magnitude difference. Surely that isn't expected behavior?This issue mirrors that exhibited in amphp/mysql#55, so this may be a MySQL/MariaDB server issue, not a client issue, but it is not yet clear. It could also be some kind of subtle protocol mismatch that causes the server to wait for more bytes from the client?
The text was updated successfully, but these errors were encountered: