Skip to content

Created cached entrypoint look up class #12

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

Merged
merged 5 commits into from
Mar 1, 2019
Merged

Created cached entrypoint look up class #12

merged 5 commits into from
Mar 1, 2019

Conversation

codayblue
Copy link
Contributor

  • Created the CacheEntrypointLookup
  • modified the EntrypointLookup to have a protected method instead of private to easily just extend it
  • Created the cache compiler pass that reads the file and will save it to the symfony cache with the service construction
  • Created a config option to enable the cache class and compiler pass
  • Enabled the compiler pass in the bundle
  • Updated tests

This will resolve issue #3

@codayblue
Copy link
Contributor Author

@weaverryan is there anything else needed with this PR?

protected function getEntriesData(): array
{
return $this->entrypoints;
}
Copy link
Member

Choose a reason for hiding this comment

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

Hmm. Unfortunately, making this protected isn't the best way to make this extension point - though it was the easiest! ;) It forces us to make the method protected... and it also forces us to remove the @final on the parent class (which we would like to not need to do).

I see why you did it - there is actually a decent amount of logic in EntrypointLookup other than just json_decode() on the manifest.json file.

I have a different idea. Sorry for the back-and-forth - this isn't an area that I usually touch in the Symfony core :). Here's my idea:

  1. Let's always cache. Basically, we will move the caching logic into EntrypointLookup and not make it optional
  2. But, we will not use, for example, the cache.system cache or something like that. Instead, there is a system in the Config component of Symfony for this type of caching - an object called the ConfigCacheFactory.

Basically, we use the ConfigCacheFactory to cache the results of parsing the entrypoints.json file. But, built into that system is a mechanism that will automatically recache it if the file modified time of entrypoints.json is updated (in dev only, of course). It means that, in dev, the cache is loaded the first time you access the page, then reloaded whenever the file changes. In prod, we would create a new "cache warmer" so that it's loaded during cache:warmup.

A good example of this system is the translator:

Can you try this out? I think it's ultimately the best solution.

Thanks!

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I like this solution better. I just did not know about config cache. I will implement this.

Copy link
Member

@weaverryan weaverryan left a comment

Choose a reason for hiding this comment

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

NIce work! I left a few comments - and I know of one other thing we'll need: a cache warmer (so the cache file can be created during cache:warmup). For example: https://github.com/symfony/symfony/blob/254f4c8f7f116733fb32cbb872f9184c094630b1/src/Symfony/Bundle/FrameworkBundle/CacheWarmer/TranslationsCacheWarmer.php

}

if (!isset($options['cache_dir'])) {
throw new RuntimeException('Cache directory not set.');
Copy link
Member

Choose a reason for hiding this comment

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

If it's not set, then we basically just need to disable caching. This is needed, at the very least, for backwards compatibility - we can't add a required option/argument to this class without a BC layer. However, we can trigger a deprecation warning so that we can make this option required in the future.

EOF
, var_export(json_decode(file_get_contents($this->entrypointJsonPath), true), true));

$cache->write($content);
Copy link
Member

Choose a reason for hiding this comment

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

Here, I believe the second argument is Resource - we need 1 resource that points to the entrypoints.json file. This is the key to how cache invalidation works in the dev environment: by passing that here, the cache will know to regenerate if that file is updated.

@codayblue
Copy link
Contributor Author

@weaverryan I have updated the PR. Sorry for the long wait. Also all the tests passed? There appears to be a deprecation warning from PHP unit that caused Travis to fail.

@weaverryan
Copy link
Member

weaverryan commented Dec 15, 2018

I'm going to wait for #17 and #21 before this one - especially #21, which may need to add a constructor arg to EntrypointLookup to fix a bug.

Thanks!

});
}

public function warmUp($cacheDir)
Copy link
Member

Choose a reason for hiding this comment

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

The warmup should now use the cache service to read and set the cache - no more ConfigCacheFactory stuff

@@ -9,6 +9,9 @@

<service id="webpack_encore.entrypoint_lookup" class="Symfony\WebpackEncoreBundle\Asset\EntrypointLookup">
<argument /> <!-- entrypoints.json path -->
<call method="setCache">
<argument key="$cache" type="service" id="webpack_encore.cache" />
</call>
Copy link
Member

Choose a reason for hiding this comment

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

One tricky thing is that there are now more than just this one service for this class. Look at the Extension class in this bundle - you’re now able to configure multiple builds and each will have its own service. We need to call setCache on all of these AND make the warmer work for all of them.

- Altered entry point lookup to check if cache has been added and if it has then cache the file
- Altered collection to set the cache for every lookup
- Altered services file to create a cache service
- Updated tests to test caching
- Updated cache warming to warm all the builds

Github Issue: #3
{
$entryPointCollection = $this->container->get('webpack_encore.entrypoint_lookup_collection');

if ($entryPointCollection instanceof EntrypointLookupCollection) {
Copy link
Member

Choose a reason for hiding this comment

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

We could simplify if we added a getAll() method to this object. Then we wouldn’t need to inject builds here. We could just foreach over getAll()

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The container interface does not have a get all. Is that a method on the class that gets injected?

Copy link
Member

Choose a reason for hiding this comment

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

Yea, I think we should actually inject the EntrypointLookupCollection service here directly - I think there’s no need to inject the container and then fetch it. Then, yes, the getAll() method would be on that class (the collection class) - it will allow us to easily ask for all the EntrypointLookup instances.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The collection class gets an instance of ContainerInterface this does not have a get all. So should I pass the builds to the collection? So it can track them. When I debug through I see it is the ServiceLocator class from the dependence injection component. I dont see a get all method there but I can see how it works and try to extend it to make that possible.

use Symfony\Contracts\Service\ResetInterface;
use Symfony\WebpackEncoreBundle\Exception\EntrypointNotFoundException;

interface EntrypointLookupInterface extends ResetInterface
interface EntrypointLookupInterface extends ResetInterface, WarmableInterface
Copy link
Member

Choose a reason for hiding this comment

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

What’s the purpose of the WarmableInterface?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This is to make sure the warmup method is there. I can remove the interface and just make a warmup method in this interface. Another thing is I can remove the interface and not have the warm up method here and the cache warmer can just assume the method exists.

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 we should remove it - the warmer knows exactly what instances it’s workijg with and therefore what methods exist. What’s the purpose of this interface normally?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The warmer knows about the collection and this interface. It does not know about the actual instance. I can have it do an instance check and on what is returned from the collection.

// If the file does not exist then just skip past this entry point.
if (!file_exists($fullPath)) {
continue;
}
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 logic shouldn’t be necessary. We can just call warmUp() - that method is already written to not fail if this file is missing

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I will remove this.

<argument key="$directory">%kernel.cache_dir%/encore/</argument>
</service>

<service id="webpack_encore.cache" class="Symfony\Component\Cache\Adapter\PhpArrayAdapter">
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 we’ll need a new config option to opt into this behavior. If that config option is false, I think we calcium’s just remove this service in the Extension class. Then we just need the on-invalid behavior on the setCache calls to be correct so Symfony just skips the call instead of an error

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Do you know what you want the config option to be named?

Copy link
Member

Choose a reason for hiding this comment

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

Probably could be cache: true on the “root” level of the bundle config. IMO, no need to make it possible to disable/enable cache independently for each build - someone can ask for that later. Just one global option.

@@ -9,6 +9,9 @@

<service id="webpack_encore.entrypoint_lookup" class="Symfony\WebpackEncoreBundle\Asset\EntrypointLookup">
<argument /> <!-- entrypoints.json path -->
<call method="setCache">
<argument key="$cache" type="service" id="webpack_encore.cache" />
Copy link
Member

Choose a reason for hiding this comment

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

Remove the $key here - the whole "binding by named arguments" thing is really something that should only be used in application land. In a bundle, we should avoid this and pass arguments by order - it'll avoid a minor performance impact when rebuilding the container.

@codayblue
Copy link
Contributor Author

@weaverryan Is there anything else needed for this PR? Are we waiting on another PR?

@weaverryan
Copy link
Member

Thank you @codayblue and @nicolas-grekas - this was a bit more complex than I gave it credit for initially :)

@weaverryan weaverryan merged commit c165e64 into symfony:master Mar 1, 2019
weaverryan added a commit that referenced this pull request Mar 1, 2019
…as-grekas, weaverryan)

This PR was merged into the master branch.

Discussion
----------

Created cached entrypoint look up class

- Created the CacheEntrypointLookup
- modified the EntrypointLookup to have a protected method instead of private to easily just extend it
- Created the cache compiler pass that reads the file and will save it to the symfony cache with the service construction
- Created a config option to enable the cache class and compiler pass
- Enabled the compiler pass in the bundle
- Updated tests

This will resolve issue #3

Commits
-------

c165e64 removing extra use statement
614964e Add cache flag
465aae2 Adjusted code to pass tests and updated tests
badc55d -
243c87f Cache Entry Point Files
@codayblue codayblue deleted the 3-cached-entrypoint branch March 1, 2019 16:51
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants