WEBVTT NOTE Created by CaptionSync from Automatic Sync Technologies www.automaticsync.com 00:00:00.046 --> 00:00:03.016 align:middle So quite a bit. 00:00:04.126 --> 00:00:24.356 align:middle So I'll just do a quick recap on what it is for those that haven't. 00:00:25.456 --> 00:00:28.656 align:middle Ah cache tagging, what that this anyone using that already? 00:00:28.656 --> 00:00:36.436 align:middle Yeah, there's a few, not that many of us with some, uh, there are quite a lot 00:00:36.436 --> 00:00:41.686 align:middle of Symfony applications using it under the hood, you might not actually know it, um, 00:00:42.326 --> 00:00:47.026 align:middle shortly on Memcached versus Redis just uh, for awareness Redis and Redis cluster what it is. 00:00:47.726 --> 00:00:56.186 align:middle Um, and some issues we had over the last year in the Redis adaptor and what ended 00:00:56.186 --> 00:01:03.546 align:middle up with the RedisTagAwareAapter and then we'll do, uh, I'll do a short demo, um, 00:01:03.546 --> 00:01:06.386 align:middle just to show how you can add caching to your application. 00:01:06.886 --> 00:01:07.726 align:middle Nothing fancy there. 00:01:08.086 --> 00:01:14.406 align:middle And then I'll go through some more advanced edge cases to be aware of if you do this and also 00:01:14.406 --> 00:01:17.916 align:middle where the new adapter fits to the old one. 00:01:19.156 --> 00:01:25.576 align:middle Um, so me, uh, working for eZ Systems, uh, been doing that for a long time. 00:01:25.576 --> 00:01:29.596 align:middle Try whenever I have the time to contribute to the Symfony FOS. 00:01:29.716 --> 00:01:31.546 align:middle Uh, especially FOS HTTP cache. 00:01:31.586 --> 00:01:36.566 align:middle Um, well only a Composer, long time ago. 00:01:36.846 --> 00:01:45.566 align:middle Um, PHP-FIG long time ago, Docker Compose, um, eZ is a kind of a global system, small company, 00:01:45.756 --> 00:01:50.506 align:middle 75 people spread around with the community and partners. 00:01:50.506 --> 00:01:57.686 align:middle Uh, beyond that, uh, we make, um, we make a CMS, uh, 00:01:57.686 --> 00:02:00.246 align:middle today called eZ Platform in the past called eZ Publish. 00:02:00.796 --> 00:02:01.686 align:middle You might have heard of it. 00:02:02.206 --> 00:02:06.436 align:middle It's a very extendible, very feature rich, flexible ah it can be used 00:02:06.436 --> 00:02:09.576 align:middle for headless or full, uh, full, uh, use. 00:02:11.326 --> 00:02:16.406 align:middle Uh, we've been on Symfony since 2012 and, uh, we're actually in the middle of uh, um, 00:02:16.406 --> 00:02:21.176 align:middle getting a new version out soon with either, either on 44.4 LTS or 5 or both. 00:02:21.176 --> 00:02:25.366 align:middle We haven't really decided, um, we have commercial flavors 00:02:25.366 --> 00:02:34.236 align:middle of that eZ Platform Enterprise and Commerce, which adds additional features. 00:02:34.376 --> 00:02:39.746 align:middle Um, so in our case, we started to use Symfony cache back in 2007. 00:02:40.436 --> 00:02:43.376 align:middle Uh, we used a Stash if you've ever heard about that. 00:02:44.186 --> 00:02:50.836 align:middle Um, pull that out, put in Symfony cache, uh, to instead use Symfony, uh, 00:02:50.836 --> 00:02:53.826 align:middle sort of the cache tagging there because we wanted 00:02:53.826 --> 00:02:56.466 align:middle to take a different approach to how we clear cache. 00:02:57.356 --> 00:03:01.416 align:middle If you are aware of a Stash, it has something called a hierarchical cache. 00:03:01.926 --> 00:03:06.226 align:middle So we can for instance, in for instance, say I want to delete locations slash 00:03:06.356 --> 00:03:07.976 align:middle and it will delete all locations. 00:03:08.766 --> 00:03:13.666 align:middle Uh, if that's an entity, uh, if you have a blog, you can delete all blogs, 00:03:13.666 --> 00:03:17.666 align:middle but if you have something that goes across all these trees or hierarchies, 00:03:18.186 --> 00:03:21.706 align:middle then becomes a bit more, uh, difficult. 00:03:21.706 --> 00:03:26.286 align:middle And you also end up clearing way too much cache in some cases when this gets complex. 00:03:26.856 --> 00:03:33.776 align:middle So after moving to that, we found some, some issues. 00:03:33.776 --> 00:03:39.466 align:middle So going back to that, and that's all sort of already mentioned, we in the end ah decided 00:03:39.536 --> 00:03:43.016 align:middle to contribute optimized tag adapters for Redis file system. 00:03:43.776 --> 00:03:48.386 align:middle So quick recap on Symfony cache for those that haven't used it yet. 00:03:49.946 --> 00:03:53.446 align:middle Uh, PSR6 compliant cache adaptors plus plus. 00:03:53.786 --> 00:03:57.316 align:middle So it adds additional features on top and needs to be fast. 00:03:57.996 --> 00:04:01.656 align:middle Um, I can sign on that compared to Stash. 00:04:01.656 --> 00:04:03.826 align:middle It's definitely faster. 00:04:04.916 --> 00:04:10.046 align:middle Um, it's also progressively being used all over the place in Symfony itself. 00:04:10.116 --> 00:04:13.466 align:middle So you'll probably see new places being used for that. 00:04:14.536 --> 00:04:18.466 align:middle There are a lot of adopters right now. 00:04:18.926 --> 00:04:23.816 align:middle This is maybe not even complete, but there's APCu a PHP Array 00:04:23.816 --> 00:04:30.656 align:middle in memory chaining several using Doctrine, um, existing adopters from them. 00:04:30.986 --> 00:04:36.426 align:middle There's FileSystem, there's PHP file and PHP Array using Opcache for those cases 00:04:36.426 --> 00:04:38.276 align:middle where you have cache that never changes. 00:04:38.986 --> 00:04:42.026 align:middle So, um, uh, there's that. 00:04:42.896 --> 00:04:47.256 align:middle And then there's a proxy for using other PSR6 caches. 00:04:47.316 --> 00:04:49.386 align:middle There's Redis and there's Memcached. 00:04:50.006 --> 00:04:53.006 align:middle And finally there is something called TagAware. 00:04:53.756 --> 00:04:56.576 align:middle So this is kind of a special kind. 00:04:56.996 --> 00:04:58.976 align:middle This is not a backend per se. 00:04:59.426 --> 00:05:02.196 align:middle This is something that wraps, uh, a backend. 00:05:03.396 --> 00:05:09.556 align:middle And to go a bit into tagging, um, what do we use that for? 00:05:09.556 --> 00:05:14.906 align:middle How do we use it and to try to be generic? 00:05:15.786 --> 00:05:22.006 align:middle You can say, depending on an application, it can be a entity type, it can be a placement somehow. 00:05:22.006 --> 00:05:24.696 align:middle If you have some kind of placement in your system for your entities, 00:05:25.426 --> 00:05:28.046 align:middle variant type in commerce systems maybe. 00:05:28.846 --> 00:05:31.446 align:middle So a lot of different things depending on your system. 00:05:34.466 --> 00:05:39.616 align:middle And where this is relevant is if you have operations in your application 00:05:39.616 --> 00:05:43.036 align:middle that affects, uh, quite a big subset of those. 00:05:43.296 --> 00:05:47.526 align:middle Not the whole thing, but definitely a part like the change 00:05:47.526 --> 00:05:52.806 align:middle to a variation affecting all the variations in that specific kind of variation, for instance. 00:05:53.646 --> 00:05:57.296 align:middle So affecting bulk of entities but not all. 00:05:58.496 --> 00:06:04.096 align:middle So about tagging cache, uh, you can basically directly invalidate on this here. 00:06:04.096 --> 00:06:08.786 align:middle Jumping a bit to an eZ example, we have the key, uh, for eZ content. 00:06:08.916 --> 00:06:13.226 align:middle In our case we have, ah content can be anything, articles, blogs, whatever. 00:06:13.226 --> 00:06:17.786 align:middle So there's ID 66, there's type two. 00:06:18.016 --> 00:06:22.676 align:middle If, um, and that's a article, I think a location 44. 00:06:22.776 --> 00:06:28.796 align:middle So tagging where in the content tree we place this so that if there is an operation 00:06:29.126 --> 00:06:33.856 align:middle on location 44 and everything beneath it, we can say to the cache, 00:06:34.126 --> 00:06:37.766 align:middle please clear this, please clear path, actually 44. 00:06:42.696 --> 00:06:48.026 align:middle So to jump to PHP code and look at how this looks like, actually it's quite simple. 00:06:48.796 --> 00:06:52.146 align:middle There's just one method added to what PSR6 provides. 00:06:52.926 --> 00:06:59.456 align:middle There is invalidate tags but of course in the detail there, it does imply a lot of concepts. 00:06:59.456 --> 00:07:03.856 align:middle It doesn't imply a secondary index, it does imply keeping tracking of this 00:07:04.316 --> 00:07:09.106 align:middle and does imply setting those tags or well if you want. 00:07:10.336 --> 00:07:16.976 align:middle Um, as mentioned it wraps your normal adapter and stores the tags in a separate key 00:07:17.236 --> 00:07:18.646 align:middle which you don't have to care about. 00:07:18.646 --> 00:07:19.806 align:middle It's completely internal. 00:07:21.326 --> 00:07:28.816 align:middle Um, it does, when you look up your key, it will do a separate in parallel lookup for to know 00:07:28.816 --> 00:07:31.276 align:middle which tags you have added to this cache. 00:07:32.696 --> 00:07:39.716 align:middle And after knowing that it will do a second look up or third, I'll show you in a second 00:07:40.376 --> 00:07:42.886 align:middle to to actually know if any of those have been validated. 00:07:43.316 --> 00:07:49.216 align:middle So if we look at actually output from web profiler who will see it first, 00:07:49.266 --> 00:07:53.416 align:middle do one round of lookups, to the backend, it will look up what you asked for. 00:07:54.126 --> 00:07:57.596 align:middle It will look up for which tags is assigned to this. 00:07:58.186 --> 00:08:02.806 align:middle And then it will look up all the tags you have assigned in this case. 00:08:02.806 --> 00:08:05.446 align:middle Um, it's a timestamp. 00:08:05.636 --> 00:08:09.236 align:middle So in this case there's false, there's nothing set yet. 00:08:09.236 --> 00:08:10.696 align:middle I just booted up the application. 00:08:11.276 --> 00:08:15.806 align:middle But if there were, you know, relations, it would return a timestamp. 00:08:15.806 --> 00:08:26.156 align:middle We'll return a bit to the new adapters around this. 00:08:26.316 --> 00:08:28.806 align:middle But first, what about Redis and Redis Cluster? 00:08:29.736 --> 00:08:34.136 align:middle Anyone here uses Redis or maybe specifically Redis Cluster already. 00:08:35.926 --> 00:08:42.376 align:middle Okay. Anyone uses other things like from Netflix or other solutions? 00:08:44.376 --> 00:08:53.596 align:middle Yeah. Um, so starting with Redis, it's not Memcache, it's way more advanced. 00:08:53.596 --> 00:08:55.746 align:middle It has, um, data types. 00:08:56.176 --> 00:08:57.846 align:middle So it has a lot of different data types. 00:08:57.846 --> 00:08:58.916 align:middle It's not just string. 00:08:59.206 --> 00:09:07.546 align:middle There's lists, sets, so list within a key, sorted sets, hashes, bitmaps, hyper log logs, 00:09:07.546 --> 00:09:11.216 align:middle which are not going to try to explain it this evening, streams, 00:09:11.216 --> 00:09:13.606 align:middle which is a complex talk in itself. 00:09:13.606 --> 00:09:16.606 align:middle Uh, there are, um, several talks about it. 00:09:16.686 --> 00:09:19.836 align:middle You can find online if you're interested, quite recent future. 00:09:21.116 --> 00:09:25.386 align:middle And on these data types, there are tons of operations, 00:09:25.736 --> 00:09:29.566 align:middle so it's not just get, set and almost that's it. 00:09:30.426 --> 00:09:34.816 align:middle There is tons more, if we talk about string, there's GET SET, APPEND, 00:09:34.816 --> 00:09:37.896 align:middle BITPOS, Decrement Increment and so on. 00:09:38.006 --> 00:09:41.996 align:middle If it's an ink, no, int value in the string. 00:09:43.576 --> 00:09:49.196 align:middle Um, there's also a generic, um, cluster commands for doing specific things. 00:09:49.276 --> 00:09:50.756 align:middle There's a transactional. 00:09:51.076 --> 00:09:57.116 align:middle There's um, also something called the pipeline for doing several in one call. 00:09:58.506 --> 00:10:04.096 align:middle Uh, and for SET, which is what we use in RedisTagAware there is add to this app. 00:10:04.286 --> 00:10:11.576 align:middle So add a member, remove a member, pop the last member, uh, dif when you have two sets, uh, 00:10:11.576 --> 00:10:16.246 align:middle in do intersections, unions, moving members from one set to another. 00:10:16.246 --> 00:10:19.276 align:middle So there is a lot of advanced operations that you can do on this. 00:10:19.276 --> 00:10:25.676 align:middle You can use this, uh, for something cool in your application without it actually being a cache. 00:10:25.886 --> 00:10:27.616 align:middle It can be a storage. 00:10:28.636 --> 00:10:29.446 align:middle I'll get back to that. 00:10:30.196 --> 00:10:36.896 align:middle Um, and also, um, if you still compare it to Redis and now to Memcached, 00:10:37.606 --> 00:10:42.146 align:middle it allows you to control much more what happens on a lot of different cases. 00:10:42.936 --> 00:10:45.756 align:middle One important one is what happens when it reaches max memory. 00:10:46.206 --> 00:10:53.166 align:middle So when it reaches maximum memory, can ask it if it should randomly clear things, if it should, 00:10:53.166 --> 00:11:02.776 align:middle uh, respect TTL, if it should respect a last, um, rather than, um, 00:11:03.066 --> 00:11:06.996 align:middle recently used object or frequently used object. 00:11:07.366 --> 00:11:12.316 align:middle Uh, and you can choose if that should only be on those that have a TTL 00:11:12.796 --> 00:11:15.966 align:middle or everything, those even that doesn't have it. 00:11:18.616 --> 00:11:26.026 align:middle So onto Redis Cluster, um, this allows you to scale Redis by running several instances. 00:11:26.886 --> 00:11:29.846 align:middle You can also run several on the same server. 00:11:30.276 --> 00:11:34.526 align:middle So if you want to handle more load than you have a lot of spare CPU 00:11:34.526 --> 00:11:36.656 align:middle and memory, you can even do it per server. 00:11:37.656 --> 00:11:45.016 align:middle Uh, what it does for you is coordinate a cache across cache slots, which is within each server 00:11:45.946 --> 00:11:50.416 align:middle and deal with the replication and also figure out who is the master. 00:11:50.536 --> 00:11:55.846 align:middle So if something goes down, it will communicate with the different servers, elect a new master 00:11:55.846 --> 00:12:02.086 align:middle in the background and deal with this itself. 00:12:02.086 --> 00:12:07.406 align:middle Um, thinking back to all the different advanced options I talked about earlier with Redis, 00:12:07.726 --> 00:12:13.546 align:middle there are some limitations when you use a cluster unlike Memcached. 00:12:13.646 --> 00:12:19.156 align:middle But of course, uh, it, it supports everything Memcached supports also in cluster mode. 00:12:20.096 --> 00:12:22.626 align:middle So talking about limitations, you can, for instance, 00:12:22.626 --> 00:12:26.866 align:middle not do a pipeline that would, um, have to go to one server. 00:12:26.866 --> 00:12:33.586 align:middle Uh, in the case of our clients are the one, most of the use, the PHP Redis one. 00:12:34.196 --> 00:12:42.406 align:middle It mainly supports doing several operations at once using simpler commands like MGET and MSET. 00:12:42.496 --> 00:12:49.886 align:middle Um, and to give an example, uh, if you do a rename of a key from one name to another, 00:12:50.506 --> 00:12:56.306 align:middle this might result in the following, uh, exception cross slot keys 00:12:56.306 --> 00:12:58.276 align:middle in request on the hash to the same slot. 00:12:58.746 --> 00:13:01.056 align:middle There is a way around this I'll show you later. 00:13:01.846 --> 00:13:03.736 align:middle But, um, it's things to be aware of. 00:13:03.946 --> 00:13:10.386 align:middle You need to kind of work around those things. 00:13:10.886 --> 00:13:13.966 align:middle Shortly on Memcached versus Redis, whoever uses Memcached. 00:13:15.676 --> 00:13:18.676 align:middle Okay. Where do you used to use it and move to Redis? 00:13:19.106 --> 00:13:19.946 align:middle Yeah, yeah. 00:13:20.326 --> 00:13:22.766 align:middle There is a lot of those, including me. 00:13:24.216 --> 00:13:27.586 align:middle Um, they're there. 00:13:27.586 --> 00:13:29.076 align:middle It's good to know what the difference are 00:13:29.076 --> 00:13:32.316 align:middle because actually Memcached has its strengths also compared to Redis. 00:13:33.266 --> 00:13:36.496 align:middle Um, to, to talk about them here. 00:13:36.496 --> 00:13:38.356 align:middle I already talked about the strengths of Redis. 00:13:38.356 --> 00:13:42.226 align:middle You have the data types, you have the controller eviction, you have persistence, 00:13:42.226 --> 00:13:45.166 align:middle you have the pipeline Lua capabilities. 00:13:45.836 --> 00:13:51.866 align:middle Um, and there is also a bunch of other things, but those are the ones I felt were important. 00:13:52.976 --> 00:13:58.866 align:middle Um, and then when it comes to multi server, uh, you start to see the difference because um, 00:13:59.166 --> 00:14:02.366 align:middle Memcached is by default meant for running on several servers. 00:14:02.876 --> 00:14:05.616 align:middle While Redis you have to introduce a different thing, right? 00:14:05.616 --> 00:14:09.276 align:middle Redis Cluster or other technologies to run it across several servers. 00:14:09.926 --> 00:14:10.746 align:middle So that's one difference. 00:14:11.696 --> 00:14:15.756 align:middle The second one is multithreading Memcached does multithreading especially 00:14:15.756 --> 00:14:18.436 align:middle in the last five, six, seven years. 00:14:19.746 --> 00:14:25.166 align:middle Um, while Redis today, today it's a single threaded. 00:14:25.796 --> 00:14:30.266 align:middle So this means if you want to take, as I mentioned earlier, take care about extra load, 00:14:30.646 --> 00:14:35.256 align:middle uh, and want to take care about and use a lot of CPU, CPU, of course, 00:14:35.606 --> 00:14:43.916 align:middle you would need to use Redis Cluster to spawn additional processes to take advantage of those. 00:14:44.516 --> 00:14:47.696 align:middle Redis 6 does come with some background threading capabilities 00:14:47.776 --> 00:14:50.996 align:middle but it's more for the slow operations. 00:14:52.186 --> 00:14:59.576 align:middle Okay. So having set somehow some uh, some background and some context here. 00:14:59.576 --> 00:15:00.916 align:middle Let's talk about the new adapters. 00:15:02.046 --> 00:15:05.126 align:middle Um, there's two of them. 00:15:05.296 --> 00:15:07.806 align:middle FilesystemTagAware and RedisTagAwareAdapter. 00:15:08.866 --> 00:15:15.076 align:middle What they do on the high level is basically instead of like the TagAwareAdapter, 00:15:15.326 --> 00:15:20.486 align:middle where it does several look-ups, it moves tags to be like a relation. 00:15:21.046 --> 00:15:27.416 align:middle So instead of using the expiry time and looking up that it will store it as a separate thing 00:15:28.436 --> 00:15:34.436 align:middle and not have to do a lookup on it, this means it has a one round trip 00:15:34.926 --> 00:15:39.466 align:middle to the server instead of two. 00:15:40.056 --> 00:15:43.736 align:middle In the case of file system adapter, it uses a file for tags. 00:15:44.356 --> 00:15:49.106 align:middle So kind of like a upended the file every time there's something added, appending to it. 00:15:49.496 --> 00:15:55.916 align:middle If there is invalidation, I think it moves it and then reads it, clear all the keys in it. 00:15:56.416 --> 00:16:05.326 align:middle Uh, in Redis, um, TagAwareAdapter, it was, um, it is using a set, so storing that as a relation 00:16:05.986 --> 00:16:08.136 align:middle and it uses it with out expiring. 00:16:08.606 --> 00:16:15.066 align:middle This is, um, one of the tricky things with this adapter it needs to do that because uh, 00:16:15.066 --> 00:16:21.066 align:middle if the tags are suddenly evicted before the cache itself, if you clear it by the tag 00:16:21.776 --> 00:16:25.006 align:middle and that has been evicted, you basically end up with stale cache. 00:16:26.796 --> 00:16:29.976 align:middle So clear limitation may be I'm not that up there. 00:16:30.696 --> 00:16:37.116 align:middle So all this efforts to try to do one, um, one round trip lookups? 00:16:37.986 --> 00:16:42.266 align:middle Uh, we have cases with customers where they're on Amazon or something else 00:16:42.266 --> 00:16:46.446 align:middle and there are often quite some latency to reach the cache backend 00:16:46.446 --> 00:16:48.416 align:middle if it's Memcached or if it's redis. 00:16:48.926 --> 00:16:50.016 align:middle Uh, it can be the same. 00:16:50.546 --> 00:16:57.206 align:middle What we had from customers was some were in the range of 0.2 to 0.5 milliseconds per lookup. 00:16:58.386 --> 00:17:03.836 align:middle Um, and if you are on a small instance of that Redis uh, 00:17:03.836 --> 00:17:06.476 align:middle or the ElasticCache, it will be even slower. 00:17:07.876 --> 00:17:10.446 align:middle So simple page, no problem. 00:17:10.796 --> 00:17:15.656 align:middle That might attribute to something like five to 20 millisecond of the total, uh, 00:17:15.656 --> 00:17:17.476 align:middle time spent to generate that page. 00:17:18.336 --> 00:17:25.706 align:middle But um, us, me and Yonnie in the front there, um, coming from eZ we make a CMS, 00:17:26.046 --> 00:17:32.566 align:middle there's like complex news papers or something that our customers tries to load, you know, 00:17:32.606 --> 00:17:37.846 align:middle cases like this or worse where there's like thousands of articles or different kinds 00:17:37.846 --> 00:17:39.706 align:middle of content being shown on one page. 00:17:40.336 --> 00:17:45.826 align:middle All of them need some look up to the cache and this starting to attribute to a lot 00:17:45.826 --> 00:17:49.936 align:middle of the load time, uh, unless the page is cached by Varnish. 00:17:50.346 --> 00:17:55.406 align:middle So whenever varnish isn't caching the page just to look ups to Redis 00:17:55.406 --> 00:18:00.016 align:middle or memcached will attribute to up to one second. 00:18:00.016 --> 00:18:06.926 align:middle And that's just with one Redis instance, it was in some cases worse. 00:18:08.076 --> 00:18:12.556 align:middle So to talk briefly on the optimizations that's been done in, 00:18:12.556 --> 00:18:20.466 align:middle in Symfony Cache over the last year, we first figured out that it was using pipeline. 00:18:20.576 --> 00:18:25.936 align:middle So when you move to Redis cluster, it wouldn't have to then do, if you were asking 00:18:25.936 --> 00:18:28.826 align:middle for five things at the same time, it would do five calls. 00:18:28.826 --> 00:18:33.846 align:middle So instead of using MGET, it ended up being a single GET per 00:18:34.076 --> 00:18:35.726 align:middle in a foreach for each and everyone. 00:18:36.456 --> 00:18:39.326 align:middle So back and forth ping pong to the Redis server. 00:18:40.606 --> 00:18:42.466 align:middle So this was fixed. 00:18:44.056 --> 00:18:50.606 align:middle There was also a small thing around having to do versioning of cache in Redis Cluster, this, uh, 00:18:50.606 --> 00:18:53.916 align:middle added additional lookups to know which version the caches is on now. 00:18:54.246 --> 00:18:56.986 align:middle And then if you clear everything, the version will be bumped. 00:18:57.646 --> 00:18:59.106 align:middle And this wasn't needed anymore. 00:18:59.106 --> 00:19:04.276 align:middle When Nicholas did some other, uh, improvements here and there and he fixed this one. 00:19:05.736 --> 00:19:11.456 align:middle He fixed a lot of things, uh, also on the TagAwareAdapter, it's, it's way better now. 00:19:11.596 --> 00:19:13.976 align:middle It does some micro TTL caching. 00:19:13.976 --> 00:19:18.686 align:middle So if you do a lookup on those cache to know the expire time, it doesn't have to look 00:19:18.726 --> 00:19:21.216 align:middle that up every single time during your request. 00:19:21.786 --> 00:19:23.776 align:middle So that's a great improvement as well. 00:19:25.086 --> 00:19:27.906 align:middle Um, but a lot of the improvements you need to deal 00:19:27.906 --> 00:19:30.026 align:middle with is actually going to be on your application. 00:19:30.536 --> 00:19:36.416 align:middle So in our case on the eZ Platform side, we did change this to make sure we could take advantage 00:19:36.416 --> 00:19:38.786 align:middle of looking up several things at the same time. 00:19:39.296 --> 00:19:43.886 align:middle Instead of us having our API where you load one thing at the same time and our, 00:19:43.886 --> 00:19:49.206 align:middle our users are using that, we expose more APIs, to load several things at the same time. 00:19:49.596 --> 00:19:53.716 align:middle We took advantage of it ourself in the cases we could and we started 00:19:53.716 --> 00:19:56.616 align:middle to like encourage our users to use it also. 00:19:57.826 --> 00:20:03.486 align:middle Um, we also then more importantly introduced, um, RedisTagAwareAdapter 00:20:04.806 --> 00:20:07.626 align:middle and maybe what had a biggest impact for us. 00:20:07.626 --> 00:20:13.796 align:middle We introduced application specific in-memory cache, um, kind of discussing Nicholas 00:20:13.796 --> 00:20:16.596 align:middle about trying to build something generic for Symfony around this. 00:20:16.686 --> 00:20:19.646 align:middle But it's, it is application specific. 00:20:19.646 --> 00:20:23.196 align:middle It's only you and your application that knows what can safely be put 00:20:23.196 --> 00:20:26.796 align:middle in memory for a short amount of time. 00:20:26.796 --> 00:20:31.196 align:middle So yeah, it's possible to do it but then be aware of where you can do it. 00:20:31.616 --> 00:20:36.466 align:middle In our case, we do it, um, for maybe our second on metadata lookups, 00:20:36.596 --> 00:20:41.256 align:middle things that doesn't change too often and for anything that is related to the entities, 00:20:41.326 --> 00:20:47.986 align:middle so the content in our case, which can update quite often, we have a very, very short, um, 40, 00:20:47.986 --> 00:20:53.106 align:middle 60 millisecond burst cache just to make sure if there's an inefficient code on top looking 00:20:53.106 --> 00:20:59.626 align:middle up the same data several times, we don't have to ask Redis about that. 00:20:59.746 --> 00:21:07.456 align:middle So to talk about end result example, in our case we had like a page or a dashboard doing um, 00:21:07.746 --> 00:21:15.336 align:middle 17,000 lookups, to Symfony cache, um, and on Redis cluster due to the issues I talked 00:21:15.336 --> 00:21:19.236 align:middle about before, that was up to 40 to 60,000 lookups. 00:21:19.396 --> 00:21:24.076 align:middle So we're talking like around 30 second wait time for, for the user. 00:21:24.076 --> 00:21:26.486 align:middle The editor is sitting there and waiting to load the dashboard. 00:21:26.956 --> 00:21:32.966 align:middle Only 30 seconds was just for Redis and then additional 200 milliseconds, 00:21:32.966 --> 00:21:35.196 align:middle again or something just to do the PHP execution. 00:21:36.276 --> 00:21:44.466 align:middle Um, after all those fixes I talked about, we went down to 63, so today it, 00:21:44.566 --> 00:21:51.666 align:middle it's not even showing up almost on the, on the profiler, on your running in dev mode. 00:21:51.986 --> 00:21:54.176 align:middle So it's, it's really a great improvement. 00:21:54.176 --> 00:21:59.356 align:middle And if we hadn't done it, uh, we wouldn't be able to launch a couple 00:21:59.356 --> 00:22:01.996 align:middle of larger customers stuff we've done in the last half year. 00:22:02.286 --> 00:22:04.146 align:middle So it was definitely necessary. 00:22:06.036 --> 00:22:13.026 align:middle Okay. Um, before I'm going on to edge cases and some things to be aware of, 00:22:13.506 --> 00:22:16.756 align:middle I'll um, briefly do our small demo. 00:22:19.986 --> 00:22:26.656 align:middle I'll need to switch screen. 00:22:28.406 --> 00:22:36.596 align:middle So anyone here tried out Symfony demo or looked at it or used it or, yeah, some nodding. 00:22:37.306 --> 00:22:40.136 align:middle It's a quite simple application. 00:22:40.316 --> 00:22:47.076 align:middle It kind of has blogs and comments on that and some, um, editorial tags 00:22:48.396 --> 00:22:52.816 align:middle and it has a admin backend to let you log in and edit this. 00:22:53.426 --> 00:22:54.976 align:middle And it's all on Symfony, pure Symfony. 00:22:55.526 --> 00:23:03.686 align:middle So, if we, um, if we start the server, 00:23:04.486 --> 00:23:09.546 align:middle make sure that we have cleared cache to make sure that it's correct. 00:23:11.466 --> 00:23:15.716 align:middle And if we, okay. 00:23:17.576 --> 00:23:25.046 align:middle And that's on the right. 00:23:25.876 --> 00:23:30.326 align:middle Yeah. So clearing cache again. 00:23:38.016 --> 00:23:45.556 align:middle So, um, first load now it will basically, you can see that doing a, 00:23:45.556 --> 00:23:47.276 align:middle at the bottom there is, is it readable? 00:23:48.626 --> 00:23:53.996 align:middle No. Yeah, Better? 00:23:54.516 --> 00:23:58.336 align:middle Let's do that here also. 00:24:04.296 --> 00:24:09.396 align:middle Okay. So you see at the bottom there, there's three lookups 00:24:09.396 --> 00:24:12.916 align:middle to the database and 174 lookups to the cache. 00:24:12.916 --> 00:24:19.206 align:middle Actually, it's, if we look at it, it should be a lot of save requests, GET items, save, 00:24:19.206 --> 00:24:20.866 align:middle GET items time, save, save the third. 00:24:21.396 --> 00:24:26.246 align:middle Yeah. And actually it's annotations in Symfony itself doing most of that. 00:24:26.246 --> 00:24:30.646 align:middle So you can see we have 14 look-ups and done by the cache backend. 00:24:31.776 --> 00:24:37.616 align:middle And then if I go back now you can see now it's loading again. 00:24:37.966 --> 00:24:42.036 align:middle There is no database lookups, and now there's 23 lookups to cache. 00:24:42.826 --> 00:24:52.236 align:middle So what did I do to, ah, to demo application, if we have a look in the code here, let's zoom in, 00:24:53.696 --> 00:24:59.386 align:middle you might not see the tree on the side here, but basically in demo there's a BlogController 00:24:59.386 --> 00:25:01.876 align:middle for the front and a BlogController for admin. 00:25:02.666 --> 00:25:03.806 align:middle Is this one readable? 00:25:04.446 --> 00:25:05.986 align:middle Nope Better? 00:25:06.166 --> 00:25:18.436 align:middle Nope. Uh, so what's been done here? 00:25:19.166 --> 00:25:26.226 align:middle Uh, imported a couple of, um, uh, interfaces, uh, 00:25:26.226 --> 00:25:29.776 align:middle ItemInterface specific thing here and then you'll see it later. 00:25:29.776 --> 00:25:34.306 align:middle But TagAwareCacheInterface is the most important thing because now, 00:25:34.396 --> 00:25:43.536 align:middle and this is both from contracts by the way, um, now we can do a change to, to the controllers. 00:25:44.786 --> 00:25:46.206 align:middle We can on the IndexController. 00:25:46.206 --> 00:25:52.016 align:middle We can just type in that we want TagAwareCacheInterface 00:25:52.016 --> 00:25:57.516 align:middle and then get the whole cache adapter being already wrapped in this tag aware stuff. 00:25:57.696 --> 00:25:59.936 align:middle Um, and we can start to use it. 00:26:00.716 --> 00:26:10.006 align:middle Um, so changes to the controller here is then to generate a key that is unique 00:26:10.036 --> 00:26:14.626 align:middle to every request coming in, page index, which page we are on. 00:26:14.926 --> 00:26:16.296 align:middle If it's a tag filter. 00:26:16.296 --> 00:26:20.746 align:middle So the editorial tag shows you in the admin or the demo. 00:26:21.786 --> 00:26:24.666 align:middle Although this needs to be added to the key to make sure we look 00:26:24.666 --> 00:26:26.856 align:middle up what is unique to that, to the page. 00:26:27.216 --> 00:26:30.636 align:middle We check if it is a hit and it's not. 00:26:30.916 --> 00:26:32.116 align:middle So we need to do the loading. 00:26:32.826 --> 00:26:35.366 align:middle Um, this is then code, that was there from before. 00:26:36.016 --> 00:26:42.226 align:middle Um, get the latest post in the end, store it, here, 00:26:43.186 --> 00:26:49.326 align:middle and then what is beyond normal PSR6 is this part, setting the tags. 00:26:49.716 --> 00:26:54.846 align:middle So I put it into a separate function here now to make it a bit more readable, 00:26:55.466 --> 00:27:03.976 align:middle but basically adding one global tag for the list itself and I'll show you why later. 00:27:04.626 --> 00:27:09.076 align:middle And then also for each and every post that is going to be displayed on this page, 00:27:09.736 --> 00:27:12.226 align:middle add the tag with an ID of that post. 00:27:12.626 --> 00:27:17.466 align:middle So return an array of tags and this is being stored together with the cache. 00:27:17.916 --> 00:27:24.376 align:middle And we now have a relation, kind of not a strong one, not a foreign key checked one or anything 00:27:24.376 --> 00:27:26.136 align:middle like that, but we have some kind of a relation. 00:27:28.626 --> 00:27:36.106 align:middle So, but if I now try to edit this post, will it work? 00:27:37.176 --> 00:27:38.966 align:middle So we have the first here. 00:27:39.956 --> 00:27:53.166 align:middle Let's just do something simple, edit, you have the tags there for the editorial part, 00:27:53.976 --> 00:28:01.556 align:middle we saved it and then backend, the edit shows up but not in front end. 00:28:01.926 --> 00:28:04.596 align:middle So I need to do something more. 00:28:04.836 --> 00:28:09.286 align:middle I need to also take care about the admin side of things. 00:28:10.146 --> 00:28:22.706 align:middle So doing that, So to go over what has been changed. 00:28:23.056 --> 00:28:35.566 align:middle If you go to BlogController again and now I need to zoom, yeah, Readable, um, 00:28:37.216 --> 00:28:44.606 align:middle TagAwareCacheInterface, same as before and now index not so important. 00:28:44.606 --> 00:28:46.276 align:middle We don't need to cache things in admin. 00:28:46.866 --> 00:28:57.816 align:middle Maybe. New; when a new Post is created we type in that we want a cache and in this case 00:28:58.296 --> 00:29:03.686 align:middle when there is a valid change, we need to clear the index because the ID is new. 00:29:03.686 --> 00:29:09.536 align:middle If we clear on the ID and the right, um, the right ones won't be cleared. 00:29:09.536 --> 00:29:13.126 align:middle So we cleared the index to being sure that whoever's page this ends 00:29:13.126 --> 00:29:16.706 align:middle up on it will kind of be be there. 00:29:17.606 --> 00:29:26.796 align:middle And secondly, when there is an edit, same thing type in the TagAwareCacheInterface 00:29:27.506 --> 00:29:32.226 align:middle and in this case we can safely invalidate on the one with the ID. 00:29:33.636 --> 00:29:34.706 align:middle So in this case, 'blog-post' . 00:29:34.706 --> 00:29:40.266 align:middle $Post->getId() and affecting just the caches where this one showed up. 00:29:40.986 --> 00:29:46.776 align:middle Um, there is actually a bug in this and that is if it's changes to those that are old tags 00:29:47.886 --> 00:29:50.816 align:middle and suddenly this page shows up, on this 00:29:50.816 --> 00:29:53.656 align:middle and this blog post shows up on a different, uh, list. 00:29:54.566 --> 00:29:57.236 align:middle It's, it's not going to show up before the cache, expires. 00:29:57.236 --> 00:30:01.516 align:middle But yeah, that's the life of caching. 00:30:04.106 --> 00:30:10.746 align:middle Uh, so now we have check this out and we should, I think, be able to just edit again. 00:30:11.386 --> 00:30:23.546 align:middle Mmm. So now updated successfully and, it shows up. 00:30:24.006 --> 00:30:27.616 align:middle So that's the basics of using cache tagging. 00:30:27.826 --> 00:30:37.456 align:middle Um, the last thing I wanted to show briefly was how you can just switch to the new adapters. 00:30:38.756 --> 00:30:46.336 align:middle So at the end of this, so I'm pulling in the Symfony 4 here to use the latest version 00:30:46.846 --> 00:30:53.526 align:middle and in the end, configuring App to be a new service, 00:30:53.866 --> 00:30:57.746 align:middle setting up a new service and setting it to the new class. 00:30:59.346 --> 00:31:02.486 align:middle And I want them on this because it will just fail. 00:31:02.656 --> 00:31:03.706 align:middle So I'll save you that time. 00:31:04.446 --> 00:31:14.646 align:middle Um, but I'll talk a bit about the downsides and the things you need to think about around this. 00:31:14.726 --> 00:31:19.416 align:middle So, yeah, I already mentioned one cache bug there and there can be others. 00:31:20.136 --> 00:31:27.186 align:middle So some things to be aware of around caching, um, race conditions. 00:31:27.866 --> 00:31:28.706 align:middle Uh, it's a common one. 00:31:30.186 --> 00:31:36.556 align:middle Um, you have, um, for instance, in order to load your entity you might have to do two look ups 00:31:36.556 --> 00:31:40.886 align:middle to the backend first to figure out, okay, what's the latest version or something like that. 00:31:40.886 --> 00:31:42.636 align:middle And then secondly, do you look up on that? 00:31:43.556 --> 00:31:46.526 align:middle Those kinds of situation can be prone to race conditions. 00:31:46.526 --> 00:31:50.106 align:middle For instance, there are many other cases if in between those 00:31:50.106 --> 00:31:54.766 align:middle who calls someone publishes a new version, it will look up the wrong version 00:31:54.766 --> 00:31:59.206 align:middle and your system might act wrongly then by using that. 00:32:00.196 --> 00:32:02.916 align:middle When caching data. 00:32:03.286 --> 00:32:07.726 align:middle So kind of the case I was showing here, if you, if you are using transactions 00:32:07.726 --> 00:32:12.156 align:middle and using complex things going on in those transactions, you'll have to deal with that. 00:32:12.636 --> 00:32:16.836 align:middle Hopefully we can see if we can find some solution in Symfony cache to make it simpler. 00:32:17.636 --> 00:32:22.216 align:middle But what we opted to do is kind of like disabled caching during transactions and make sure 00:32:22.216 --> 00:32:28.476 align:middle that we're not sending updates to the servers when we are within the transaction, 00:32:28.476 --> 00:32:31.826 align:middle but kind of just a store, whatever should be changed. 00:32:31.826 --> 00:32:36.706 align:middle So when you hit that the last commit, we commit all the changes to cache that should happen. 00:32:37.676 --> 00:32:42.236 align:middle So without doing that end up with a lot of strange stale cache situations. 00:32:42.886 --> 00:32:47.756 align:middle And then there's async and stale cache. 00:32:47.916 --> 00:32:53.226 align:middle Um, if you used Varnish, yeah, are probably aware of that, but it's typically if you, 00:32:53.226 --> 00:33:00.146 align:middle you forget to clear something, uh, or if you actually do it on purpose, um, 00:33:00.146 --> 00:33:02.176 align:middle then you need to be aware how to deal with it. 00:33:03.866 --> 00:33:12.016 align:middle Um, then the adapter, so we have the RedisTagAwareAdapter, it has a requirement due 00:33:12.016 --> 00:33:19.996 align:middle to the non expiry on the tags to use volatile, um, cache eviction or no eviction, 00:33:19.996 --> 00:33:21.286 align:middle which is the standard in release. 00:33:22.086 --> 00:33:29.256 align:middle So this means on the pro side, it has just one round trip to the server on the negative side, 00:33:29.256 --> 00:33:31.466 align:middle including consumes more memory one. 00:33:32.086 --> 00:33:36.536 align:middle And two, you risk of running out of memory because it will not be able to clear those tags. 00:33:37.116 --> 00:33:39.096 align:middle So that's kind of a problem for some. 00:33:40.006 --> 00:33:46.436 align:middle And then maybe this adapter is not for you or you need to put much more memory to Redis. 00:33:46.576 --> 00:33:49.246 align:middle So comparing that to RedisAdapter, the one that we had 00:33:49.296 --> 00:33:52.396 align:middle for quite some time now, uh, uses less memory. 00:33:53.226 --> 00:33:54.396 align:middle All the memory can be freed. 00:33:54.396 --> 00:33:57.176 align:middle You can use whatever eviction strategy you want. 00:33:58.086 --> 00:34:03.666 align:middle A negative side, it does two look ups and done MemcachedAdapter compared to this. 00:34:03.666 --> 00:34:05.216 align:middle Again, even less memory. 00:34:05.866 --> 00:34:10.216 align:middle Um, because of the just one data type and probably other reasons it, 00:34:10.216 --> 00:34:12.746 align:middle it's more efficient on how it stores things. 00:34:13.466 --> 00:34:20.816 align:middle All can be freed and at least, in smaller setups it's capable of handling more traffic normally 00:34:20.816 --> 00:34:22.446 align:middle because of the multi tread in nature. 00:34:23.096 --> 00:34:25.776 align:middle But that's a, not everyone agrees with that. 00:34:26.486 --> 00:34:31.796 align:middle On the con side two look ups to, to do when you use it with tagging. 00:34:33.156 --> 00:34:38.066 align:middle So this is all in regards to, in the context of when you use tagging, 00:34:38.066 --> 00:34:40.396 align:middle if you don't use tagging, there will just be one lookup. 00:34:40.396 --> 00:34:45.836 align:middle So now and then you haven't, don't need the RedisTagAwareAdapter in the first place. 00:34:46.236 --> 00:34:50.616 align:middle Lastly, some details on the RedisTagAwareAdapter just 00:34:51.506 --> 00:34:56.346 align:middle like RedisAdapter it uses MGET to look up everything it needs. 00:34:56.346 --> 00:35:00.976 align:middle So one call, spread it around on all servers when you're on Redis cluster. 00:35:00.976 --> 00:35:07.216 align:middle So in parallel and on the invalidation in this, a few things, um, 00:35:07.436 --> 00:35:12.286 align:middle here showing how the command will look on the command line, it does a rename 00:35:13.126 --> 00:35:17.726 align:middle and then the existing name and then it does the curly brackets there. 00:35:18.056 --> 00:35:23.436 align:middle So that's the kind of the trick it needs to do to avoid, to hit that cross slot issue I talked 00:35:23.436 --> 00:35:28.856 align:middle about earlier because now we telling the client that, Hey, we want to put it on the same slot 00:35:29.066 --> 00:35:35.376 align:middle as the, that key in the first place, but we add additional, uh, suffix, um, 00:35:36.016 --> 00:35:39.186 align:middle to make sure this now our unique one that we can deal with. 00:35:39.506 --> 00:35:42.446 align:middle Specifically, this temp is a unique cache. 00:35:42.766 --> 00:35:45.616 align:middle So there is no, there's not one string. 00:35:45.646 --> 00:35:50.496 align:middle There is a, so we can safely now use this on our process 00:35:50.496 --> 00:35:52.416 align:middle without interference from other processes. 00:35:53.196 --> 00:35:56.206 align:middle We can read the numbers and we can delete. 00:35:56.206 --> 00:36:02.746 align:middle You can delete that, the set itself and the keys within the set, so then the members. 00:36:03.056 --> 00:36:06.616 align:middle So we can safely do this all the way until it's done 00:36:06.776 --> 00:36:09.666 align:middle without interference from other processes and yup. 00:36:09.976 --> 00:36:16.736 align:middle Done. And that is the last, Oh, you're one. 00:36:17.356 --> 00:36:21.336 align:middle Um, someone wanted to have a picture. 00:36:21.876 --> 00:36:23.536 align:middle Uh, this will be online by the way. 00:36:24.836 --> 00:36:25.416 align:middle That's okay. 00:36:26.246 --> 00:36:28.006 align:middle Yeah. Okay. 00:36:28.656 --> 00:36:30.686 align:middle So that's the end. 00:36:30.946 --> 00:36:39.786 align:middle Any questions around any of this? 00:36:40.316 --> 00:36:42.356 align:middle Yeah, I'm sorry. 00:36:42.426 --> 00:36:45.346 align:middle Are there any limitations on the tags? 00:36:45.346 --> 00:36:49.676 align:middle I mean, maybe there is a how many you mean? 00:36:49.716 --> 00:36:52.556 align:middle Yeah. Or how long they can be. 00:36:52.906 --> 00:37:01.426 align:middle Yeah. The, the limitation of the set is 4 billion members in Symfony 4.3. 00:37:01.426 --> 00:37:09.776 align:middle We had, um, limit in PHP due to, we used s pop, uh, to for 2 billion, but now, 00:37:09.776 --> 00:37:11.696 align:middle now we're using it, uh, as it is. 00:37:11.696 --> 00:37:14.696 align:middle So it's 4 billion. 00:37:15.796 --> 00:37:19.386 align:middle Okay. Any more questions come by me later? 00:37:19.506 --> 00:37:31.016 align:middle I think everyone is ready for lunch. 00:37:32.276 --> 00:37:35.166 align:middle Thank you everyone.