Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Conversation

@bzikarsky
Copy link

Overview

This PR relates to #589 and refactors SourceCache into pluggable caching-backends while keeping backwards compatibility.

Current state

  • Implemented new caching classes
  • Integrated new caching classes into ContainerBuilder
  • Added unit-tests for the new classes

Contents

Create an explicit, new class hierarchy for definition caching

  • Establish interface Cache\Cache which represents any definition-cache
  • Move logic from SourceCache to both Cache\AbstractCache and
    Cache\ApcuCache
  • Deprecate SourceCache in favour of Cache\ApcuCache but still
    keep its functionality by extending the former. This is to keep
    backwards compatibility
  • Create a new simple cache-backend ArrayCache which just caches
    lookups in memory

Integrate new Cache backend into ContainerBuilder

Instead of tracking the caching status with a boolean, this commit changes
$sourceCache to a nullable string. This allows to define different cache-
backends.

::enableDefinitionCache(..) not takes an optional parameter - optional to
keep backwards compatibility - with the class-name of the definition cache to
use. By default the ApcuCache is used which is identical to the deprecated,
original SourceCache

Tests

TODO 😄

Future work

  1. This functionality can be used to provide adapters to more general caching-backends, such as PSR-6 or PSR-16 compatible libraries.

  2. In a major release SourceCache can be removed.

@bzikarsky bzikarsky force-pushed the pluggable-caching-backends branch 3 times, most recently from e6cb96d to 2cbcda2 Compare March 1, 2019 08:40
@mnapoli
Copy link
Member

mnapoli commented Mar 1, 2019

Hi, thank you for the effort you put into that!

Unfortunately I'm still not sure I understand the need behind that.

The other thing is that this adds a lot of complexity and new code to maintain, and using an abstraction or PSR-6/16 adds overhead as well. I did some testing back then and using Doctrine Cache (which is fairly optimized) did add some overhead compared to a custom and simple cache implementation around APCu directly.

/**
* Returns the DI definition for the entry name.
*
* @param string $name The name of the definition to get
Copy link
Contributor

Choose a reason for hiding this comment

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

Not a big fan of adding obvious annotations. The type is already enforced, and the explanation for a method "getDefinition" with the parameter "name" is also pretty obvious: You get the definition for the name.

Copy link
Author

Choose a reason for hiding this comment

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

I think if there is a docblock, it should be complete, shouldn't it? I'm all for dropping the comment after $name though.

Copy link
Contributor

@SvenRtbg SvenRtbg Mar 1, 2019

Choose a reason for hiding this comment

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

My personal opinion: Docblocks should be as short as possible, and should not contain redundant information. Comments will start to contain wrong information in the long run, the less info they contain, the better.

Now, PHP-DI is a little bit inconsistent in this regard. If you look over some code, you'll find methods like this

* Enable or disable the use of autowiring to guess injections.
*
* Enabled by default.
*
* @return $this
*/
public function useAutowiring(bool $bool) : self
without @param for the parameter and the opposite like this
*
* @param bool $writeToFile If true, write the proxies to disk to improve performances
* @param string|null $proxyDirectory Directory where to write the proxies
* @throws InvalidArgumentException when writeToFile is set to true and the proxy directory is null
* @return $this
*/
public function writeProxiesToFile(bool $writeToFile, string $proxyDirectory = null) : self
with every parameter annotated and commented.

I cannot define the code style for this project, @mnapoli is the project lead.

* @var string|null
*/
private $sourceCache = false;
private $sourceCache;
Copy link
Contributor

Choose a reason for hiding this comment

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

I'd probably use another name for this property for defining the class name instead of recycling a property that already has a meaning, especially if it's type would change.

Copy link
Author

@bzikarsky bzikarsky Mar 1, 2019

Choose a reason for hiding this comment

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

Renaming the property is a non-issue. What would you prefer?

Copy link
Contributor

Choose a reason for hiding this comment

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

cacheClass

@bzikarsky
Copy link
Author

bzikarsky commented Mar 1, 2019

The need for this is simple: If you run PHP in any long-running-process configuration (php-pm, amp, etc.) it's fully sufficient to simply cache in-memory. APCu is an overhead for us, we don't even run it in production. I also think that the introduced overhead of 2 method calls is negligable. In the end it's still only 1 object which does the same as SourceCache - only that apc_fetch and apc_store are moved behind those 2 new methods.

Essentially it comes down to this: We have currently a copy of SourceCache and ContainerBuilder in our source-tree solely for the purpose to cache into arrays. It'd be great to be able to drop those.

If you give your 👍 I will addthe missing tests for the new classes.

Benjamin Zikarsky added 3 commits March 29, 2019 10:55
- Establish interface `Cache\Cache` which represents any definition-cache
- Move logic from `SourceCache` to both `Cache\AbstractCache` and
  `Cache\ApcuCache`
- Deprecate `SourceCache` in favour of `Cache\ApcuCache` but still
  keep its functionality by extending the former. This is to keep
  backwards compatibility
- Create a new simple cache-backend `ArrayCache` which just caches
  lookups in memory
Instead of tracking the caching status with a boolean, this commit changes
`$sourceCache` to a nullable string. This allows to define different cache-
backends.

`::enableDefinitionCache(..)` not takes an optional parameter - optional to
keep backwards compatibility - with the class-name of the definition cache to
use. By default the `ApcuCache` is used which is identical to the deprecated,
original `SourceCache`
@bzikarsky bzikarsky force-pushed the pluggable-caching-backends branch from 2cbcda2 to ae5adc6 Compare March 29, 2019 09:56
@bzikarsky
Copy link
Author

@mnapoli Please consider merging something along these lines. The copied and modified version of ContainerBuilder we need to maintain in our source-tree broke prod builds after a composer upgrade again.

I'm more than happy to implement the missing tests/refactorings if you greenlight this implementation.

@mnapoli
Copy link
Member

mnapoli commented Apr 21, 2019

@bzikarsky sorry for the delay.

The use case makes sense (not using APCu yet caching definitions in memory).

I've been thinking about this and I wonder if it wouldn't make sense to always have the in-memory cache, even when using APCu. Indeed if a process gets the same definition twice it doesn't make sense to fetch it twice from APCu. That wouldn't mean a noticeable performance improvement but I think it might be a simpler architecture (no pluggable cache backend).

What do you think?

@bzikarsky
Copy link
Author

I agree on having an in-memory cache "by default".

What I don't understand yet is why you insist to not move the "concern to cache" into a separate interface. From an architecture point-of-view smaller classes with a smaller footprint make sense in my opinion (and are also easier to test).

Going with the proposed way would open the code up to all different kind of caching use-cases and with Memory(APCu()) only being one/the default.

@mnapoli
Copy link
Member

mnapoli commented Apr 23, 2019

Well I was thinking of a simple key-value map of definitions that were already resolved but it seems I forgot that this exists already:

PHP-DI/src/Container.php

Lines 151 to 154 in 2c7a3a6

// Local cache that avoids fetching the same definition twice
if (!array_key_exists($name, $this->fetchedDefinitions)) {
$this->fetchedDefinitions[$name] = $this->definitionSource->getDefinition($name);
}

Does that cover your use case?

@bzikarsky
Copy link
Author

So... I know this is a very old PR, but priorities moved, but here we are again.

I checked your proposal, but the issue is:

While Container::getDefinition uses the $this->fetchedDefinitions cache. Container::injectOn does not. We introduced hack which led to this PR, because we have a lot of injectOn callsites for legacy reasons.

I am not sure: Maybe it's just possible to make injectOn use fetchedDefinitions (or getDefinition), but I assume there is a reason that injectOn neither stores nor reads from this "cache".

I'd like to have your input:

  1. Do you think injectOn could be modified in such a way this PR is not neccessary anymore?
  2. If this is not the case, would you merge this PR if I rebase it on the current master?

@mnapoli
Copy link
Member

mnapoli commented Mar 19, 2020

I had a look, I think the fetchedDefinitions cache was omitted from injectOn() by mistake. I can't recall any reason not to use it there. (the tests should also validate that nothing break if you try to use it)

I think 1. is the simplest solution. If you manage to implement it that would be best!

@bzikarsky
Copy link
Author

Closed in favour of #709

@bzikarsky bzikarsky closed this Mar 20, 2020
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