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

Removed ServerInfoAwareConnection#requiresQueryForServerVersion() as implementation detail #3807

Merged

Conversation

morozov
Copy link
Member

@morozov morozov commented Jan 3, 2020

Q A
Type improvement
BC Break yes

Testing the implementations of this method requires partial mocking of the implementing classes which makes it impossible to make them final (#3590, build #632116824).

Additionally, this API breaks the encapsulation of the driver layer: instead of exposing the fact of whether the connection will perform a query to detect the server version, the driver should just instantiate a platform corresponding to a connection.

The rationale behind introducing this method (#487) is really questionable:

This is also required for drivers that cannot return the database server version without an additional query (performance reasons).

  1. There's no evidence that an underlying driver that exposes the server version via its API doesn't make a request of any kind to the server.
  2. For an application that works with any realistic database, a query like SELECT VERSION() wouldn't be a performance bottleneck.
  3. Even if it was, it's always possible to specify the platform version upfront. Otherwise, the current logic of falling back to a default platform may cause undefined behavior of the application (we don't test the compatibility of the lowest level of the DBAL platform with all supported server versions). Remember, “If it doesn’t work, it doesn’t matter how fast it doesn’t work.”

In addition to the above, the only driver that doesn't support the platform version detection via the underlying driver API is sqsql which is barely supported.

In the future, I'd like to get rid of the ServerAwareConnection interface at all make the platform instantiation part of the driver and driver connection interfaces:

interface Driver
{
    public function connect() : Connection;
    public function createPlatform(Connection $connection) : Platform;
}

interface Connection
{
    public function getServerVersion() : string;
}

Depending on whether the DBAL supports multiple versions of the given platform, the driver will or will not call getServerVersion() on the given connection and will always return a platform. This way, the wrapper logic will not have any conditions based on the implementation details of the underlying driver and the platform.

…implementation detail

Testing the implementations of this method requires partial mocking of the implementing class which makes it impossible to make them `final` (doctrine#3590).

Additionally, this API breaks the encapsulation of the driver layer: instead of exposing the fact of whether the connection will perform a query to detect the server version, the driver should just instantiate a platform corresponding to a connection.

The rationale behind introducing this method (doctrine#487) is really questionable:

> This is also required for drivers that cannot return the database server version without an additional query (performance reasons).

1. There's no evidence that an underlying driver that exposes the server version via its API doesn't make a request of any kind to the server.
2. For an application that works with any realistic database, a query like `SELECT VERSION()` wouldn't be a performance bottleneck.
3. Even if it was, it's always possible to specify the platform version upfront. Otherwise, the current logic of falling back to a default platform may cause undefined behavior of the application (we don't test the compatibility of the lowest level of the DBAL platform with all supported server versions). Remember, “If it doesn’t work, it doesn’t matter how fast it doesn’t work.”

In addition to the above, the only driver that doesn't support the platform version detection via the underlying driver API is `sqsql` which is barely supported.
@morozov morozov force-pushed the remove-requires-query-for-server-version branch from 0c3b349 to 8d01589 Compare January 3, 2020 09:38
@beberlei
Copy link
Member

beberlei commented Jan 8, 2020

@morozov the problem we tried to solve, with the check for server versions is not the time it takes to make the server version query, but that making the query opens up the connection, even though the current request might not even need the database.

Making the query breaks the laziness of the connection.

However since we recommend to set the server version as param now anyways it might make sense to just show this everywhere explicitly in the documentation.

@morozov
Copy link
Member Author

morozov commented Jan 8, 2020

but that making the query opens up the connection, even though the current request might not even need the database.

I don't see how this is relevant to the laziness. Whether this is a query or another driver API call, detecting the server version needs a connection establishing which makes the connection not lazy.

@morozov
Copy link
Member Author

morozov commented Jan 8, 2020

@beberlei could you clarify how exactly lazy the connection is meant to be? I tried the following code:

use Doctrine\DBAL\DriverManager;

$conn = DriverManager::getConnection([
    'driver' => 'pdo_mysql',
    'user'   => 'root',
    'dbname' => 'mysql',
]);

exit();

In this scenario, the code of PDOMySql\Driver::connect() or even MysqliConnection::requiresQueryForServerVersion() is not executed because there's no need for the server version.

We can test the requirement of the laziness by mocking a driver and making sure its connect() method is not invoked in certain scenarios where the laziness should be guaranteed.

@beberlei
Copy link
Member

beberlei commented Jan 8, 2020

@morozov I only remember this popped up massively in the context of Symfony, so maybe the DoctrineBundle acquires the platform early for something and triggers the query.

@beberlei
Copy link
Member

beberlei commented Jan 8, 2020

@morozov just checked with a callgraph that the main reason it happens is actually the ORM (v2.6), which loads the platform for entity persisters, proxy generators and other services, that can be created already without a query being executed.

@morozov
Copy link
Member Author

morozov commented Jan 8, 2020

Even if that's the case, as I said earlier, acquiring a specific version-aware platform requires a connection. It's irrelevant whether the version is detected via a query.

Should we proceed with the change or you want some research to be done?

UPD: this response was to #3807 (comment).

@beberlei
Copy link
Member

beberlei commented Jan 8, 2020

@morozov I believe we should move forward, and I open a ticket on ORM for fetching the platform more lazily in some places (not in constructors), so that we can avoid accidentally loading the connection, when the platform is fetched but not used.

@morozov
Copy link
Member Author

morozov commented Jan 8, 2020

@beberlei please file the ticket since I'm not that well familiar with ORM and the specific components where the platform instantiation needs to be revisited.

@beberlei
Copy link
Member

beberlei commented Jan 8, 2020

@morozov done here: doctrine/orm#7972

@morozov morozov added this to the 4.0.0 milestone Jan 8, 2020
@morozov morozov self-assigned this Jan 8, 2020
@morozov morozov merged commit 2e54e78 into doctrine:master Jan 8, 2020
@morozov morozov deleted the remove-requires-query-for-server-version branch January 8, 2020 19:47
@morozov morozov modified the milestones: 4.0.0, 3.0.0 Nov 17, 2020
@github-actions github-actions bot locked as resolved and limited conversation to collaborators Jul 26, 2022
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants