-
-
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
Deprecate master/slave terminology and underlying feature #4055
Conversation
/** | ||
* Connects to a specific connection. | ||
*/ | ||
private function connectTo(string $connectionName): DriverConnection |
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.
If you think somebody relies on connectTo
or chooseConnectionConfiguration
, we could make them public. Doing so after this is merged and release would not be a BC break though.
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.
If somebody needs to choose the connection explicitly, they should operate those connections manually w/o this wrapper.
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.
Why? And who would that somebody be? This method is private
, so it can't be used by somebody outside this class if that's who you were thinking.
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.
If you think somebody relies on
connectTo
orchooseConnectionConfiguration
, we could make them public.
It was the answer to your comment. I don't think that this should be made part of the public API.
lib/Doctrine/DBAL/DriverManager.php
Outdated
@@ -142,17 +142,29 @@ public static function getConnection( | |||
|
|||
$params = self::parseDatabaseUrl($params); | |||
|
|||
// URL support for MasterSlaveConnection | |||
// @todo: deprecated, notice thrown by connection constructor |
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.
Will the Connection
constructor get a deprecation message in this PR?
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.
Ah right that's a part I forgot to backport from #4054
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.
Unsure about the @todo
part, I copied it from #4054, is this a way we have to remember to remove things on 3.0.x?
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.
I don't know. I thought @deprecated
and/or trigger_error()
is usually in use.
755b058
to
52da3f5
Compare
Codecov Report
@@ Coverage Diff @@
## 2.11.x #4055 +/- ##
=========================================
Coverage 71.02% 71.02%
Complexity 5261 5261
=========================================
Files 217 217
Lines 13355 13358 +3
=========================================
+ Hits 9485 9488 +3
Misses 3870 3870
Continue to review full report at Codecov.
|
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.
API-wise looks good. We should be able to address the implementation details issues later.
* | ||
* @var DriverConnection[]|null[] | ||
*/ | ||
private $connections = ['primary' => null, 'replica' => null]; |
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.
As far as I understand, it's one primary and many replicas. Why do we need to keep them under one array with magic keys? It's fine if we don't resolve it in this ticket.
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.
I can resolve it in this ticket, I think that will be the easiest part TBH :P
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.
In fact it's just the one replica (picked at random), as stated in the comment above. Your comment still applies though.
/** | ||
* Connects to a specific connection. | ||
*/ | ||
private function connectTo(string $connectionName): DriverConnection |
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.
If somebody needs to choose the connection explicitly, they should operate those connections manually w/o this wrapper.
02971b3
to
0f368ee
Compare
} | ||
|
||
|
||
parent::__construct($params, $driver, $config, $eventManager); |
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 is another place where my eyes are bleeding. The parent class doesn't understand the "replica" key but for some reason, we're passing it there. Shouldn't the concept of multiple connections be encapsulated to this very class?
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.
🤔 uh yeah that's nasty. Are you suggesting unset
-ing replica
and primary
, storing them in some other property, or not calling the parent constructor altogether (and then maybe stop extending Connection
??)
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.
We cannot avoid extending the connection because there's no DBAL-level connection interface to implement. Unsetting and managing the replica parameters within the class would help migrate to composition later.
|
||
public function query(): Statement | ||
{ | ||
$this->switchToPrimary(); |
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.
And it's even worse here. Why are we switching to the primary endpoint to execute a query?
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.
I won't lie, I have no idea…
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.
the query function can be used for any kind of SQL, including write operations. This cannot be detected without parsing the SQL statement, which is a rabbit hole. This is why for safety reason the connectoin assumes that when using $connection->query(), you might be performing a write operation.
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 is why the developer should be in control of which connection to use for a given statement. Not the abstraction layer.
@morozov this looks nasty too: dbal/lib/Doctrine/DBAL/Connections/MasterSlaveConnection.php Lines 34 to 41 in 0ad4c16
|
|
||
$driverOptions = $params['driverOptions'] ?? []; | ||
|
||
$connectionParams = $this->chooseConnectionConfiguration($connectionName, $params); |
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.
So does this mean that every time we "switch" between the primary and replica endpoints, the replica will be randomly chosen and reconnected to while the old replica connection will be closed? Even if there's only one replica configured. What's the rationale behind this behavior?
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.
🤔 we are not supposed to switch from the primary to the replica, are we?
* 3. If master was picked once during the lifetime of the connection it will always get picked afterwards. |
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 is really questionable behavior. If I insert a "user logged in" event in one table and then query a heavy report from the other, why should the report use the master connection?
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.
The step from primary to replica is not really a common use-case, but what you describe could be an example. If the application does a single write early in the request and you can isolate that and don't need to care about replication lag with this write, then you could switch back to a replica. The idea would be:
$connection->connect('primary');
$connection->executeUpdate("....");
$connection->connect("replica");
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.
Why not just do $primaryConnection->executeUpdate(); $replicaConnection->doWhatever();
? Why keep one connection as a shared state and switch between the inner connections implicitly?
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.
The answer is probably "convenience" (at the cost of some usefulness). Like always with such things ;)
Given the number of WTFs that this code produces, I'd personally prefer to just deprecate it for now. If we don't manage to implement a proper replacement (and given all the possible variations of the behavior, I don't think we should), the old code could be just copy/pasted in the project that needs this functionality. But looking at the behaviors that it implements, I hope nobody in their right mind uses it in production. |
@morozov I agree. In my company, we have a RW endpoint and an RO endpoint, we just created 2 connections, and don't use this class, and it works just fine. These features could be brought back at some point with a brand new design, that would have to avoid this unholy mix of composition and inheritance. |
This is how I see it should be implemented. The existing implementation has too many questionable assumptions to be the one and the only that is shipped with DBAL. |
0f368ee
to
e6a7703
Compare
Ah I need to add an upgrade note for this. |
e6a7703
to
552eb88
Compare
UPGRADE.md
Outdated
## Deprecated `MasterSlaveConnection` | ||
|
||
The naming is offensive, the implementation very questionable. Use regular | ||
connections and switch between them explicitely. |
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.
Should it be “explicitly”? I'd also avoid “switch” — it implies shared state. Let's use “Use each endpoint connection explicitly”.
552eb88
to
0d621ba
Compare
Not only is the terminology needlessly hurtful, the design is a very leaky abstraction, and users are better off using regular connections and managing them themselves explicitly.
0d621ba
to
5149e52
Compare
Closing in favor of #4054 |
Summary
MasterSlaveConnection
is left untouched, deprecated, copy/pasted/improvedAlternative to #4054
Targeting 2.11.x because of the deprecation.