Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

[Dsn] New component #24267

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

Closed
wants to merge 5 commits into from
Closed

[Dsn] New component #24267

wants to merge 5 commits into from

Conversation

jderusse
Copy link
Member

@jderusse jderusse commented Sep 19, 2017

Q A
Branch? 3.4
Bug fix? no
New feature? yes
BC breaks? no
Deprecations? no
Tests pass? yes
Fixed tickets NA
License MIT
Doc PR TODO

A new Component to handle DSN and retrieve a parameterized connection.
Such logic is actually duplicated in Cache and Lock component.

Other DSN could be handled in the future like pdo

When merged, the next steps are (can not be done in this PR because coimpoent is not released and not available in packagist => could not be used as a dependency)

  • use this component in cache and lock
  • deprecate createConnection from Cache adapters

TODO

  • find usage of other Dsn in the project
  • add an option class in MemcachedFactory too

@jderusse jderusse force-pushed the dsn branch 3 times, most recently from 66d1891 to 6802f09 Compare September 19, 2017 21:58
@jderusse jderusse changed the base branch from master to 3.4 September 19, 2017 21:59
@jderusse jderusse force-pushed the dsn branch 3 times, most recently from 0a9c8f2 to 1beaa92 Compare September 19, 2017 22:14
@nicolas-grekas nicolas-grekas added this to the 3.4 milestone Sep 20, 2017
@javiereguiluz
Copy link
Member

@jderusse thanks for submitting this proposal!

I have a quick question about the long-term vision of this component. Would it also handle the other DSN used in modern Symfony apps: Doctrine DBAL DSN and Swiftmailer DSN? And would it also be useful for the new Queue/AMPQ components that will be included in the future?

@jderusse
Copy link
Member Author

@javiereguiluz yes, the purpose of this component is to centralize usage of DSN parsing. It could be LDAP too

Moreover, with the usage of env variables over arameters.yml, Dsn would be a better alternative to declare endpoint

# before
doctrine:
    dbal:
        driver: "pdo_mysql"
        host: "%env(DATABASE_HOST)%"
        port: "%env(DATABASE_PORT)%"
        dbname: "%env(DATABASE_NAME)%"
        user: "%env(DATABASE_USER)%"
        password: "%env(DATABASE_PASSWORD)%"
        charset: UTF8
        server_version: 5.6


# after
doctrine:
    dbal:
        dsn:     "%env(DATABASE_DSN)%"
        charset:  UTF8
        server_version: 5.6

# + helper if you don't want to use env for the entier DSN
parameters:
  env(DATABASE_DSN): mysql://root:%env(DATABASE_PASSWORD)%@%env(DATABASE_HOST)%/my_app

This PR was about Redis & Memcached because code is already here, and is just about copy/paste.
And I planned to migrate other ConnectionFactories in dedicated PR to simplify the review (Wath do you think)

*
* @author Nicolas Grekas <p@tchwork.com>
*/
class MemcachedConnectionFactory
Copy link
Contributor

Choose a reason for hiding this comment

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

shouldn't they implement an interface?

Copy link
Member

Choose a reason for hiding this comment

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

each factory has a different return type, so I see no point creating an interface (the return type would have to be mixed

Copy link
Member Author

Choose a reason for hiding this comment

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

the input could also vary (string in redis, array or string in memchached).

namespace Symfony\Component\Dsn\Exception;

/**
* Interface for exceptions.
Copy link
Contributor

Choose a reason for hiding this comment

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

this comment is uselesse

Copy link
Member Author

Choose a reason for hiding this comment

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

fixed

*
* @author Jérémy Derussé <jeremy@derusse.com>
*/
class ConnectionFactory
Copy link
Member

Choose a reason for hiding this comment

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

please make it final

Copy link
Member

Choose a reason for hiding this comment

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

👎 for having everything static here

Copy link
Member

Choose a reason for hiding this comment

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

although we don't usually make things final by default, I think we should here, because there is no "type" to inherite from, only behavior to composer.
So 👍 for final.

Copy link
Member

Choose a reason for hiding this comment

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

👎 for non-static, there is no OOP here, just constructors.

}
if (0 === strpos($dsn, 'memcached://')) {
return static::TYPE_MEMCACHED;
}
Copy link
Member

Choose a reason for hiding this comment

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

this implementation is hard to extend / maintainance
when we will want to have 3rd and 4th dsn type, you would just add more if's, but when one wants to use component from outside, he won't have nice way to add more guessers.

what about some indicator classes created and registered in factory class as a list, each of them specialized in recognizing only one type and answering "yes" or "no" ? then, here we would just iterate over the list

Copy link
Member Author

Choose a reason for hiding this comment

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

Instead of hardcoding a list of supported Factories, an other solution would be ot use instances (instead of static calls), definig a common interface with 2 methods supports and createConnection(mixed, array): mixed and use a strategy pattern to iterrate over a list of injected factories.

  • do we really need OOP in factories?
  • interfaces with mixed/mixed seems useless

Copy link
Member

Choose a reason for hiding this comment

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

then at least mark this class as final, maybe even @internal

Copy link
Member

@nicolas-grekas nicolas-grekas Sep 24, 2017

Choose a reason for hiding this comment

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

Adding guesser on user's side is not needed: these are factories, they don't adhere to any contract, exactly like constructors. Instead, they ship behaviors, that users would then wire in their DI layer.
If they have more DSN factories, they'll use these factories directly. No need to create any OOP design here, that'd we completely useless and wouldn't solve any problem, just create new ones.

Copy link
Member

Choose a reason for hiding this comment

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

I red your comments from 15 minutes ago. and especially for that:

then at least mark this class as final, maybe even @internal

Copy link
Member

Choose a reason for hiding this comment

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

@internal would defeat the purpose of the component by stating "don't use it" to users.

class ConnectionFactory
{
const TYPE_REDIS = 1;
const TYPE_MEMCACHED = 2;
Copy link
Member

Choose a reason for hiding this comment

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

if this would be logged anywhere for some reason, 2 is magic value. I advice to make value matching constant, like MEMCACHED here

/**
* Factory for Memcached connections.
*
* @author Nicolas Grekas <p@tchwork.com>
Copy link
Member

Choose a reason for hiding this comment

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

it's new code. is he an author here indeed?

Copy link
Member

Choose a reason for hiding this comment

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

this is code extracted from the Cache component, so he is.

Copy link
Member

@keradus keradus Sep 22, 2017

Choose a reason for hiding this comment

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

cool. then we shall use this in Cache component ;)
lack of that confused me

Copy link
Member

Choose a reason for hiding this comment

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

please read the PR description. It says it is the next step.

*
* @throws \ErrorException When invalid options or servers are provided
*/
public static function createConnection($dsns, array $options = array())
Copy link
Member

Choose a reason for hiding this comment

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

👎 for 100+ lines in one method. please decouple

/**
* @dataProvider provideBadOptions
* @expectedException \ErrorException
* @expectedExceptionMessage constant(): Couldn't find constant Memcached::
Copy link
Member

Choose a reason for hiding this comment

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

using annotations is considered as bad practice for expectations.
please use ->expectException (or ->setExpectedException if lower phpunit is needed)

Copy link
Member

Choose a reason for hiding this comment

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

The issue is we would have to use both of them (as lower PHPUnit is needed for older PHP versions, but PHPUnit 6 removed expectException entirely).
The annotation works in all versions.

And we use them in most places in the Symfony testsuite btw.

Copy link
Member

Choose a reason for hiding this comment

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

PHPUnit 6 removed setExpectedException, not expectException.
Is there a need to support PHPUnit 4, 5, and 6 ?

Copy link
Member

Choose a reason for hiding this comment

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

yes, due to PHP versions being supported (PHPUnit 5 does not support PHP 5.5, which is our min version in Symfony 3.x, and so must be covered in CI)

@jderusse jderusse force-pushed the dsn branch 2 times, most recently from ad501b1 to df8a505 Compare September 22, 2017 16:44
@nicolas-grekas
Copy link
Member

do we really need OOP in factories?

clearly no, these are factories, they belong to the same world as constructors: no contract there.

@jderusse jderusse force-pushed the dsn branch 2 times, most recently from a17f50e to 668c870 Compare September 24, 2017 15:46
@mvrhov
Copy link

mvrhov commented Sep 26, 2017

I'm against any kind of DSN, and I find it bad DX because every time I have to look up on how to build it up. If every property stands for itself, then I can simply call config:debug <bundle>

Copy link
Member

@nicolas-grekas nicolas-grekas left a comment

Choose a reason for hiding this comment

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

LGTM, let's create this component asap to unlock the corresponding PRs on Cache and Lock


/**
* @dataProvider provideInvalidDsn
* @expectedException InvalidArgumentException
Copy link
Contributor

Choose a reason for hiding this comment

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

should be FQCN


/**
* @dataProvider provideInvalidDsn
* @expectedException InvalidArgumentException
Copy link
Contributor

Choose a reason for hiding this comment

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

should be FQCN

@jderusse
Copy link
Member Author

I just merged PR #24323 with this one. It'll help to understand the problem that this new component try to solve.

Dsn Component
=============

Provides utilities for handle Data Source Names.
Copy link
Member

Choose a reason for hiding this comment

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

for creating connections using DSN (Data Source Names) strings.

Copy link
Member Author

Choose a reason for hiding this comment

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

fixed

@nicolas-grekas
Copy link
Member

About the name of the component, if "dsn" is not clear enough, what about "dsn-factories"? or "connection-factories"?

@@ -77,6 +77,7 @@
"suggest": {
"ext-apcu": "For best performance of the system caches",
"symfony/console": "For using the console commands",
"symfony/dsn": "For using the configuring locks and cache with Dsn",
Copy link
Member

Choose a reason for hiding this comment

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

I think this should be moved to "require", so that DSN works out of the box in fwb.

Copy link
Member

Choose a reason for hiding this comment

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

at least must be in require-dev, so that tests can pass

Copy link
Member Author

Choose a reason for hiding this comment

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

moved into require section

@@ -1704,9 +1705,13 @@ private function registerLockConfiguration(array $config, ContainerBuilder $cont
break;
case $usedEnvs || preg_match('#^[a-z]++://#', $storeDsn):
if (!$container->hasDefinition($connectionDefinitionId = $container->hash($storeDsn))) {
if (!class_exists(ConnectionFactory::class)) {
throw new LogicException('Lock stores cannot be configured as the Dsn component is not installed.');
Copy link
Member

@nicolas-grekas nicolas-grekas Sep 28, 2017

Choose a reason for hiding this comment

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

should be removed once/if dsn is added as a requirement in composer.json

Copy link
Member Author

Choose a reason for hiding this comment

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

removed (same for CachePoolPass)


switch ($type) {
case ConnectionFactory::TYPE_MEMCACHED:
return MemcachedConnectionFactory::createConnection($dsn, $options);
Copy link
Member

Choose a reason for hiding this comment

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

bad name, this has been renamed now

Copy link
Member Author

Choose a reason for hiding this comment

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

fixed

@@ -0,0 +1,13 @@
Dsn Component
Copy link
Contributor

Choose a reason for hiding this comment

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

- Dsn Component
+ DSN Component

Copy link
Member Author

Choose a reason for hiding this comment

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

The component's name is really Dsn Component. See @javiereguiluz comment

@OskarStark
Copy link
Contributor

About the name of the component, if "dsn" is not clear enough, what about "dsn-factories"? or "connection-factories"?

👍 for dsn-factories

@javiereguiluz
Copy link
Member

dsn-factories or connection-factories don't look like component names to me. But maybe it's because this doesn't look like a "real component" either? (similar to what Fabien said). It's very useful to avoid repeating code in Lock and Cache ... but is it useful outside of that?

@nicolas-grekas
Copy link
Member

It's very useful to avoid repeating code in Lock and Cache ... but is it useful outside of that?

I personnaly it is, because Loch & Cache are not the only use cases of Redis/memcached connections, and this allows creating them easily using DSN, for any other use case. Just my 2cts :)

@fabpot
Copy link
Member

fabpot commented Sep 28, 2017

Having the other PR here does not change my point of view. This is not about a DSN component. Changing the name might make it a bit better, but still, I've a hard time thinking about this "code sharing" between several component as being a component by itself. Still 👎 about this change, and certainly 👎 for rushing it just before code freeze.

@nicolas-grekas
Copy link
Member

nicolas-grekas commented Sep 28, 2017

OK, so let's close this.
Instead, I propose to remove the DSN-factories from Lock altogether. We don't need them for the FrameworkBundle integration since the ones in Cache are just fine for Lock. Let use them for now, and maybe forever.
If someone has another idea to remove the code duplication, please share.

fabpot added a commit that referenced this pull request Sep 28, 2017
This PR was merged into the 3.4 branch.

Discussion
----------

[Lock] Use cache connection factories in lock

| Q             | A
| ------------- | ---
| Branch?       | 3.4
| Bug fix?      | no
| New feature?  | no (feature removal)
| BC breaks?    | no (if merged in 3.4)
| Deprecations? | no
| Tests pass?   | yes
| Fixed tickets |
| License       | MIT
| Doc PR        |

An alternative to #24267 to share code between cache and lock.

Commits
-------

95358ac Share connection factories between cache and lock
symfony-splitter pushed a commit to symfony/framework-bundle that referenced this pull request Sep 28, 2017
This PR was merged into the 3.4 branch.

Discussion
----------

[Lock] Use cache connection factories in lock

| Q             | A
| ------------- | ---
| Branch?       | 3.4
| Bug fix?      | no
| New feature?  | no (feature removal)
| BC breaks?    | no (if merged in 3.4)
| Deprecations? | no
| Tests pass?   | yes
| Fixed tickets |
| License       | MIT
| Doc PR        |

An alternative to symfony/symfony#24267 to share code between cache and lock.

Commits
-------

95358ac98f Share connection factories between cache and lock
fabpot added a commit that referenced this pull request Oct 5, 2017
This PR was merged into the 3.4 branch.

Discussion
----------

[DoctrineBridge] Deprecate dbal session handler

| Q             | A
| ------------- | ---
| Branch?       | 3.4
| Bug fix?      | no
| New feature?  | no <!-- don't forget updating src/**/CHANGELOG.md files -->
| BC breaks?    | no
| Deprecations? | yes
| Tests pass?   | yes
| Fixed tickets | #20501
| License       | MIT
| Doc PR        |

The DbalSessionHandler misses all the improvements from the PdoSessionHandler (lock modes, delayed GC, configurable naming). The only advantage it had was the ability to work with non-pdo drivers. But since DBAL requires PDO now as well (doctrine/dbal@36df682) even that is not really relevant anymore.

Stofs argument in #20501 (comment) sound like a new feature that can be implemented separately. Ref. #24267

Commits
-------

ffc74eb [DoctrineBridge] Deprecate DbalSessionHandler
@jderusse jderusse deleted the dsn branch August 2, 2019 12:14
@Nyholm Nyholm mentioned this pull request May 28, 2020
3 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

9 participants
Morty Proxy This is a proxified and sanitized view of the page, visit original site.