Chapters
23 Chapters
|
2:20:28
|
Login to bookmark this video
-
Course Code
Subscribe to download the code!
Subscribe to download the code!
-
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.
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!
This tutorial also works great for Symfony 6!
What PHP libraries does this tutorial use?
// composer.json
{
"require": {
"php": "^7.3.0 || ^8.0.0",
"ext-ctype": "*",
"ext-iconv": "*",
"composer/package-versions-deprecated": "^1.11", // 1.11.99
"knplabs/knp-markdown-bundle": "^1.8", // 1.9.0
"sensio/framework-extra-bundle": "^6.0", // v6.2.1
"sentry/sentry-symfony": "^4.0", // 4.0.3
"symfony/asset": "5.0.*", // v5.0.11
"symfony/console": "5.0.*", // v5.0.11
"symfony/debug-bundle": "5.0.*", // v5.0.11
"symfony/dotenv": "5.0.*", // v5.0.11
"symfony/flex": "^1.3.1", // v1.21.6
"symfony/framework-bundle": "5.0.*", // v5.0.11
"symfony/monolog-bundle": "^3.0", // v3.6.0
"symfony/profiler-pack": "*", // v1.0.5
"symfony/routing": "5.1.*", // v5.1.11
"symfony/twig-pack": "^1.0", // v1.0.1
"symfony/var-dumper": "5.0.*", // v5.0.11
"symfony/webpack-encore-bundle": "^1.7", // v1.8.0
"symfony/yaml": "5.0.*" // v5.0.11
},
"require-dev": {
"symfony/maker-bundle": "^1.15", // v1.23.0
"symfony/profiler-pack": "^1.0" // v1.0.5
}
}
34 Comments
What exactly is the difference between using this caching service and the default caching that happens? (The cache you clear with php bin/console cache:clear).
Hey Dirk
REALLY excellent question actually - it can be a bit confusing. Here's what's going on:
A) When you first load Symfony (let's assume that the var/cache directory is empty), Symfony reads a lot of YAML files (for services and routing, for example) and does processing on those. It then writes some cache files to var/cache so that it doesn't need to do that on every request. This type of cache is (more or less) not something you can even disable or that you need to think about (except that you need to clear it when you are deploying). In dev mode, on each page refresh, Symfony checks the last modified times of all those YAML files (or XML, the format doesn't matter) and if anything has changed, it automatically rebuilds all that stuff in the var/cache directory. This type of cache is really meant to be invisible.
B) Completely independent of all of that, Symfony provides a cache service, which YOU can use to store anything you want. By default, this also stores in var/cache (in a pools sub-directory) but that cache is really a different system. If you re-configured the cache.app service to store in apcu (like we do), you will still see that var/cache is full all the stuff from the first system (A) above (but not cache_pools).
So... that's the idea - 2 totally different systems and you can almost pretend like (A) doesn't exist ;). 2 more things I'll say:
1) If you run cache:clear, it will NOT clear the cache from the "cache service" - you would use
cache:pool:clearfor that - you could (for example) clear the "cache.app" pool.2) There is actually another cache service in the container called cache.system. This is part of the caching system I describe in (B). But, it's actually used internally to store the results of YAML or annotation parsing. There are just a few things that can't be cached when you first load your page - and which must be loaded (and cached) at runtime. The cache.system service handles this (e.g validation rules).
Let me know if this helps!
Cheers!
Hi Ryan,
Thanks for your explanation. The way the cache service (or component) as described in B work is understandable. The 'invisible'/default cache is harder to understand. I cannot find documentation about it and I have some doubts about how it *really* works. Even though I could probably ignore it, knowing what it does makes it easier to decide in which situation you would need the other cache service (B) or just stick to the default caching. For example, how does the default caching behave in prod mode? It seems to me that the cached files are not only depending on the config/YAML files (and changes therein). When changing a template for example, in prod, you will not see the changes unless you clear the cache. Ultimately, I am trying to understand when to use the "extra" caching service/component and when to simply rely on the default caching.
I hope my explanation was clear. It can be confusing when using the same term for two different thing ;)
Cheers!
Hey Dirk!
I hear yea - that lower-level cache is very powerful, but mystical ;). I can say a few things:
A) That lower-level cache is the framework caching anything and everything it can that will NEVER need to change after deploy. This is, in reality, a lot of stuff: all of the YAML files (services, bundle config, routing, etc), Doctrine annotations, validation metadata, Twig templates: these are all "code" that you can read once, cache into the fastest-possible format, and re-use forever until the next deploy. In the dev environment, Symfony intelligently "notices" when you've changed one of these files and automatically rebuilds that cache. But you're 100% correct that in the prod environment (for speed), once that cache is set, it uses it forever (until you cache:clear, usually on your next deploy).
B) The best answer to when to use extra caching is probably best answered by profiling once your live - e.g with https://symfonycasts.com/sc...
But another way to answer that is this: the framework is *massively* optimized for everything that it does. What's going to slow down your app is *your* code. So, you really want to be focusing on the stuff that *you* do that's slow. Maybe you're making an external HTTP request on a popular page - that will slow down that page big time. Or maybe you're loading 500 records from the database on a page - that would also slow things down. And then, once you've identified something that you think could be cached (again, Blackfire is the best tool for this because it will tell you for certain when you have a problem, instead of trying to guess where a problem might be), the next challenge is *doing* that cache. If what you're caching is highly dynamic and would need to be invalidated frequently, then it makes it harder to cache that correctly from a "complexity" standpoint. Sometimes we won't cache something that *could* be cached (but isn't that painful) because the logic to correctly invalidate it when it needs to be invalidated would be super ugly :).
Let me know if that helps! Each component in Symfony handles storing its own stuff into that "low level" cache, which is why I can't point you straight to one spot to see it all. The two most important caches, however, are probably the container and routing cache, but there are many more.
Cheers!
At 2:40 he added 'use' after the function. I dont understand that. Is it maybe explained in the PHP beginner course?
Hey Farry7,
That
usekeyword is used for telling PHP compiler that we want to have access to any variables we declare on it on our anonymous function. I believe we do not talk about it in the PHP beginner course but you can read a bit more about it here https://stackoverflow.com/a/10304027/2088447I hope it helps. Cheers!
Hi, after "If we refresh now and check the toolbar... yea! The key changed, it was a cache "miss" and the new markdown was rendered i went back removing the asterisks around "thoughts" and refresh: i expected to hint the previous cached item and see in the profiler:
but any time I switch back and forth (with or without asterisks) i get:
Is that correct? Thank you for your attention
Hey @Claudio-B,
If it's already in the cache - you will need to clear the cache, otherwise it will constantly hit it.
Cheers!
Hi Victor, the problem is the opposite: it seems not hitting the previous cached element.
Changing the text, put / remove asterisks, both are cached already and i would expect
Cache Calls 5
Total time xxx ms
Cache hits 5 / 5 (100%)
Cache writes 0
as i get if i do not change asterizks.
Hey @Claudio-B ,
It still might be due to some cache, I think clearing cache and clearing cache pools may fix the problem.
Cheers!
Thank you Victor, was my problem reading profiler info: changing the text back and forth make the cache numbers into the profiler to change, but the one from app get always hitted (sorry for my "maccheroni" english")
Ciao!
Hey @Claudio-B,
Ah, no problem! Glad you figured that out and everything now works as expected ;)
Cheers!
Hi and thanks for the great tutorial! Minor question.. I downloaded the code and am running it on PHP 7.4. I'm seeing this in the cache folder:
```
$ ls /var/cache/
cups/ libvirt/ private/
```
A little different from what you see in the tutorial. Where can I learn more about what folders I'll see here by default and why it is different from your version in the tutorial? Thank you.
Hey Cheshire Y.
Symfony stores a few files inside the
var/cache/{env}directory after the bootrstrap process. I'm not sure if there are docs about each folder but you can learn more abouth caching on Symfony here https://symfony.com/doc/current/components/cache.htmlOh, also some bundles can cache stuff and may use their own directory (inside the cache folder), so, it's normal to see other folders in there.
Cheers!
After this chapter i have one question... when should i really use cache?
Hey Jakub!
Very fair question :). The downside of using cache is... just added code and complexity... so you don't want to run around and add it everywhere. In most cases, you want to identify that some "code path" is truly a performance problem and *then* add caching. My favorite tool to do this is Blackfire: I can run this on production, identify what parts of my code are *actually* slow, and then consider adding caching to those.
More generally speaking, the places where we tend to use cache relate to complex (or frequent) database calls, network requests (like if we make an API request to a 3rd party serve in order to load a page) and certain filesystem operations. But again, ideally you let a tool like Blackfire guide you to find these things :).
Cheers!
there
PHP: Version 8
Symfony: Version 5.2 with API platform
I am trying to store Doctrine entity object to cache.
When next time I retrieve same object from cache, doctrine consider that object as new object and try to persist it.
Can u help me to solve this issue ?
Thanks
Hey Nirav R.
I'm afraid you cannot do that, Doctrine needs to instantiate objects for you, so it doesn't get confused about if it's a new or old object. What you can do is to store the object's id on the database, and then, query for it
Cheers!
MolloKhan
I am adding object to cache bcz. I not want to execute that query again.
In that case, you can get the UnitOfWork out of the
EntityManagerand add the object manually to the identityMap$entityManager->getUnitOfWork()->addToIdentityMap($object);Cheers!
I tried that but it gives error like...
`
{
"type": "https://tools.ietf.org/html/rfc2616#section-10",
"title": "An error occurred",
"detail": "Warning: Undefined array key \"000000005fdb2782000000003494e2a0\"",
"trace": [
`
Could someone explain this syntax to me? I don't understand what this bit does...
'markdown_'.md5($questionText),and the whole line seems a bit hinky, to me. Someone's already asked about the use statement, so I'm fine with that.
`$parsedQuestionText = $cache->get('markdown_'.md5($questionText), function() use ($questionText, $markdownParser) {
Hey somecallmetim27,
I'd recommend you to rewatch this video again. The md5() is a core PHP function, that generates a specific hash, i.e. an exactly 32-length string with some random chars. But every time you generate hash of the same string - it will give you the exact same hash. You may want to read the docs about this function here: https://www.php.net/manual/...
So in short, we create a cache key with that "'markdown_'.md5($questionText)" and then if the Symfony Cache component has a value for this key and that value isn't expired - it just returns the value. If there're no value or that value is expired - Cache component will generate a new value calling that anonymous function passed as the 2nd argument to that get() method. That anonymous function main job is to generate the proper value for the given key and return it, then Cache component will write it into cache and return the cached value on the next call. Please, also read the docs about PHP anonymous functions here: https://www.php.net/manual/... - it will explain how it works and how to pass some extra arguments in it - we do this via "use ()" as you can already guess :)
I hope this helps!
Cheers!
Hey Ryan!
I was wondering how to get an individual cache key if we want to fetch all data (like $repository->findAll()) from the database? Do I have to make the database query before and get all the needed data then make the md5($result)? You used hard coded content here, what about getting a new key when the content of the fetched data changed?
I hope you get this, Cheerz!
Hey Przemysław S.
That's a good question. Invalidating cache is one of the hardest thing to do in programming because is difficult to know when to refresh the content. The easiest way is just to add an expire date to the cache item
Another approach is to specifically invalidate a cache item on demand. For example, whenever you add a new record, then you refresh the cache
And the last approach I can think of is to just count the records and use it as part of the cache key. For example, you can name a key as "all_users_1536", the
1536part is the total number of users in your table. The downside is that you'll do a count query all the timeIf you get a better idea, let me know! Cheers!
Thank you for responding!
I found a solution, just invalidating the tagged cache key by using TagAwareCacheInterface after adding/ editing an article. Not sure if this is the best solution, just found this working for me.
Cheers!
Hey Przemysław S.
Nice, that's a good solution too. BTW, did you see the latest news about caching? https://symfony.com/blog/ne...
Hi Ryan,
when I implements the CacheInterface It removes my questionText.
my code is:-
[
`public function show($slug, MarkdownParserInterface $markdownParser, CacheInterface $cache)
]
I cant understand why this is happening.
My PHP version is 7.4.4 and it didn't work. For me that was resolved by upgrading to 8.0.5 but now I see 139 deprecation alerts in toolbar..
Hey Andrii,
Difficult to say what was wrong on 7.4.4 for you without seeing the actual error, but yeah, this project should work on 8.0 too. Well, I believe most deprecations are not PHP 8 related but Symfony version related. And since it's a learning project, you can completely ignore them, they are not too important for learning purposes. But if you're talking about your personal project - yeah, it would be a good idea to start fixing those deprecations before your next major Symfony upgrade.
Cheers!
I'm having the same issue running PHP 7.3.22. What version did you upgrade to?
Hmm, your code looks fine to me, the only thing I can think about is that you may have imported the wrong CacheInterface. Double check that you imported this one
Symfony\Contracts\Cache\CacheInterface;Hi, you've hinted (autowiring) the markdown interface, but didn't it need to be a class to actually have methods? Thanks
Hey Joao P.
When you type for an interface Symfony will give you an instance of a class that implements such interface. It works that way by default taking into account that you only have one implementation of the interface. If you have more than one, then you'll have to choose which instance you want through configuration
Cheers!
"Houston: no signs of life"
Start the conversation!