-
-
Notifications
You must be signed in to change notification settings - Fork 1.3k
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
long-running processes: allow checking the database connection #6351
Conversation
b132491
to
eb3fe0e
Compare
c3251e9
to
cacec40
Compare
Hi! I think the try
{
// execute dummy SQL
} catch (ConnectionLost $e) {
$logger->notice('Looks like we lost the connection there', ['exception' => $e]);
}
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This looks like a quite pragmatic and effective strategy, I hope this can make it into the codebase.
Sorry to be the party pooper:
|
Right now, I see one issue with doing this though: if the middleware handles Lines 222 to 224 in c70bae9
It would mean having to keep the code inside the wrapper connection and inside the middleware carefully in sync. |
cacec40
to
e6459ef
Compare
Thank you all for your feedback. Indeed, I need to add some tests. |
No. |
@derrabus ok I'll update it! |
e6459ef
to
5a800f0
Compare
8450eac
to
fc382de
Compare
b80fe31
to
d69e0cd
Compare
… pinging it, thus avoiding exceptions. Additionally, it provides the flexibility to configure the frequency of the check to be performed in the case of long-running processes.
d69e0cd
to
3a7e486
Compare
Just gave this a bit more thought, and I'm wondering what will happen if this is done only for MySQL… we know the issue exists for PostgreSQL as well: dunglas/frankenphp#438 I would hate it if we started being the cause for MySQL being recommended over other RDBMSes 😁 Also, I'm wondering if a way of addressing the issue would be to let the user specify a session timeout (or maybe even obtain it from the server itself somehow), and forcibly close the connection unconditionally, without any I don't even know if the concept of session is widespread among RDBMS. |
@@ -210,7 +219,23 @@ public function createExpressionBuilder(): ExpressionBuilder | |||
protected function connect(): DriverConnection | |||
{ | |||
if ($this->_conn !== null) { | |||
return $this->_conn; | |||
$isTimeToCheck = time() - $this->lastCheckedAt >= $this->heartbeat; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
micro-optim: wrap this in if ($this->heartbeat !== null)
to prevent the useless call to time()
if possible.
|
||
$isAvailable = $this->reconnectOnFailure(); | ||
|
||
$this->lastCheckedAt = time(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe could you store the previous call to time()
in a variable to prevent doing two calls, also to prevent weird issues, likely be better to have a time older than the check than younger as in the current implementation.
After reading @stof 's comment, I think that this is not the right way either: @ostrolucky did not like the Symfony PR because it would result in an SQL request for every message, but again, I don't think you actually have to issue a request at all. Instead you could forcibly close the connection (by calling Like @stof, I'd really like to know what exact scenario you want to fix here, because according to MySQL's documentation, one has to wait 8 hours (!) before the issue I think you're trying to address appears. |
@greg0ire When using FrankenPHP, RoadRunner, and the like, it's common that the worker script (and so, if using Symfony, the instance of the Symfony kernel) runs much more longer than 8 hours. |
@dunglas I don't doubt that, what I'm saying is that it's quite unlikely that a RoadRunner or FrankenPHP worker sends 0 SQL requests in 8 hours. In case it does happen, or in case a shorter timeout is used, closing the connection when 95% of the time is elapsed seems IMO like the right thing to do, and needs IMO to be done once per request. I doubt there's a need to perform this check in the middle of processing a request. Benefits:
Cons:
I'm using quotes around "waste", because I think in both cases, if the timeout is high enough, the waste is negligible. |
So, we should have a request listener that does this heartbeat when no connections have been made since a long time? |
Yes indeed, I will add a listener (onKernelRequest) and try to timestamp the last SQL request. Based on the time difference between that last request and the current one, I will proceed to close the connection. |
I'm using frankenphp on a business app that is - not surprinsingly - only used during business hours. 0 requests for 8 straight hours happens pretty much every night...and I've had to disable worker mode because of this. |
symfony/symfony#53214 for listener |
@mrossard then maybe the adjective I should have used is "infrequent". My point is, the code that addresses this issue should not live in what might not be the hot path, but is probably quite warm already. |
Thanks, I'd just found it. I'll try this on my staging environment! |
I think this approach is much better as the code is isolated and simple :) |
Summary
DRAFT:
In long-running processes, allows checking the database connection by pinging it, thus avoiding exceptions. Additionally, it provides the flexibility to configure the frequency of the check to be performed in the case of long-running processes.
As stated in this issue, there is a bug with long-running processes where Doctrine is left in a non-operable state, leading to the issuance of exceptions.
While I acknowledge that this approach appears to resolve the problem, the current code has a strong dependency on Symfony. This is why the PR is kept in draft. If you have any suggestions for improvement and providing an extension point that Symfony can utilize, I am all ears! Or simply I move the code directly from the
ConnectionLossAwareHandler
(Symfony\Bridge) into the Doctrine bal.linked to:
symfony/symfony#53214
doctrine/DoctrineBundle#1739