 Cool. So, as you found out, I'm Philip Johnson. I work for Bolster. Bolster is essentially a creative agency down in Melbourne. We specialize in anything to do with music and events, which is, you know, quite an interesting space and rather unique for the way that we have to do, you know, sort of web development. A lot of what we do is digital marketing and we started up an agency specifically to support that, so we do a lot of website development for festivals, touring companies, artists, and, you know, whatever else gets thrown at us. So, our problem. It was about, it was, what, January last year? So, in 2017, I'd just come back from about three weeks leave in India. The team at the office were preparing Groove and the Moe, which is a festival that runs across Australia. There was a couple of days till launch and I pretty much came in last minute to, you know, get it across the line, make a couple of changes and really try to steal all the glory. Now, Groove and the Moe being a national touring festival. There's multiple locations, so we kind of expected that once the lineup gets announced, we were going to have a high level of traffic. So, the guy at the time decided, cool, what we'll do is the client will connect to a load balancer. That load balancer will then, you know, have multiple EC2 instances on AWS and those EC2 instances will have an RDS database and have all our uploads going to an S3 bucket. Fortunately, before any of these sites go live, it's usually got a static web page. So, we had just host, you know, that in an S3 bucket. And our plan for the launch was, we would have the load balancer point to the one EC2 instance that's got the, you know, static page and then we'll just switch it over, point to the multiple instances and there'll be zero downtime. At least that was the plan. Now, the important thing for festivals is to actually get this zero downtime working for when you do a lineup announce. They are very particular about their timing. They line up the radio announcements along with media releases, all the digital marketing to be announced at the same time. If the website goes early, we've just kind of, you know, leaked the lineup. If the website goes too late, all the traffic that's going to it doesn't have the information on it. So, this zero downtime is super important. So, we came up to announce 8 a.m., we flicked the switch. We're expecting this site to go live. Instead, this one came up. If I'm going to get, there we go. So, not that good. So, we had an influx of 16,500 users between the hours of 8 a.m. and 9 a.m. Previously, there was maybe 300 users on the site. So, we had gone through and built a site that couldn't handle the traffic that we've got. Then we had to figure out what went wrong. So, for about two hours, we were on the phone to our client trying to calm them down. We were on the phone to the contractor who was not really answering because he was wetting himself because we've just crashed a major during festival website. Finally, we worked out, we're just going to need to put a lineup on a static page. We launched a static page by the afternoon and at that stage, the website had been down for effectively five hours, which is not a really good thing to have happen, particularly when they're paying quite a lot of money. The main thing that went wrong with this site was we had a lot of complex data connections. So, we really wanted to try and make sure that the client had to enter the content once and reuse it across the site. So, we might have, for example, 50 artists playing at various different locations. So, there's six different locations. Artists will play at some of them, but not all of them. They might play at all of them or one of them. So, we only wanted to have them enter the data in once. Then FAQs, there's FAQs for every different state. They might be for all states, same thing with news. So, we ended up with essentially custom post types and connections looking very much like this. So, we created content blocks. We created custom post types for artists, for marshals, for FAQs. We even had custom post types using a plug-in for tables. On top of that, they're all connected using advanced custom fields, using relationship connections and post objects, and then advanced custom fields throughout the entire site. This is actually a downfall because we had created this really complex SQL setup. When people went to visit the site, there was far too many SQL queries, and the CPU couldn't handle it, and it crashed. It crashed, load balancers entered the traffic to another server, it crashed, and so on and so forth. We tried to be quite simple for the client and caused a lot of problems. So, our solution at the time, pretty much we had to spend a couple of days optimizing these queries, going through, and we had to spin up larger EC2 instances so they could handle the CPU, and then essentially wait until the traffic died down on the site so that we could actually launch it and cache the page. That was the solution for Groove in the Moon. I spent pretty much the next year going through all our touring sites and our festival sites, taking a look at how they were built and what we could do better. We still liked the idea of these custom post types so that you could have data entered in once and then just link them across the site. So, my solution was build the theme using object-orientated programming and MVC. Does everyone know much about MVC? A lot of you control here? All right, I'll cover a little bit of that because that's the main focus that we worked on. For those aren't familiar, MVC, you've got, you essentially split out the logic, so you've got your styles in one section, so your HTML markup in one section, you set up your models which handles the data between the database, and then you've got a controller which just says, cool, get this data, put on this page. Quite simple. Super important to be able to have a setup like that, particularly if you're working with multiple developers because you can have one person working on the view, one person working on the data, and then when you store it in, you get, there's no conflicts. So, we had started and looked at our setup and we decided that we were going to change the way that we also deployed our sites and we ended up using the Roots.io stack. Now, if you're not familiar with Roots.io, I'd strongly recommend you go and check it out. It's a really good environment to go through and basically have your deployment set up, your WordPress installs, and even your themes. In particular, we wanted to use Trellis. So Trellis uses Ansible Playbooks to essentially allow you to provision all of your servers and deploy all the code all through the command line. So all I need to do now when I want to go through and set up a server, I jump on a digital ocean, spin up a new clean instance, make sure my SSH key is in there, and then I, one command, and it goes through installs SQL, the right version of PHP, and then if I need to go from staging to development, I just change one line of code. The other thing that we wanted to use was Bedrock. Now, we're already using Bedrock. It essentially handles your WordPress installs. So it uses Composer to manage your plugins, to manage the WordPress installs, and you can version control everything and make sure, obviously, with Composer, what version of WordPress you're using is consistent between your staging, your local, and your production environments. Now, I'm not going to go too much into these set up. The documentation on Roots.io is really quite good. If you want to check it out, recommend going and reading the documentation. It's pretty simple once you get your head around it. So that handled our deployments, and that handles our WordPress installs. From there, we ended up using Timber. So this is something that we were already using in our site. So Timber is really where our theme development works. So it's already got the concept of MVC built into it, so we didn't have to change too much. To give you an idea, this is a typical set up for what you would probably have in a theme file, and I assume everyone here has seen something like this. You've got your PHP logic running through loops. It's going through and grabbing your permalinks, titles, all that sort of stuff. Where Timber comes in is that this is what your index.php file looks like. So this is essentially the way that you would handle your controllers. So it sets up your model, which is the get posts, and it serves it to the Timber.tweak file, which then renders it out. So using exactly the same setup that you'd probably be familiar with with the theme development of single.php, index.php, page.php, you can just put this type of logic inside that and then render out these files. So this is what your twig file looks like. So has anyone used twig before for templating? Excellent, yeah. You'll probably be familiar with this sort of stuff. So you can basically, it's very similar to the original index.php, but you don't have to run through while loops. You're not trying to query the database from your theme file. The other advantages with Timber is you've got these caches. So it's got three main types of caches. You can do a partial cache, which allows you to collect just some stuff and stores it. And that's what's going on in this. So it's got your post loop. That way you don't have to query your posts every single time. It's got a page cache. So a lot of you might be familiar with this. This will essentially, basically once you load the page, it converts it to HTML. So instead of serving PHP, it'll serve a static HTML page for you. And this is quite good for any sort of stuff. Then you've also got transient cache. So built into WordPress, you'll probably be familiar with transient cache. This kind of takes that concept and makes it simpler. So this is probably where I'm gonna dive in a lot more. So if you're not really a programmer, I'm sorry. Hopefully you'll pick up something from this. Otherwise the talk next door is pretty good. The speaker's quite, quite interesting. So this is a transient cache. So the transient cache will allow you to do all your PHP logic. And instead of going through and running that every single time, at this one, this will store it for 600 seconds, which is the section at the bottom. It will then go through a main that anytime you call a transient tour, it'll return the logic that you've already gone through and worked out. So you don't have to go through and run through loops or anything like that. Now, this is a good start. And if I'd used any of these three caching methods within Groove and the Moo, we might not have crashed. But we didn't think about that. And we regretted it. We went through, I went through and basically took a look at this and went, well, for the best practices, you probably wanna separate out this kind of logic and put this into your models. Single responsibility principle says, you shouldn't be doing stuff like this in your controller. So I went through and simplified it. So I created an object specific for our tours that had all the data in it. And so now inside my tour.php is something very similar to this. Now, if you've used Sage before, which is the root stack, they use a composer plugin called, what is it? It's by SobaWP called controller. That's really good if mainly what you're focusing on is single page data. So for me, it wasn't quite right because we've got lots of complex relationship through post types. So I wanted to create models for tours, artists, shows, and that connected then with the venues, the stages, all those sorts of other connections. So for me, the Soba controller didn't quite work. I did play around with it. Instead, I went, well, I want something similar to what Timber is already doing. So this is essentially what I came up with. So we went through and created a tour model and extended it off the existing Timber post. By doing that, I was able to utilize all the stuff that Timber had already put in place for its models. So for example, if I wanted to go through and use an ACF field, Timber's already handled the way that you call that. It's just post.acffieldname. All I did here was take the logic from this transient cache. So that loop which goes through and gets all my shows and sets up the shows as a model and I put that into my class. So by doing this, all I need to do now is in my theme, I can go through and say tour.shows and do a loop on that. So that's gone through and set it all up. It's gone through and allows me to cache it and I don't have to worry about creating complex relationships and saying get field every single time I want to use this sort of stuff. My setup uses namespacing. So normally you would probably go through and say, this requires tour.php. I've got a namespace and an auto loader which basically gets anything. So that's probably how I'm handling including all these files. So then in my single do it.php file, I go through and I set up, I include my tour namespace. I go through and set up the context which gives me access to all the generic site themes. So normally you would say, get blog info. Tim has already got that inside. So all I need to do is site.name and it's got that sort of stuff which is what goes on in the context. And then I just go through and assign tour, new tour. From there inside my twig file, I've got this setup. So I can go through and just, as I said, loop tour.shows and then get access to all the show data. Now this is a really nice, what I thought was elegant solution. You guys might disagree and that's okay. So this is good. This allows me to go through and start separating out my logic so that I've got my models, my views and my controllers. I took it a little step further. So a lot of the stuff that we do, so we have all these complex relationships. So inside ACF I use a relationship field. That way I can link my artist to my show, my show to a tour, or for example a show to a venue and I can reuse all of that sort of stuff. ACF have got a really good function that they've got in their documentation for bi-directional relationship updates. So that means that when I connect my artist to a tour, I can go to my tour and see that the artist is in there in the admin. And so I wanted to go through and use that for all of the stuff that we did. Now, instead of having to create that code every single time, I created a base post type. So that base post type, similar to what you saw with the tour, that had a whole bunch of functions in it and then I'd take my tour and extend that base post type. Inside that I use anything else that's reused across the site I go through and have registered there. I've even got a register function which allows me to add filters. So inside, for example, if you were using functions.php, I can just say, cool, register new post type of tour and it'll do those functions. So inside my register function, it basically, as I said, register tour.function and then I add all my filters. So for any given post type, if I want to create a filter or a hook or anything like that, I can now run that through my register and now whenever I go through and create a new post type, I go tour register, it adds all those filters for me. So I don't have to go through and recreate code every single time I want to do this sort of stuff. And you can take that even further. So for example, maybe you want to modify the way that your images are handled across the entire site so you could put that and extend it inside your filters or anything. So the final thing that I really went through and did with my classes was start to put in variables. So I've got my protected shows at the top. And so now when I go through and run that shows query, which gets all the fields for shows and sets them up, I assign it to the variable. And this is quite good for me because a lot of the stuff that we do requires you to have Google Rich Sniffer data in the header. So I've got to go through and run a query that builds out JSON formatted data that will go into Google and that will then show up any time that, hey, C is on tour, you can search C or tour dates and it comes up in Google as opposed to need to go to the website. So we're reusing the same variables every single time. So instead of querying the database each time, whenever I want to go through and get the show's information, once it's got it once, I just go, there's shows. So that way I'm not hitting the database every single time that I need to go through and get this information. It makes things far more efficient, particularly if you're using ACF, you're probably fine that it can slow down the site because it can be quite intensive. Basically, this gets rid of the need for that because once you've done it once, you don't have to do it again. So basically, now that I've covered those kind of things, what's the next step? Where do we go from here? So I wanted to then cache all my data. I'm not a DevOps guy. I actually hate DevOps. That's why I'm using Trellis. So for me, caching wasn't that great. So I ended up going with W3 Total Cache. Now, the reason I went with this was because it's super easy to use. Some of you might be familiar with the way that caches that you can use a browser cache, which means that any sort of images or style sheets that you've got, it'll store it in the browser. So every single time you go to the web page, you've got that. You've also got the ability to do a page cache, which is really good if all you need to do is, you've got a five-page website. It'll store all that page. So instead of hitting the database, it hits that. The problem is, as I said, we use a lot of this complex relationships. So for us, an artist might be appearing at three or four different locations. If I was using a page cache, I'd have to cache at least 100-plus pages. And we could have people, for example, with Groove and Mood, they had Bendigo locations. They had Cans locations. They had, I can't remember where the rest of them, someone's Sydney. So they're all pulling the same data. Every single page, a person might visit. So a page cache didn't quite cut it for me. We ended up going down the route of using an object cache. And because I'd gone through and created all these classes with all my post types, I created an object cache. All those variables that I'd set in the functions were now cached. So any time an artist was called, it loaded once, it cached it, and then it served the cache file for every other time that people accessed the information. The advantage of that then is that you're not hitting the database, which means that all the problems we had with Groove and Mood were resolved. An artist loads on the home page. Next, when you go to see the lineup at a specific location, it was getting the cached version of that. So we'd gone through and used this to basically handle all of that sort of stuff. Now, we had done some of these things for Groove and Mood, but because we hadn't constructed our models well, it didn't matter what we did with our cache. It was not going to be able to handle it. So let's take a look at it on a Turing site. Now, this is a Turing site. This was probably the first one that I tried to implement all of this stuff in one hit. Now, it's only a Turing site, so we weren't expecting massive amounts of data, but I'll show you it anyway. Let's see if I can know. All right. So, SBM presents. Did anyone else know Bill Murray was a musician? I didn't. These guys are Turing him. So SBM presents. This has got maybe about 350 different posts. Oh, it's not loaded. Sorry. Excellent. There we go. Excellent. So, basically, I've got about 350 different posts across artists, tours, shows. So this page pretty much loads everything on page load. So it's got embedded content in all these sorts of areas. It's got a post type for artist, tour, show date. And it loads in about three and a half seconds. And it's loading all 350 posts on the one page. And that's by using this sort of setup. Now, realistically, for past tours, I mean, we're going back to 2004, so it probably didn't need to load all those on page load. I could have done lazy loading, but I was lazy and I didn't. Funny. Yeah, so essentially, we've gone through it and set this sort of up. Now, if I go to this page, it's got all the data loaded. It navigates quite quickly. It's great. It's essentially a prime example of, you know, how you can go through and use complex relationships in a fast way. Now, that's all well and good. This is a site that didn't need to be highly available. Tours don't get the same traffic. Like, we didn't have 16 and a half thousand people trying to hit Bill Murray's tour. And to Spilt Milk. Now, Spilt Milk was the first festival website that we essentially rolled this environment out. George Gambino is on the lineup. You know, we've got all this sort of stuff. It's a little bit slow. You can see all this sort of information. It's only a single location festival, so it doesn't have the same traffic that something like Groove in the Moon did. Now, for this launch, I didn't want to go through and use AWS with Elastic Load Balance and multiple EC2 instances and S3 buckets, any of that sort of stuff. As I said, it's a small festival, doesn't need that kind of instance. Once again, just like we had with Groove in the Moon, Splash Page goes up before launch. Always Splash Page goes up before launch because they want to rebrand it every single year. So I was like, well, Splash Page, hosted on Digital Ocean, we'll put it on the smallest instance possible. It's a static page. So I spun up a Digital Ocean droplet, one gig RAM, one CPU, $5 a month. My plan before launch is, cool, I'll go through and spin up a larger version of that instance, deploy all the code, this time instead of switching it from one location to another, I loaded up the Splash Page in WordPress and all the other data was behind. So all we needed to do was just go, cool, change the home page from the Splash to the lineup. That way I didn't need to worry about caching and lots of stuff. So that was my plan. Launch Day came the night before, got up super early and I was like, excellent, let's go through this instance, increase it. Logged onto Digital Ocean, shut down the instance, went to increase it to maybe two CPUs, little bit more RAM just to make sure I could handle it. Digital Ocean was having issues and it threw an error. I think that's okay. I've got time. It's probably just a once off. I'll get up in the morning of launch. Up at 6 a.m., same problem. So I've now got a website which is running on the smallest possible VPS that you could buy. I thought, okay, so what can I do? I don't have time to go through and spin up a new instance and then re-point the DNS. On top of that, I don't think I had access to the DNS settings. So by the time the client was up and understood what I was talking about, that wasn't gonna happen because an ounce was at 8 a.m. So I was living on the edge of it. You know what? It'll be fine. We'll see how it goes. So 8 a.m. came around. We went from zero users between seven and eight to five and a half thousand users at 8 a.m. Flicked the switch and I went through a moved, took down the splash page, put up the loading page. Because I was a little bit anxious because I didn't get up the instance, I had the droplet CPU usage, RAM, all that stuff on one screen and then a live feedback of the Google data, page views on the other screen. As I'm sitting there working on it, I can see the users flooding in, you know, 3,000 users on the side of it now, three and a half thousand. I'm starting to feel a bit stressed. Next thing I notice, the CPU starts to spike. It's going up and up and up and finally hits 99%. So I'm sitting there going, oh gosh, grooving the move, year and a half ago, my boss isn't here, no one else is in the office and I'm sitting there quite stressed. I'm like, well, what do I do? How can I go through and fix this? W3 total cache goes through and saves all its settings inside a PHP file. That PHP file is then stored in my Git. So when I go through and deploy my Git, it updates all my cache settings. For development, I turned off my cache. So it took me about 15 minutes to realize that I didn't have any caching on this site for this scale of setup. So I went through, quickly logged on to the production server and I went through and turned on my object cache. And as you can see, it instantly dropped in CPU. Now between nine and 10 AM, we actually had an increase of users. So the fact that it started to drop down to 33% of usage was a really good sign because it meant, cool, by the end of the day, no one's really gonna be visiting this site anymore. And it was holding stable. So we had about 5,800 users. I don't know how many page users there were, but basically throughout the entire day, we had 60,000 users. And I was running a full festival website on a $5 a month instance. All that kind of stress made me realize that the code base that I've been working on for about six months was actually quite stable. And it could be used in a production environment with this sort of stuff. It's really good. That made me feel a lot better. So what's next? So when I was going through and playing with these setups, I was looking at object caching. And I was like, cool, let's go through and put something like a Redis cache in this. Fortunately for Trellis, it's using Ansible Playbooks. So I just went through and said, in the Ansible Playbook, add a Trellis cache. So I went through and just deployed it, installed it, reprovisioned it. And I had installed a Redis cache, which then meant in W3 Total Cache, I go to my settings, turn on object cache, use Redis. Now, I know that maybe not everyone is familiar, but basically it means that it uses your RAM to go through and cache all your objects. So I then had a much, much faster way of caching all this sort of stuff and make it highly available. The next lot of stuff that we want to be able to do as well is put JSON endpoints in. So because I've gone through and created my models, they're extending off that base one, I can turn on my JSON endpoints, which then means that instead of using, for example, a twig templating engine, we could create an API service which I could get my React developer to go through and create a really nice React frontend. We could then use the same data to go through to the mobile apps that the festivals use, because at the moment they have to replicate everything they put on their live site into their mobile site, or I could give that to the mobile app developers and say, cool, here's our JSON endpoints for all the FAQs. This is everything that you'll need and I can go through and do that all within one model and then it'll flow through to everything else we've got. So that's kind of the direction that basically we want to be heading into next and hopefully I'll get a chance to play around with it and hope I'm not crashing any more festival websites. So yeah, if you've got any questions, now's probably a good time to ask them and hopefully I can answer them.