 Good afternoon everybody. Is this too loud? Not loud enough? Good? I guess it's good. Okay. So my name is Wim Leers and this session is about building really fast websites. And I'm a speed freak. So I really hated my website slow slowly. Maybe one of you also has the habit of on a mobile device when you're loading a website, counting to five and if it's really long enough five seconds just closing in town because it's annoying to hell out of you. At least that's what I do. So my goal is really to have every website load fast. Some mobile devices are not. So I actually work for Acquia on the Spark project which you may know. But that doesn't mean that I work just on Spark stuff. I also work on related stuff in Drupal 8. Or in the past few months I've been spending some time working on performance improvement specifically in Drupal 8. And this talk is going to be about some of the improvements in Drupal 8 that should make your lives easier, your sites faster and your sites more scalable. These are not things that were built just by me. These are things that were built by community, by consensus. Lots of smart people collaborated on this. And these are just, this is not everything. This is just a few things that I find personally really interesting. So there are patterns that are applied in Drupal 8, some of which you can apply in Drupal 7 as well. And I hope that they are of use to you as well and that you can really leverage them. So hopefully you'll find it interesting. So what this talk is about, what Drupal 8 is better than 7, render caching, client-side caching, and pluggable CSS and JavaScript optimization, which is a whole lot of words. But you'll see that it's actually really interesting and not that hard anymore in Drupal 8. And of course some of the things that we should have and hopefully we'll have, but that aren't there yet. So some of the things that still need to happen. So let's get started. The first thing, my personal website was for a very long time on Drupal 5 until 2011 when Drupal 7 was already around for quite some time. And I didn't find the time to upgrade it to 6, so I skipped 6, went from 5 to 7. And since I am a speed freak, I really care about performance, I wanted to make sure that my website learned it as fast as possible. So it could be some sort of example of how you can make a simple website, of course, because it's not complex, how you can make it fast. And to my surprise, and I'm not sure if many of you have noticed this or if many of you have tried this, but if you try to make a website on Drupal 7, or even a single page, load with our JavaScript. Good luck, but it won't work because Drupal 7 will always load jQuery.js, Drupal.js, and Drupal settings. So that's not so great, of course. And in Drupal 7, we fixed that. So in Drupal 8, we fixed that, of course, not 7. So in Drupal 8, what we're doing is two important things. We're declaring all dependencies. So whenever you load a certain assets, you declare which things it depends upon. So the Drupal knows which things to load so that it doesn't have to do the stupid thing anymore of always loading jQuery and Drupal.js. And we're using pound attached or hash attached instead of Drupal at CSS, Drupal at JS, and Drupal at library. And I'll explain why that's much better in a second. And sadly, this problem, as in the problem of jQuery.js, Drupal.js, and Drupal settings always loading, is something that is utterly unfixable in Drupal 7 unless you break the API. And Attic, I'm not sure if he's here, Peter Druckmans, a Belgian guy. He's bravely working on a workaround, but it relies on a settings.php setting that essentially changes the API. So there is no way to make it work good in Drupal 7 out of the box without breaking the API, hence breaking all of contrib. So we can do that, but we can't, you can't fix it for individual size by applying his patch. Hopefully it will end in core. And then you can do it as well, but then you will have to declare all your dependencies. And if you use contributed modules that don't do that, you'll have to update those. In any case, let's take a look at the fix. So if you look at an example of a.js file, for example, table select.js in Drupal 8, there is some JavaScript in there, and we're using two things that aren't part of JavaScript itself. We're using Drupal. Drupal behaviors is what you see up there. And we're also using jQuery, the dollar sign, dollar context. So those are the two things we're using. And if we're using those, then we should import them into the current closure. So jQuery is used, Drupal is used, and you see these things at the top and at the bottom of the code example. What we're doing essentially is we're declaring, we're using jQuery, we're declaring we're using Drupal, the Drupal object that is available through Drupal.js, and we put them in the closure. So we rename jQuery to dollar sign, so that's easier to use. That's the convention. You can rename it to llama if you want. It doesn't matter. So if you use it, you declare it in the closure. I keep pressing the wrong key. This is the right one. So that's the JavaScript side. You declare your dependencies by putting them in the closure. The second step is to do the similar thing on the PHP side because Drupal needs to know which things it needs to load, so the PHP needs to know, right? So we have tables like.js in the library, but the library also declares two dependencies, one on jQuery, one on Drupal. Very simple. So the rule is jQuery and Drupal are in the closure of the JavaScript file, so we should also declare dependencies on the PHP side. Very simple. The next step is to abandon Drupal at CSS and JS because we don't want individual assets anymore because you can declare dependencies for those. So instead, you should use Drupal at library. You should always, always use libraries because then you can declare the dependencies properly. However, you should also abandon Drupal at library because you should be using bar in the touched. So what you're doing is essentially the same thing, but you're doing it in a slightly different way. And the reason you can't use a Drupal at functions anymore is because they use global state and that breaks caching. Because, for example, if you have a block and that block uses foo.css and you add it through Drupal at CSS and it's then cached in the render cache in the server, in the database, and then you retrieve it at later point in time because some other page wants to use that, then the Drupal at CSS call won't occur anymore because the code that generated the block won't be run because it's already cached, the HTML is cached in the database, you retrieve it, hence the CSS won't be added, hence your side breaks, or at least won't look as it should look. So don't use the Drupal at functions, you can do it in Drupal 7, you can use parent attached in Drupal 7 as well, because it breaks caching, just use parent attached always. The conclusion here is really simple, you should always load assets by attaching libraries. That's it. It results in better DX because there's less or no magic rather going on. You have cacheable render arrays, which you wouldn't have otherwise because if you use Drupal at functions, something is happening with lights. Okay. So you should always, an advantage is also that you have cacheable render arrays, which you wouldn't have otherwise because you would be calling Drupal at functions, only the required assets will be loading, so load it, so no more jQuery that is loaded while you don't even need it, and dynamic content, as in the one that you Ajax in, will also have the proper assets without having maybe the same asset loaded twice, or all sorts of weird things that can happen when you use a Drupal at functions. And all of this you can do in Drupal 7. So to prepare to move to Drupal 8, just do things correctly, or as well as you could in Drupal 7, and you'll be, asset wise, you'll be very, it'll be super easy to upgrade. So it's one thing, it's a tiny thing, but it can have a big impact. So the next thing, one of the things that has gotten much more expensive in Drupal 8 is the rendering of entities. And to get to Drupal 7 or better performance levels, we need to finally achieve render caching of entities. We've had the render cache API since 7, at least, maybe six, I don't think so, but we've had it in 7, but we aren't really putting it to use, because there are still too many things breaking the render cache, and that's what we're fixing in 8. So essentially what this means is, if render caching works for entities, then users with the same combinations of roles, for example, editor and authenticated, all those users, maybe 10, maybe 100, maybe 1,000, they will all get the same chunk of HTML without having to regenerate that HTML over and over and over again, which is pretty expensive. So the goal is essentially to really use render caching. It wasn't already available, but we weren't really using it. And the thing it was blocking it was we're not yet putting the right data in there, but we'll get to that in a second. So before, we're almost there. We're not yet there. We're almost there. While this conference was going on two days ago, the patch got committed that adds entity render caching, but it doesn't yet add the render caching for nos and comments, which are the most common entities, of course. So that's coming soon, but it's not there yet. We're almost there. But before we got there, we had to do quite a lot of things, and this is just a tiny selection of things. So cache tag support is something very cool, very interesting. You can do a lot of things with it, and it's useful for many things, not just for render caching. You should definitely look at it. Contextual links were fundamentally incompatible with the render cache. This is one of the reasons it couldn't work in Drupal 7. The new indicator, the new and updated indicators, the red small markers, they were also fundamentally incompatible. So that's another reason why Drupal 7 couldn't do that. So we're almost there, but still not yet. And that actually brings us to this nice quote that I'm sure most of you have already seen at some point in time. There are only two hard problems in computer science, naming things and cache invalidation. And naming things is irrelevant here. Cache invalidation, though, is very relevant, because caching itself is super easy. As in, you do this complex thing that is very expensive to do. You store it somewhere, and then you retrieve it later, so that you don't have to repeat the same expensive calculations over and over again. And that's easy. But once you start going to the point of invalidating things, as in determining when something in the cache should no longer be used, because it's inaccurate. Well, that's the hard part, because you can do it in a very efficient way, or you can do it in a very inefficient way. As in, you could clear everything if you change this, sorry, you could clear all the entries in the cache if you just change one thing. That works, but it's very inefficient, because then every cache entry has only a very short amount of time where it can make itself useful. Whereas if you clear just the things that are affected by changes on your website, it will be very efficient, because it needs to generate things far less often. So really, the reason for poor performance when you're using caching is poor cache invalidation. And that's the thing that we have to solve. So really, we want to first make sure the Drupal has solid cache invalidation, and only then we want to make sure that we fill the cache correctly. So, fill cache correctly and solid cache invalidation. Those are the two things that really matter. Before we continue, just a very small acronym, instead of me repeating it every time in full on the slides. Just like we have I18N for internationalization, you have also got P15N for personalization. So personalization, P15N. Let's start with the first one, cache invalidation. Cache invalidation is hard. I'm sure many, or if not all of you, have seen this particular page. It's a performance settings page in Drupal 7. And one thing is not blurred out. It's a thing that matters. It's a setting called cache pages for anonymous users. And I think most of you are familiar with this, but I'm not sure if most of you are familiar with the fact that if you enable this, which is fine in and of itself, but if you create any new node, or edit any node, or any anonymous user that creates a new comments, it will actually clear all of the cached pages. Everything. And that's not very great, is it? Because then you're not really using your cache properly. So it clears the entire page cache every single time. But the reason is that cache clearing in Drupal 7 only had a very limited API. So you could clear a specific cache entry, cache clear all, some ID, some cache bin. You could clear a course set of cache entries, meaning everything that is in the cache that matches a certain prefix. So everything that starts with full colon content colon, you can clear all those. And the final possibility that you have is clearing all cache entries in a cache bin. And that's, of course, the least efficient of all. So the question is, if I edit node 42, how can I clear all those cache entries just containing node 42? The answer is it's impossible in Drupal 7. You just don't have the API to do that because you can't know which cache entries all contain node 42. There is no way to predict all IDs, as in the cache IDs, that may contain node 42. Because any country module can create any sort of cache ID that it wants. And there is no way of figuring out which things it contains. So really, that's the crux of the problem. We couldn't do better cache invalidation in 7 because we didn't have the API for it. And now in Drupal 8, we have something new called cache tags. And that's really the thing that makes a lot of new things possible. So in Drupal 7, you had the cache underscore set function. This is the equivalent in Drupal 8. So you call the cache function, you specify a bin, you call the set method on it, you specify a cache ID, of course. You specify a value to be cached. You specify, for example, cache permanent, which means that it won't expire automatically. And then the new parameter tags. You can specify cache tags there. So they look somewhat like this. So if you want something in the page cache, you would specify the content cache tag. If it's just some HTML containing just a title of Node42 or maybe the author of Node42 or something of Node42, then you could tag it with Node42. And if it's a full node with, for example, two taxonomy terms, then it would look like this. A node, Node42, a user, the user's ID, which is the author. And then two taxonomy term IDs. And now you have all the information of the things that are embedded within the node, as in the things that are within the render cache entry. So really in Drupal 7, you could only clear one specific entry, all entries, or a core set of entries, which kind of is like this, but not really, because it doesn't allow you to find which specific entries contain a certain thing. This allows you to do that. And this is what enables much better cache handling. So using cache tags and render arrays in Drupal 8 looks somewhat like this. The keys and granularity parameters to the pound cache key already existed in 7. They are the things that make up the cache ID. They automatically generate a cache ID. But then the tags key is a new thing. And in this case, it's generic for all entities. So entity type expense to node, for example, in our example, entity ID expense 42. So this is tagged with Node42. But you may wonder, Node42, it does not contain just Node42, because also on the previous slide, what we saw up there was, for example, this. Node42, user 314, taxonomy term, do taxonomy term. So how do you do these two additional things get on there? Because this really only affects Node42. The answer is every field within a node is a child of the render array of the node. And what we're doing is we're traversing from the top level, which is the node itself, into the child levels, as in the fields themselves. And they also have cache tags. And we just collect them and put them at the top level. And that's what the triple render collect cache tags function does. It just merges, it collects all the cache tags that are relevant, puts them at the top level so we know which things are inside of the whole. And that's it, essentially. But clearing caches is what is what's called in Drupal 7. You can have the cache clear all. Now in 8, you have something slightly different. You have more granularity in what you can do. You can use delete tags, which means delete all cache entries that contain these tags immediately, which prevents still content because you delete stuff immediately. But there's also invalidated tags, which allows you to keep still content, because maybe your code doesn't really require you, or your use case maybe doesn't require you to have the very latest content. Maybe it's okay to serve slightly still content. In that case you can say invalidated tags, which merges them as outdated, but the cache entries are not deleted so you can still use them. So as you can see, you have more control of what you, depending on your use case. So we handled caching validation. I think, I hope it's pretty, you guys, that invalidation is a lot more flexible and fine grained and you can do a lot more with it in Drupal 8. So the next thing, caching is easy, right? That's what it feels like always. You just generate something, store it somewhere, that's caching. However, it'll be more interesting, I promise. Contextual links in Drupal 7, for example, it looked like this. It's a touch contextual links, but doesn't really matter. The thing in the top right hand corner is what the contextual links are in case you hadn't seen it yet. And that particular thing looks in the HTML somewhat like this. So there is a div containing a UL, containing links. Those links are access based, as in not every user has access to all these links. For example, an anonymous user doesn't have the ability to delete a node, right? So it's depending on the current user's permission. So it's role dependent. But not only that, you also see the query parameter to the URL. So there's question mark destination equals node. And that's path dependent. Because what it's doing is, it says, okay, if you click this link, you can go to the latest node. And then once that's done, you should be redirected to the destination is the node path, which is a front page in Drupal. So it's not only role dependent, but it's also path dependent. So really, there is a problem here because we have HTML that's embedded in the node's HTML. And it's adapted to the user's permissions and to the current page. And that just breaks render caching because this specific render cache entry, if it were to go into the render cache, would only be useful in very specific circumstances, which is this page. Imagine having 10,000 locations where this node appears. Then you would have 10,000 cache entries instead of just one. And that's of course not very ideal. So we have to figure out a way to make it work with render caching and still have the personalization because this is what personalization is, right? The links are to the specific current user. So in Drupal 8, what we did was instead of having the entire HTML like this, we made it look like this. Just a div that is an invisible placeholder with a data attribute that contains the relevant metadata that is used to generate the contextual links. It's also embedded in the node's HTML. However, it's an invisible placeholder that doesn't contain anything user specific, so it is compatible with the render cache. So how does this expand into the actual contextual links in Drupal 8? Well, we have the contextual IDs. That's what they are called. We have those in the HTML. We load some JavaScript that is only added to the page when the user actually has permission to use contextual links. And that JavaScript is going to make sure that the contextual links show up by posting those contextual IDs, the identifiers in the invisible placeholders to a certain path route in Drupal 8. And getting back the rendered contextual links. That's it. And the result is we have the same HTML for all users, so it can be served very fast immediately to all users. It's the same for everybody. And the personalization can be applied later. Yes? I wonder if there's anyone in the room that has a key to that. Okay. Can somebody turn off the lights because they were being turned off earlier? Thanks for saying that, by the way. Okay. I wish somebody gets spoken up sooner, though. In any case, I hope the explanations were sufficient in that case. So contextual links and contextual IDs in HTML, JavaScript loaded only if the current user can use contextual links, a request to the server to generate them, and then it's inserted into the page. Results, same HTML for all users. Personalization is applied later, as in not when the page is served, but only applied when the JavaScript is doing its thing. A similar story for the new comment marker. So you had the new marker and the updated marker for Drupal 8 comments, comments in general. And you see a small diff at the top of the slide. And before, it was really just printing the final result immediately. So it was calculating for every comment for the current user if the user had seen it already or not. And if so, apply a newer updated comment marker. And instead, what we're doing now is, again, a hidden placeholder with a data attribute that contains some useful metadata. That is the same for everybody. Another thing we're adding is because just a common timestamp, which is a data being added for each comments, is not sufficient to be able to figure out if the user has seen it already. We also need to know which node it is. So in HooknodeViewAlter, we're adding a data attribute to the node. So we can correlate the common timestamps with the node IDs and figure out what the appropriate marker is for the current user. So again, we have embedded stuff that's embedded in Node's HTML, again, an invisible placeholder, and again, compatible with the render cache. So the logic for this in AA is similar to what we just saw. Node ID, data attribute, common timestamp, data attribute, some JavaScript that is loaded only if authenticated because authenticated users are the only ones that can use or see rather in these markers. We've posted the node IDs in this case to a certain location on the server. We get the response back. The result is same HTML served to all users and the personalization is applied later through JavaScript. Tracker new and updated markers, which is similar, X New Commons links, it's all similar. The general gist is Drupal 7 user-specific data on HTML, which is problematic in Drupal 8. We have data attributes with universal truths, if you will, perhaps on invisible placeholders, perhaps somewhere else, like, for example, on the node. The node's HTML, of course. We load some JavaScript if the user has a proper permission. And only then we cache and then we cache user-specific data on the client side. And only if necessary, we talk to the server. So really, the conclusion here is to use cache tags precisely because if they're not the right ones or not granular enough, not specific enough, then they won't be of as much use as they could be. We tag render arrays with cache tags. You can delete cache tags to prevent still content, invalidate them to allow for still content, personalize via JavaScript and caching on the client side. That allows you to have cacheable render arrays, which can be served to everybody, at least to everybody with the same combination of roles. You apply universal truths through data attributes and you personalize through JavaScripts in combination with local storage, if you wish. So the upper half use cache tags precisely is about cache invalidation, which is a tricky part. And then cache filling, as long as you're not doing user-specific stuff, you're safe. And if you're doing user-specific stuff, use this approach. Now, that was render caching. The next thing is caching all the things on the client side. Because I think some of you, if not many of you, are thinking, didn't you make Drupal a whole lot slower? Well, the answer is so far from what you've seen so far. The answer is yes, because we have separate requests for the Drupal 8 toolbar to render the entire menu tree for the new and updated comment markers, for the contextual links and for in-place editing. So if you have all those things enabled, that's four separate requests, so from one request per page to five requests per page. And that's not great, of course. So let's take a step back and look at our goals. The goals are to make Drupal itself more scalable, as in more requests answered per second. The second thing is better perceived performance is what matters. Not better performance in general, but better perceived performance as in how fast the user sees things on his device. So what we're doing is, by serving HTML faster, because we don't need to personalize on the server side, we can just do very little computation on the server side and send HTML as fast as possible. We personalize via JavaScript, meaning that it doesn't have to happen on the server and we cache things on the client side. What we get then is, we get always a faster time to glass. And time to glass is really the time between requesting a page and actually seeing something appear on your screen. So it's always going to be faster because less calculations have to happen on the server side. And what we're doing is, we're just moving personalization from the server side to the client side. And then the personalization on the client side, what it's doing is, it's indeed making things slower if the cache is cold, meaning there is nothing in local storage, nothing in the client side cache, because now we would need five requests per page, which is not very great. But it won't happen every time because once the cache is warm, it will think things will be a lot faster because imagine that you have to send the entire toolbar menu tree, which is the entire admin menu tree, which can be quite large if you install additional contributed modules, which everybody does, right? And you have to send those many kilobytes of HTML with every single page request that will make things quite a bit slower. And if you can just cache it on the client side and use it from there, it will be a lot faster. So really, what this is about is about having an HTML skeleton contains the main contents of the page plus JavaScript personalization. That's going to be faster as long as the client side cache is warm. And another important fact that I think many of us in the Drupal world have forgotten is more personalization implies a slower website because how many of you did think of the fact that something simple, stupid, tiny, as the common new and updated markers were actually making your site slower? And there's many of these kind of subtle, small things that we are maybe forgetting about and that we really should take into account if we want to make our websites faster and as fast as possible, really. So the real question here is how to keep the cache warm? Because as long as the cache is nice and warm, things will be faster, especially on mobile devices. And that brings us to, again, the same quote, essentially. Cache invalidation is really hard. So not only do we have to solve it on the server side with cache tags, render arrays, and so on, but we also have to solve it on the client side because, again, caching is easy. Poor cache invalidation is hard and poor cache invalidation. That's what really leads to poor performance. So let's look at an example. An example is a toolbar in Drupal 8. Before the optimization that went in, I think, last week, we had one HTTP request per page to load the menu tree that you see on the left-hand side. And that's not good, of course. That's very, very bad. But now, after the optimization, we only have one setting per page. As in a one tiny string of information, that's it. That's the only overhead we have. And how do we do it there? Well, in the page build, which is kind of like hook in it in Drupal 7, what we're doing there is we're adding a setting that contains a hash, a hash that identifies the content that should be cached on the client side. And we make sure, of course, that calculating the hash itself is also fast because otherwise this is expensive and we're not really winning anything. And this hash is what identifies the toolbar menu tree that we should have on the client side cache. And it depends. It changes whenever a role's permissions change, a user's role change, a menu link in the admin menu, not just any menu, but the admin menu changes. And when any module is installed or uninstalled. So anything that might affect what is contained inside the admin menu tree. So the hash changes whenever anything changes in what it should look like, what it should contain. Now, combined effect that we're adding that setting to the page, combined it with this JavaScript. So this is somewhat simplified, but it gets to just across. So retrieving, the first thing we're doing in the load function, so we're renaming local storage to LS to keep things shorter, we're retrieving the hash from Drupal settings. It's Drupal settings, one word now instead of Drupal.settings in Drupal 7, but that's just a detail. We retrieve the hash. Now we look at the hash that is stored in client-side cache, because if those two are the same, which is the if test that comes up next, then that means that the thing that we have cached in the client side is exactly the same as the thing that we should be showing. So we don't have to do any work, except for getting the stuff, the menu tree from local storage, parse it to JSON again, and then just set it as in loaded, show it to the user. However, if the hashes are not the same, meaning that the thing that the server says we should be showing is different from the thing that we have cached on the client side, then we should remove the thing that is cached on the client side, do an Ajax callback to start loading it, and remember the hash that we are now supposed to be showing, and then once the Ajax callback fires, we have the save method that's at the bottom that's storing it in client-side cache and then showing it to the user. So as you can see, the overall logic is not hard at all. Most of the logic is that it sits on the server side where you determine this hash that you can use, which you can kind of view as a client-side cache tag like we have on the server side in Drupal. So that's it essentially. Another example is the new common market that I explained earlier. So I told you that it was doing additional HTTP requests, but that's not really the case. It's trying to be smart about it. It's applying heuristics to make sure that it's only doing HTTP requests when it's really, really necessary. For example, Drupal forever, or at least in seven, I think in six as well, has never shown updated or new common markers. Whenever the content was older than 30 days. So Drupal essentially assumes if content is older than 30 days, the user should have seen it already. I don't care if it's relative to that user new or updated, we just assume it's already been read. So that means that for anything that's older than 30 days, which on big websites is usually a lot, we just don't have to do any requests at all because Drupal already was assuming that it was read, so no work for us, great. That's a lot less things that we have to do. However, for those things that are newer, what we're doing is we're storing the last read timestamps in local storage for a node. So how the new and updated common markers work is essentially it tracks for each node when the last time is that a user has seen that node. Very simple. And what we're doing is we're caching that information on the client side as well to be able to determine based on the common timestamp that we were already embedding for each comments. And then we can compare that information with the last time a user has seen the node and then we can just match them and figure out if it's new or updated for the user. What's interesting in this case is that we don't need something like the toolbar where we had this hash that changed whenever some things on the server side configuration changed. And why is that the case? Because, well, what has been seen cannot be unseen in Drupal because you cannot mark something as unread. If you've seen something at point A in time, then if you're progressing a week, then that thing was still seen at point A in time, right? So any comments that was created before point A in time will by definition already have been seen. So that may sound very verbose, but it's actually really simple. So if you put it on paper, I'm sure you will find it very sensible. So in this case, no hash is necessary. So you can see that there is various use cases and they all have very different properties, but you can do very clever things to make sure that you have to do as few work as possible. As little work as possible. So ideally we would have many hashes, many client-side hashtags essentially. And today we only have one and it's not really even a hash. It's just the user ID so that you can, for example, in the case of the common new marker, that's what it uses it for. It knows which user has which reads timestamp. So you can do user-specific stuff and cache user-specific stuff on the client side so that you know which user it belongs to. One thing that we will probably have, but we don't have yet is a permissions hash, which is kind of similar to the toolbar hash that we saw earlier. And what it does is it is a hash that reflects all of the permissions that the current user has. So if any permission is added or removed or even renamed, I think, this hash will change, which means that anything that is permission dependent that is cached on the client side should be cleared on the client side whenever that hash changes. So anything that's permission dependent can use this specific hash. And that's something that we will use in getting rid of the additional HTTP requests for contextual links and in-place editing. And that's just the top of the iceberg. We can hopefully add more. And if not, you can easily add more yourself. It's just as simple as creating a Drupal setting, passing into the client side, then have JavaScript that leverages it. That's it. So conclusion for client-side caching. You should only cache slowly changing data because it doesn't make sense to cache data that changes every second, like stock information. You should probably use it for personalization and in general, really you should use it for anything that is expensive to send because it's a lot of bytes to put on the wire or anything that is expensive to calculate because it requires a lot of server CPU. And in general, what's also really, really important if you want things to remain fast and not have to do additional HTTP requests every single time is to minimize the number of HTTP requests, obviously. Try to apply heuristics like we're doing for the comments new and updated markers and use hashes as kind of client-side cache tags. So that's all about caching. This is the last thing I wanted to talk about, which I also find really interesting. So optimize away all the unnecessary bytes. This is about asset handling. For the longest time, we have had aggregation in Drupal core, but for also the longest time we haven't had JavaScript minification in core. And in Drupal 8, it should become trivial to write your own JavaScript minification or plug it in very easily without worries like you would have worries in Drupal 7 and before. So the API, if you could call it that in Drupal 7, looked like this for aggregation. You had an aggregates callback and a group callback and that was it. You could override the whole, implement both or nothing or apply hex and hex is the thing that most people chose because that was the only way to get something meaningful done. It felt at least in Drupal 7. So the end result is that you had incompatibility hell essentially because the Omega team, for example, is doing some special stuff with I believe CSS in the header to do, I don't know, maybe chassis stuff. Then there is the CDN module that also tries to do, well, it just tries to implement CDN integration but it uses the same technique and because there is not really an API for this, they conflicted and they just, it was a mess. It was impossible to get to work reliably. Now what we did in Drupal 8 was essentially made the API more explicit, so that you really know what's going on and more building blocks so that you can override individual building blocks and instead of overriding the whole. The logic is really identical. So don't expect JavaScript minification core just yet. Don't expect any special new features just yet. It's just restructuring, refactoring into building blocks and the building blocks look like this. The first one is the assets collection optimizer interface and it sounds like a mouthful but it's really simple essentially. So this is an interface that's implemented both for CSS and JavaScript. Those are the two different asset types that we have. A collection of CSS files is essentially, for example, all the CSS on your page is one CSS collection. So this is for implementing the optimizer for all the CSS files on the page. What it uses is the asset collection grouper interface. This is what groups the sites which CSS files to combine. So if you had 30 CSS files on your page probably would have something like maybe three groups. Each group contained three files and contained 10 files, three groups, 10 files each makes 30. And then those 10 files per bundle would be concatenated together and minified. That's what this does. This is the thing that decides which the groups are. Then you have the optimizer. So given a asset optimized, meaning minified, meaning, for example, strip out all the white space. And then there's the dumper, which is essentially for Drupal Core, write it to disk. As in to public colon slash slash files slash CSS, something like that. Just some location to write it down. And then finally the asset collection renderer interface. So given the optimized collection, so from our 30 files we went to three optimized files. Those three optimized files are passed to the asset collection renderer and that generates the renderer rate that will eventually result in, for example, the script tags on the page. So that's all it does. And really the nice thing about this approach is that you can override individual things at will. So in Drupal 8 we have this thing called services. And each of these aspects is a service. So you have the CSS optimizer, which optimizes an individual CSS file, but you also have a JavaScript optimizer, which optimizes an individual JavaScript file. And the things that we get with this are really cool, at least as a speed trick I find really interesting. So possibilities are amongst others. You can override just the asset number, retain everything else, and then write your files to an external server, a CDM, S3, whatever you want. So that's one class, one tiny, tiny class, it's one method, just implement that, override the service, that's it, and your files will be written to S3, for example. You can override just the JavaScript optimizer, because if you're a hipster, you might be using Node.js, and you might want to use Aglify.js, and then your JavaScript can be minified, again, by writing a single class. JS Collection Renderer, you could implement, you could override the way things are rendered, not just render to script tags, but use the Lab.js script loader, if you're into that. You could do really, really interesting and amazing things if you are collecting enough log data so that you can use a third party or write your own data mining thing. And then you can calculate globally optimal groups. That's something that has been a problem forever in Drupal. We group files together, but we don't really know enough to make sure that the files within a group won't appear in another group on another page, and then we might be ending up downloading the same files, but part of a different group over and over again. This is where you can solve that. You can override the entire collection optimizer, so that's the whole thing that receives all your CSS files on the page and returns the optimized ones. You could make it so that, for example, a new aggregate is generated whenever the M time changes of any of the files within the aggregates, or you could just do the really cool thing, which is say goodbye Drupal architecture for asset handling. I don't want any of you, so just override the asset collection optimizer and use your entire custom architecture. Just return the optimized files and that's it. You can do whatever you want without having to hack core. And that's really the conclusion. You can override individual asset services in Drupal 8 because you can then improve individual asset services and keep everything else as it is, just because, for example, you really need Jace minification, you can do just that, or you can apply your entire own architecture, which was unthinkable in Drupal 7. So, looking back at the things we covered, first up, asset loading. Always load assets by attaching libraries. Render caching, use cache tags precisely to do proper cache invalidation. Personalize via JavaScript and cache on the client side to do correct cache filling. Client-side caching only do that for slowly changing data and keep the cache warm if you want it to remain fast because otherwise you'll just make things slower. And asset handling, you can now override individual asset services. So things that still need to happen, this is just a tiny selection. Entity render caching just landed, so yay for that, but there is a follow-up issue for that to enable entity render caching for nodes and comments, so that still has to happen. It would be really, really cool if we could have views work with cache tags because imagine that views are really fast because they can be cached properly and only when things change, only then the views will be cleared and that means that views will be faster for everybody. We could even work on enabling CSS and JavaScript aggregation by default, out of the box finally in Drupal 8 because now it's much easier to reason about things so we can have Drupal core react to changed files automatically. And the permission-slash thing that I explained earlier, it would remove two HTTP requests per page as it currently stands in Drupal 8. And really in general, anything, if you're interested in this stuff, anything that is cached with D8, cacheability, we can use your help. Please join us. So that was it, I hope you liked it and if you have any questions, feel free to ask. That might be better, yeah. I noticed that the caching system at the moment doesn't look like it uses any kind of object orientation. Is it something we can expect that the caching system is gonna move to object-based things going forwards? So the question is, the caching API in Drupal 8 doesn't use object orientation yet, essentially, right? Yeah, so you end up with lots of arrays again, basically, it's a problem. Right, so are you specifically asking if the render system will change to being object-oriented, because that's why you have these giant arrays still? Well, I mean, for tags, we're talking about caching tags and things like that, and you're putting together a big array of tags and things. So the caching system itself more than the render system, but that's a good follow-up question as well. So the caching system itself is now object-oriented, essentially, but it's just the aspect of defining which things are within a render array, the cache tags for those. Those are still indeed array-based, but that's not really a problem, though, but ideally, eventually, we would move away, indeed, from an array-based render system. So I hope that kind of answers your question, but not really. So it's not now, but soon. What's that? You're saying not now, but soon, for render arrays. You're saying it's a long-term goal for render arrays. Yes, it's a long-term goal. It won't happen in Drupal 8. Okay, thanks. I wanted to ask if you say that there are tags for the client-side caching, also for the server side. So is there any API for altering these tags? I mean, if I have an entity cache and I want to calculate a different hash per language, is something that I can do in the language model? So is a question, can I alter cache tags or is a question, can I alter the hashes that we saw? Both, okay. So cache tags, yes, you can alter those because it's essentially render arrays. So anything, any hope that can alter the render array, you can modify the cache tags there. You can just add your own or remove some. Though I'd be very careful with removing some. And for the hash that's being generated, that's a very good question. It should be possible, but I'm not sure right now if it is because we only have one essentially in core. But that's a very good point. And please open an issue for that against Silver module so that we ensure that that's the case. Hi, so you discussed about using client-side cache with local storage, but I think local storage is quite limited right now. Like there are limits in some browsers that are about 2.5 megabytes, which is not bad, but it's not unlimited by far. I was wondering, did you discuss this? Are you worried about this? Because I mean, I know with the standard installation, it probably won't be a problem, but then I'm thinking with many modules, maybe everyone will use this client-side cache. That's a good question. So as far as I know, the limit across, the minimum limit across all browsers is five megabytes, so it's somewhat more, but indeed it's still very limited. So yes, if you're doing a lot of client-side caching, you might run into trouble, but at the same time, five megabytes worth of HTML, I think seems like quite a lot. So indeed with core, you wouldn't run into problems. And I think that you would only run into problems if you're either doing something super complex or super big, which kind of is the same thing maybe. But I think only then you will need to start looking into this and indeed it would need to be optimized on a per-side basis. And I think maybe in the future, once this is more common to do, and in Drupal 9 possibly we have a full API on the client-side to handle these things, then that API might be responsible for handling and detecting a full client-side cache and dealing with that. But for now, I think the current solution should be sufficient. And worst case, if we're really running into limits there, Drupal 8-contrib could just override parts of Drupal, so Drupal 8-contrib could override parts of Drupal 8-course JavaScript to make it work with a Contrib-developed API that handles this kind of thing. But I think we're very, very far away from that being a problem. And once that becomes a problem, we're probably in a good place. Thank you. I tried, it's 2.5 here. Okay, good to know. Okay, thank you. Thank you. This might be something very basic, but I just want to ask you, are we saying that if JavaScript is disabled, we may not see something? And because most of them, it looks like we are going towards the client-side. And it, okay, yeah. It's a good question. So the question is essentially, if we're depending on JavaScript, then aren't we risking that the end user doesn't see some important stuff, right? That's the question, yeah. So I didn't mention this in my talk and I should have, but the idea is, for now at least, that you should only be doing this kind of thing for not the basic, essential primary content on your websites. Things like new and common markers. It's fine to load a page without that initially, right? Because it's just these tiny additional things. Contextual links is another example. They're not visible. They're the primary content by default. So it's not bad if it loads only half a second after the page itself is loaded. For example, if an additional HTTP request has to be made. So yes, it's very important that you pay attention to not loading your primary content in this way unless you really are, you know what you're doing and you really have weighed all the benefits and downsides. But that's a really good point, yes. Do not do this for your main content unless you have a really compelling reason to do so. Okay, I have one more question. So you mentioned that we have a lot of sub requests after the main page load. Are we doing something like a symphony kind of thing, like not doing full bootstrap, just doing the things that we need? So the question is for those HTTP requests that we do for getting the additional metal data and stuff, do we do a full bootstrap which is slow or do we have something more efficient? And the goal is to have that. But as far as I know, and somebody in the room who knows is better than me, who's familiar with the BISCII internals and that's what's going on there, please correct me. But I don't think that's happening yet, but it will before Drupal 8 is released. So it will be or it should be at least. Right now, not yet. So it's part of another sub system of Drupal that is still in progress. Thank you. Could you expand a bit on cache tags and perhaps the performance impact they have in tracking an array of tags on a cached object? So that's a good question. So the question is essentially, we're using cache tags but doesn't that cause performance regressions essentially or doesn't that itself cause performance problems when you're clearing cache entries? I don't know the exact performance characteristics but the way it is implemented is we have similar cache tables like we had in all previous Drupal versions but now we have a new column called cache tags or just tags and in there we're listing all the cache tags and we can then just query by those, find the cache entries that contain those cache tags and delete them. It's possible that there are still performance improvements awaiting there, but for now that's how it works and I don't think it has major performance regressions and Mark Sullivan wanted to comment on that. Right now there is a bit of a performance problem on with cache tags with MySQL but there's an issue to, that prompted an issue to actually decouple the cache tags API from the cache API because there's not really a reason that you need to store cache tags for a certain bin along with whatever bin storage you have for that so the performance characteristics will improve but we will get them to a point where there won't be an impact. Thank you Mark. Any more questions? Okay, thank you. Hey. Thanks for your talk. I'm Stefan, one of the track chairs. Okay, I have. Yeah, I think we met in Munich. I think so too, yeah. Hey. It's a good talk. I like the slides, they were very good. Okay, great, thank you. I hope that they explained the performance patterns that we applied in Trueblade. We could talk a little slower again. Okay, that's good to know. But it was. It's nice in case there was one on the highest bracket. There's three out of three. Okay. Nice to meet you. Okay. Thank you.