Chapters
19 Chapters
|
2:02:43
|
Login to bookmark this video
-
Course Code
Subscribe to download the code!Compatible PHP versions: ^7.1.3
Subscribe to download the code!Compatible PHP versions: ^7.1.3
-
This Video
Subscribe to download the video!
Subscribe to download the video!
-
Subtitles
Subscribe to download the subtitles!
Subscribe to download the subtitles!
-
Course Script
Subscribe to download the script!
Subscribe to download the script!
03.
The Cache Service
Scroll down to the script below, click on any sentence (including terminal blocks) to jump to that spot in the video!
Subscribe to jump to this part in the video!
What PHP libraries does this tutorial use?
// composer.json
{
"require": {
"php": "^7.1.3",
"ext-iconv": "*",
"knplabs/knp-markdown-bundle": "^1.7", // 1.7.0
"nexylan/slack-bundle": "^2.0,<2.2.0", // v2.0.0
"php-http/guzzle6-adapter": "^1.1", // v1.1.1
"sensio/framework-extra-bundle": "^5.1", // v5.1.4
"symfony/asset": "^4.0", // v4.0.4
"symfony/console": "^4.0", // v4.0.14
"symfony/flex": "^1.0", // v1.21.6
"symfony/framework-bundle": "^4.0", // v4.0.14
"symfony/lts": "^4@dev", // dev-master
"symfony/twig-bundle": "^4.0", // v4.0.4
"symfony/web-server-bundle": "^4.0", // v4.0.4
"symfony/yaml": "^4.0" // v4.0.14
},
"require-dev": {
"easycorp/easy-log-handler": "^1.0.2", // v1.0.4
"symfony/debug-bundle": "^3.3|^4.0", // v4.0.4
"symfony/dotenv": "^4.0", // v4.0.14
"symfony/maker-bundle": "^1.0", // v1.0.2
"symfony/monolog-bundle": "^3.0", // v3.1.2
"symfony/phpunit-bridge": "^3.3|^4.0", // v4.0.4
"symfony/stopwatch": "^3.3|^4.0", // v4.0.4
"symfony/var-dumper": "^3.3|^4.0", // v4.0.4
"symfony/web-profiler-bundle": "^3.3|^4.0" // v4.0.4
}
}
32 Comments
Hello and sorry for the doubts I have, can be very newbie although I hope not.
1 - What is the difference between the <b>"isHit", "has" and "contains"</b> methods in the Cache Adapters?
2 - When we are talking about a <b>Cache System or Filesystem Adapter</b> we are talking about what is being stored in the <b>physical disk</b> , right? That's why I don't understand why it says in the <b>Symfony</b> documentation that you have to be careful with the memory, that also says in <b>Php Array Cache Adapter and Php Files Cache Adapter</b>, which says it uses <b>Opcache</b>, but in reality we are giving it an directory on the disk, please could you explain this. Or I'm confusing myself with the term "FileSystem."
3- In the QueryBuilder from Doctrine I see that we have the options of being able to use Cache with the methods:
Are these methods necessary? I mean this in <b>production</b> automatically creates a cache, and it is not necessary to use those methods, unless we want to change the Cache Adapter, right?
Sorry for my english === null
Hey Jose carlos C.!
Excellent questions!
Where are you seeing "has" and "contains()"? I know of a "has()" method in the cache adapters, so I CAN explain that at least. Basically, the PHP Fig created 2 separate caching interfaces - PSR-6 (which we're using here) and PSR-16. Both are interfaces that a cache object can have, and Symfony supports both of these. In this chapter, we are fetching a cache object that implements the more complex (but more powerful) PSR-6 interface. In this system, you need to get the item first and then call isHit() on the "CacheItem" to see if it's actually in the cache. But, in Symfony 4.1, you can type-hint
Psr\SimpleCache\CacheInterfaceand receive a cache object that implements the simpler PSR-16 interface. With this object, there is no idea of a "CacheItem" - you just sayif ($cache->has('cache_my_key'))or$cache->get('cache_my_key')to get the cached value (or null if it's not cached).Let me know if that answers your question - I'm not sure about contains(), so I could be missing something :).
Hmm. Could you point me to where you're reading this in the docs? The quick answer (and I'm happy to continue this conversation and elaborate further!) is that there are 2 caches in Symfony - their service ids are:
cache.system: An internal cache object used by Symfony for caching things like configuration. It IS ok (in all situations) for this to be stored in the filesystem and, by default, it does that, but tries to use some fancy "Php Array Cache Adapter" and Opcache tricks to do this. Even I don't know a ton about this - but these adapters are just fancy, super-fast ways to save to the filesystem - faster than just the FilesystemAdapter
cache.app: THIS is the object that we're supposed to use to store things at runtime - it's what we're getting in this chapter via autowiring. By default, this also stores on the filesystem via the FilesystemAdapter. That's ok... but in a perfect world, you would change this to something like APCu or (better) Redis. This is for 2 reasons. First, by changing to something that is not the filesystem, when you deploy, you don't need to worry about your var/cache directory being writable - because the cache is being stored somewhere else. All the stuff in
cache.systemis pre-stored when you run thebin/console cache:warmupcommand on deploy. So basically, during deploy, your var/cache directory needs to be writable, but once the deploy actually finishes, it does not need to be writable. This makes your app much easier to work with on almost all hosting platforms because, for example, you don't need to try to make your var/cache directory writable by your web server user.The
->useQueryCache(true)I believe is redundant because it's already activated via your prod config - https://github.com/symfony/recipes/blob/230169dc8a6ab89be7473fbb6a823f2336caa72b/doctrine/doctrine-bundle/1.6/config/packages/prod/doctrine.yaml#L7. That just defines the adapter that should be used, but I believe that this setting is on by default.The useResultCache() IS necessary... IF you want to use this feature (I personally don't). The "query" cache is easy - this basically tells Doctrine to query the transformation from DQL to SQL - something that never changes, and so, of course, cache it! But the "result cache" tells Doctrine to query the results of a query... and if you turn it on, it means that you need to worry about getting "out of date" results or invalidating the cache. So, you need to opt into it.
But in both cases, what you're doing inside of the QueryBuilder is simply saying "yes I want to cache" - but which adapters should be used is stored in your doctrine.yaml file - and you probably won't need to change those, because they're good defaults (they say to use cache.app and cache.system).
Let me know if that makes sense! If you still have questions, let me know - these were big questions :).
Cheers!
Hello weaverryan
1- Perfect, all very clear. The method "contains" seems that I invented it, lol.
2- I hadn't seen it anywhere, I thought that "FilesystemCache" was like the "Filesystem" component, that wrote it on the disk directly without influencing memory (I think I made a mess with so many adapters, I have to digest it now little by little), and I also thought it was faster than memory and database. But now seeing your answer and reading more information I have it more clear, I mean that for example I had created a service "Menu" that saved the Main menu and Footer menu of a website, of course this Entity uses the Nested system with Doctrine Extensions, which makes it slower. In this example I give you, I had done it very simple:
But now watching your video and your aswer I should have used Cache\Adapter\AdapterInterface, but to make it more perfect I'm going to use the APCu Adapter, what do you think?
3 - Perfect, all very clear.
And really thank you very much for your answer and for your great videos, I'm learning a lot.
Hey Charles!
Excellent! It sounds like you have a much better handle on things now :). The great thing about the cache system is that you get that cache.app service (the one that AdapterInterface gives you) automatically - and then you can just choose to use something like apc or redis for more performance (btw, redis is useful if you have multiple servers, as the servers will share the cache instead of all maintaining their own - both apcu and filesystem will mean one cache per server).
So, yes, the advantage to using AdapterInterface instead of instantiating the FilesystemAdapter is that you’re able to use the pre-configured service - no need to create it yourself. And also, you can use this in 10 places, then with one line of config, change to use apcu instead of the filesystem. The official cache docs aren’t the best (it’s on my list) and I think it’s easy to find the “component” documentation (the docs for using the cache component outside of the Symfony framework), and these show how you can manually instantiate the adapter, which is not necessary in the framework.
Phew! Anyways, let me know how it all works!
Cheers!
Thank you very much.
Currently symfony recommend using Symfony\Contracts\Cache\CacheInterface with 'get' methods with callback rather than AdaperInterface, will this docs be updated to avoid confusions and keeping it to the PSR standard?
Hey @Piotr!
You're absolutely right :). We show the new way in the new Symfony 5 tutorial. I especially like the DX of the new CacheInterface - much nicer!
Cheers!
It looks like officially symfony says to use Symfony\Contracts\Cache\CacheInterface with callbacks instead of using adapters. Will this cast be updated?
Hello, I have about 1k user contacts. When I use cache it's reduces the loading time when I want to display the user contact, however, I seems to lose data. What I mean by that is that let's say I had the column name, surname and telephone number. I'll have every data except ones relating to telephone number. Before the data was cached I had everything
Hey @Joel
It seems to me that you are caching your user data too early, probably before setting the telephone data on it. I would recommend you to invalidate your cache every time the user data changes.
Cheers!
Hang on. How can they be exactly the same objects with the same ID alias cache.app yet PHPStorm wont complete the SAME methods. You're right. It is VERY confusing. And where are AdapterInterface implementing cache objects/classes defined? How do I navigate to them? Why do I feel we should be calling something to get the helper object using "cache.app" and not an arbitrarily picked interface definition returned from console debug:autowiring?
Hey Maxii,
Haha, those are just aliases to the same service, you can create as many aliases as you want! But the idea behind of these 2 is simple, one of them is from Symfony and another one from PSR standard. As you may know Symfony implemented PSR cache standard after it was released, but before Symfony already had their own implementation and interface. So, as outcome we have two aliases, one low level from the PSR and another one from Symfony that actually extends that PSR interface. You can see it in the code :) https://github.com/symfony/...
I hope it makes sense for you now!
Cheers!
Hello Victor,
I have this error message with intelephense in VSCode, but there is no error with the project's in Browser.
And as you sad, Symfony extends PSR interface.
Do you think that is a bug of intelephense ?
Error at the $item in the line :
$this->cache->save($item);`<br />Expected type 'Psr\Cache\CacheItemInterface'. Found 'Symfony\Component\Cache\CacheItem'. Intelepense(1006) [38,32]<a href="https://imgshare.io/image/J6ZFl">https://imgshare.io/image/J6ZFl</a>
Hey LE Xuan Hung,
Looks like so :) You can see it by yourself in the code: "Symfony\Component\Cache\CacheItem" implements "Symfony\Contracts\Cache\CacheItemInterface" which in turn extends "Psr\Cache\CacheItemInterface". I'm looking at the final code in master in https://github.com/symfony/... . So, instance of CacheItem should be valid for "Psr\Cache\CacheItemInterface" typehint
Cheers!
"Thanks to the 7 bundles installed in our app" : at this stage there are 8...
Wooops! Maybe Ryan didn't count the framework bundle :p
The question is:
What is better performance? Creating an md5 hash each time to check the integrity of the content or rely on the "updated_ad" field in the database, combining key with date string?
Or better:
If you have to retrieve the contents of the article from the database (already preprocessed by some wysiwyg keyboard) there's no sense caching it at all, because the longest procedure in that process it's the actual loading from the database.
Hey Anton!
Cool question :). I'll answer in reverse :p
> If you have to retrieve the contents of the article from the database (already preprocessed by some wysiwyg keyboard) there's no sense caching it at all, because the longest procedure in that process it's the actual loading from the database.
Yep! That's really a form of caching - "eager" caching in a sense. In this model, each time the "content" updated, you would have some code to effectively "build" the cache so that it never needs to be built at runtime. For the sake of argument, you could also do the same thing but actually store the processed content in a real cache: i.e. on update of the "content", process through Markdown and store the HTML in a "cache". Then, at runtime, the cached HTML would always be available. Performance differences would probably be negligible - I'm just making a conceptual point :).
> What is better performance? Creating an md5 hash each time to check the integrity of the content or rely on the "updated_ad" field in the database, combining key with date string?
I'm not sure! Well, what I mean is, this is probably one of those situations where they are both good... and picking one and testing it under real conditions (using Blackfire) is probably best. It's like that both are *easily* good enough - because both are ultimately using caching. But, in theory, in general, when you have a "validation caching" situation like this (where you need to check to see if some cache is valid before returning it), the general strategy is to "determine if the cache is valid with as little work as possible". In that model, you could make a query for *just* the "updated_at" field from the database (and no other data) then use that to determine if the cache is valid. If it is, return it. If it's not, make a second query for *all* the data, process it, and store it in cache. But... this sounds like possible premature optimization ;).
Cheers!
Hello,
I have a question about the meaning of Pools.
In the PSR 6 documentation https://www.php-fig.org/psr..., they said: The Pool represents a collection of items in a caching system
And in this chapter you said : Pools are different cache systems.
Is there any differences between this two definitions ?
Hey Mehdi,
Actually, both are valid. Really, every "pool" represent a collection of items in a caching system. And every pool can be different cache system: one of them might represent collection in Redis cache, another might represent collection in filesystem cache, etc.
I hope this clearer for you now.
Cheers!
I cant find the answer to this anywhere. Is caching enabled by default in all symfony projects?
Hey Tom
As far as I know, Symfony caches a lot of things required for its internal functionality (Like compiling the container), but it won't cache anything for you, you have to explicitly cache your things.
Does it make sense to cache every article content even if the Markdown Bundle is not used?
By example:
A Blog with 100.000 Articles in a Database
Caching each article would save us maybe a query, but it will use disc space.
Whats your opinion of when to use the cache system? Only on "intense" data operations/modifications or if needed also on "normal" querys (even if there are thousands of articles to potentially cache?)?
Hey Mike,
Good question! And the answer is simple - it depends! Depends on your server, website load, etc. Moreover, there're many cache types: you can use filesystem cache, Redis, Memcached, etc. and it depends on your case what to cache and what tools to use for it. My advice is do not worry about cache too much at the development stage. Most probably you won't need the cache and your website could handle all the requests perfectly. Also, you can use such tools as Blackfire.io to find bottle neck in your project, and when you found it - then you can think about caching things to speed up the website. But fairly speaking SQL databases could handle 100k rows without any problems, especially if you use indexes on those fields that are used in queries, like slug field if you search articles by slug, etc.
In short, if you release a new website and it's not too popular in the beginning - don't use cache, just monitor it, see how things go, and if you see some potential problems at rush hours when the load on it is max - start think about how to solve it, but don't overcomplicate things during development too early :)
P.S. You can also enable caching for Doctrine queries in production, so you don't need to make up a custom caching, see metadata_cache_driver, query_cache_driver, and result_cache_driver in doctrine.orm config path.
Cheers!
Hey there :) I have a quick question regarding the caching system and its use cases. I have a project similar to the spacebar where people can leave comments on articles that I want to be rendered through markdown just like the articles in this course. My project can potentially have thousands of comments and rendering even, lets say, 20 per page will slow down the page to a crawl, so I thought about caching. I am not sure if this would be a good solution, or if it would be better to save a parsed version to the database for later use in a temlate.
Also, I want my comments to be searchable, but I don't want any tags or syntax in it. Sorry if this sounds a bit confusing, ill try to give an example. Lets say a user searches for "# Something", I dont want any results to be comments that happen to use the markdown heading syntax. So I thought about saving a version to the database with all the markup strippen, along the original comment and the one that is parsed to html. But this seems also a bit silly? What would be the best solution to this problem?
Kind regards :)
Hey Tobias I.!
About your first question (caching). If you already know that rendering all those comments are slowing your page, then you can cache the markdown result using the "Symfony Cache" component (or any other caching system you like), the only downside I can see is when a user updates a comment, but probably you can add some extra logic to restore the cache of that specific comment. Another option might be to load the comments in an async way, i.e. via AJAX call
And about your second question there are many ways to provide a search functionality to your users, the technology we use here in SymfonyCasts is called "ElasticSearch", it's a great tool for indexing and searching data but it can be complex to implement. In your case you may not want to do that kind of investment (or maybe you do?) so probably storing both versions or your data might be easier. Or probably you can only store the raw version of the comment and then cache the rendered version, give it a try and check what fits better to your application.
Cheers!
Hi, first of all huge thank you for a great tutorial.
To make a long story short:
Imagine I have my application made of modules and each separte module requires a different set of configuration files ordered within the same way as the within main application. Let's say:
...
src
----Module1
--------config
------------packages
----------------dev
----------------prod
----------------test
------------routes
(etc.)
--------controller
----Module2
(etc.)
...
Now. Through my application config files I can easily call individual module config. For example in src/config/services.yaml:
imports:
- { resource: ../src/Module1/config/services.yaml }
- { resource: ../src/Module2/config/services.yaml }
...
As you can see I am forced to import all possible configurations, (no matter which module controller action is fired) because there is not
implemented any dynamic way to distinguish configs to load depending on engaged module. Briefly if the request trigers some module1 controller's action, I need to load only configs from src/module/config directory.
My question is: how I can fix this.
Thanks in advance
Hey Constantin,
Hm, so if I understand you right - you want dynamically import configuration depending on what module is triggered. Then the question is how would you know what exactly module is triggered and is NOT triggered in advance? :) If you can programmatically determine when to load this or that config file - you can do it. For example, in Symfony 4 there's src/AppKernel.php file that loads configs files and routes, so you can write your tricky rules there to dynamically include configs.
I hope this helps.
Cheers!
When I uncomment app: cache.adapter.apcu
- it gives an error
"(1/1) CacheException
APCu is not enabled"
- I install brew apcupsd successfully. Is this what he mean install Apcu Php Extension?
Please help!!!!
Hey Marc,
Not at all, apcupsd is APC UPS Daemon, see their website: http://www.apcupsd.org/ - that sounds like something different . Try to google how to install APCu for your PHP version on your OS. I see you use Mac... Unfortunately, brew deprecates https://github.com/Homebrew... repo, that's why now you can install only php with some enabled-out-of-the-box extensions with brew, but to install more extensions that does not come with php by default you need to use pecl. I suppose now you can do something like "pecl install apcu" to install it on your Mac. If it does not work for you, try to google alternative ways.
Cheers!
Hello!
I got problem undefined MarkdownInterface , how to fix it?
Hey Ahmed,
Hm, what exactly namespace do you have? Or is it just MarkdownInterface? Then, you need to use its namespace in the beginning of your file. The fully qualified class name should be: \Michelf\MarkdownInterface , so if you use this namespace above you probably won't see this error:
PhpStorm can do it for you if you try to re-type this class, choose the correct one from the list of autocompletion and press enter.
Cheers!
"Houston: no signs of life"
Start the conversation!