Skip to content

[FrameworkBundle][Cache] Do not process cache aliases that are not present in the container #29384

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 1 commit into from

Conversation

allflame
Copy link
Contributor

Q A
Branch? 4.2
Bug fix? yes
New feature? no
BC breaks? no
Deprecations? no
Tests pass? yes
License MIT

1484117

Introduced a major problem when framework-bundle is used WITHOUT doctrine.
On kernel compilation this results in ServiceNotFoundException: You have requested a non-existent service "doctrine.dbal.default_connection".
This is due to the fact there is no check whether such definition is present in the container
Since there is no clear view as how to handle such a situation (remove default value from configuration or ignore those that are not present) I chose the latter.

@allflame allflame changed the title Do not process cache aliases that are not present in the container [FrameworkBundle][Cache] Do not process cache aliases that are not present in the container Nov 30, 2018
@nicolas-grekas nicolas-grekas added this to the 4.2 milestone Nov 30, 2018
@nicolas-grekas
Copy link
Member

nicolas-grekas commented Nov 30, 2018

I don't think that works: the container in FrameworkExtension is isolated and won't give access to other bundle's services.
If you don't declare a PDO pool, that shouldn't happen. And if you do and it doesn't work, that means you should declare the default_pdo_provider yourself - and thus not hit the issue.
As you can see, I don't understand how you hit this :)
Can you please provide a reproducer?

@allflame
Copy link
Contributor Author

@nicolas-grekas You defined default value for pdo here

->scalarNode('default_pdo_provider')->defaultValue('doctrine.dbal.default_connection')->end()

and used existing loop that calls CachePoolPass::getServiceProvider(). The problem is that getServiceProvider method detects only (presumably) dsn and define a service for you, but you passed direct alias that was ignored. So guess what happens if you add an alias definition to a non-existent service.

@nicolas-grekas
Copy link
Member

nicolas-grekas commented Nov 30, 2018

That default config leads to the creation an alias named cache.default_pdo_provider which is used by cache.adapter.pdo. But since this service is abstract, the fact that the service provider is missing shouldn't be an issue. It would become an issue when a concrete pdo-based pool is declared and doctrine.dbal.default_connection doesn't exist. In this case, you have to define the default_pdo_provider configuration option anyway so that you should be able to make things work.

Please provide a reproducer if you still think there is a bug - or at least more details.

@allflame
Copy link
Contributor Author

@nicolas-grekas I probably can provide a minimal installation on Monday, but I think I'm either missing a point or can't explain myself well.
I'd rather ask: is it your impression that your commit should work just fine even if there is no doctrine.dbal.default_connection service in the container?

@nicolas-grekas
Copy link
Member

Everything works when playing with eg the website-skeleton - even without Doctrine. So yes, I don't see the issue right now. Looking forward to your reproducer :)

@allflame
Copy link
Contributor Author

allflame commented Nov 30, 2018

Ok, i think I found the problem.
That's what's get generated

<service id="cache.default_pdo_provider" alias="doctrine.dbal.default_connection" public="false"/>

This will obviously crash if doctrine.dbal.default_connectionis not in the container. And it's not. But it's working on skeleton. So the reason it's working is because of RemovePrivateAliasesPass that will remove this alias as an optimization procedure by default.
Personally I don't think the idea to rely on such a behavior should be considered at all.

To be clear: I believe that the suggested fix shouldn't be taken as is since I do not know what the initial intention of the feature was, but creating alias to a non-existent service shouldn't be done either.
How it breaks: in unit test to avoid injecting all the dependencies I mark all the aliases public and fetch them from container. Since the alias is now public it will not be removed from the container and thus will cause a crash.

@nicolas-grekas
Copy link
Member

nicolas-grekas commented Nov 30, 2018

Do you experience the issue or not? If this is purely theoretical, then you should know there are tons of services that rely on this behavior. Or if it's something that can be reproduced, please, really, describe how you get it.

@nicolas-grekas
Copy link
Member

nicolas-grekas commented Nov 30, 2018

How it breaks: in unit test to avoid injecting all the dependencies I mark all the aliases public and fetch them from container. Since the alias is now public it will not be removed from the container and thus will cause a crash.

OH OK! Then that's the issue. There can be way more things that could break with this strategy.
I'd recommend creating special test aliases if you want to get acces to any services.
Typically in services_test.yml:

services:
  test.my_service:
    alias: my_service
    public: true

@allflame
Copy link
Contributor Author

@nicolas-grekas This works if you need to mock couple of them. If you have dozens this is not that good of approach.
I don't see how this can break something. On top of that in fairly complex application that was using quite a bit of Symfony components the only failing place is this one. And it runs "Make all aliases public" compiler pass, so it definitely should've caught at least something else. Since I'm not that familiar with Symfony I wouldn't go that far as to question

there are tons of services that rely on this behavior

but this behavior causes a lot of side effects as well (#29359).
My impression is that you should be able to drop the private alias without breaking the container, but it's should, not must. I can be wrong though.
I don't like the fact that it couples two independent components together in very indirect way, but it's just my personal opinion

@nicolas-grekas
Copy link
Member

While merging unrelated PRs, one of them made me remind we have a way to deal with such situations.
Here we are, see #29399

@allflame
Copy link
Contributor Author

allflame commented Dec 1, 2018

@nicolas-grekas Hey, thanks for the update. I do have a workaround by just setting up default_pdo_driver to null in the config though.

@nicolas-grekas
Copy link
Member

Thanks for reporting and for the discussion.

nicolas-grekas added a commit that referenced this pull request Dec 1, 2018
…only if the package is installed (nicolas-grekas)

This PR was merged into the 4.2 branch.

Discussion
----------

[FrameworkBundle] define doctrine as default_pdo_provider only if the package is installed

| Q             | A
| ------------- | ---
| Branch?       | 4.2
| Bug fix?      | yes
| New feature?  | no
| BC breaks?    | no
| Deprecations? | no
| Tests pass?   | yes
| Fixed tickets | #29384
| License       | MIT
| Doc PR        | -

Commits
-------

cf75012 [FrameworkBundle] define doctrine as default_pdo_provider only if the package is installed
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.

3 participants