No Compromises

Last episode we discussed how to deal with a "slow app" complaint. This episode we discuss some technical approaches to find and fix the issue.

Sign up for our Laravel tips newsletter!

Creators & Guests

Host
Aaron Saray
Host
Joel Clermont

What is No Compromises?

Two seasoned salty programming veterans talk best practices based on years of working with Laravel SaaS teams.

Joel Clermont (00:00):
Welcome to No Compromises, a peek into the mind of two old web devs who have seen some things. This is Joel.

Aaron Saray (00:08):
And this is Aaron. I remember in the last episode you kind of did... We'll call it podcast bait. Clickbait of the podcast world where you held back all your tips, tricks, and secrets of what you actually do when your application could be slower, how you measure that. We talked a little bit about theoretical stuff but I remember you promising that we were going to cover some more of the details of how the mechanics of this might work out.

Joel Clermont (00:44):
I find it interesting that you put it on me. I think it was a group effort that some podcast bait was offered, but anyways continue.

Aaron Saray (00:53):
All two of us as a group.

Joel Clermont (00:54):
All two of us.

Aaron Saray (00:56):
I thought I'd be good to start out and ask you a question of, we know we need to measure some of this stuff, maybe it is actually going slow. What is the first sort of tool that you might reach for in your Laravel application to start measuring performance or getting an idea about it?

Joel Clermont (01:15):
Yeah. Because last episode we talked in the abstract, some tech techniques and things. But, yeah, the tools, I think that the obvious go-to tool would be Laravel Telescope, having that running in a local environment. We had talked about being able to reproduce the issue previously, so I'm assuming we were able to reproduce the slowness. The very next thing I would do would be open up Telescope and start clicking around to see what it shows me about the performance of that request.

Aaron Saray (01:44):
If you have a page that is going slow, let's just say its slash dashboard, when you say click around in Telescope, what would you actually do?

Joel Clermont (01:53):
Well, the entry point for me or the way my brain works is the requests page. They show you all the requests that came into your app and I would look for the one that has the... They all have a timeframe on them too, in terms of when the request was issued. Obviously it's one I just did now, but also how long it took. Let's say it's a page, like Nova, for example, I was troubleshooting something. There's a number of requests that made up my individual user request, but focusing in on the one that had the longest response time.

Aaron Saray (02:24):
And you say there are a number of requests, that's because the Nova page you're looking for probably has some (age acts 00:02:29) requests as well.

Joel Clermont (02:29):
Yes.

Aaron Saray (02:30):
But if you're in an application maybe that has just standard request, it pretty obvious what request is the one you just made.

Joel Clermont (02:37):
Yeah, a lot of times it's one-to-one. Correct, yeah.

Aaron Saray (02:39):
You click in there and... I guess, what are we kind of looking for in something like that?

Joel Clermont (02:46):
All right. When I'm looking at that list of requests and I find the one that I just made that has that unusually or unacceptably long duration, I'll click the details icon and look at it. What that will show you up at the top, it has a lot of information about the route and things like that. But if you scroll down, usually queries, I would say nine times out of ten, queries, and that's database queries, are where you'll see a problem. If you see double digit query numbers or maybe it's a low number of queries but individual queries take multiple seconds. That is the first thing I would scan for and start drilling in to try to make this better.

Aaron Saray (03:29):
I think there's a couple other sections in there too I've looked at. If you're kicking off a number of jobs, it'll show jobs that maybe have been kicked off by that request. You can drill into that job as well, which normally isn't such a big deal because they should be in the background. But it could be something is misconfigured and maybe that job or that event is happening in real-time. Maybe the queue is set to sync for some reason. Then those things are all going to happen on that request, which I guess is something important too to keep in mind. That if you're doing it locally, you should have your application configured the same. If it's using a Redis in production, you should be using Redis locally. Otherwise, the time it takes those requests are going to be drastically different if you're issuing things like jobs or events.

Joel Clermont (04:17):
I mentioned the queries tab, or the queries portion of the request, and obviously a high number of queries is a problem.

Aaron Saray (04:26):
It mentions duplicates in there too as well, right? Like unique or duplicates?

Joel Clermont (04:30):
Yes, right. So if you see a high number or even a high number of duplicates that would be the place to start. But let's talk about an example where maybe there's not too many queries running, but just one of the queries is slow. What's your advice for how to tackle something like that?

Aaron Saray (04:47):
Any database... Well, most databases that you're using are going to have a way to profile the query that they're running. I imagine majority of people are using MySQL. So you can use the Explain command in MySQL. What that does is it kind of tells you what the engine in MySQL thinks it's going to do to run in this query. Now, I said that very carefully, what it thinks it's going to do. Every once in a while, it might do something a little bit different than explained but those are pretty rare so we're not going to get into that. But 95% of the time, when you say, "Explain this query," it's going to explain to you what it's going to do. How you can do that is in Telescope, you can take that query that's taking a longer amount of time and you can go to your database management tool and put the word 'explain' and then paste the query in after that. Making sure that either the parameters are filled in and things like that. Then you can actually see MySQL Explain what it's going to do. Now, that's a huge topic and people have done whole presentations on there. But just a thing to keep in mind. The number of rows that it lists that it has to review to do your query, multiply those by each row. If the first one says 10 and the second one says 10 that means that it has to review a hundred rows in order to get your information. You imagine if the first one says 10,000 and the second one says a million and the third one says 60,000, that's a pretty big number. Basically, the simplest way to kind of explain this is MySQL has to kind of make a result set that is sort of all of those rows and then filter those to your criteria. So that could be a reason why it's going slower. One of the things you'll see in there as well is it'll indicate whether or not it's going to use an index on this query and that can be a common thing as well. You get really excited, you start making your data models and migrations, you start putting in data. Especially if you're local, maybe you don't have a lot of data so everything seems fast. And it goes out to production and things are going slower and then you do something that just doesn't see... There's no index on there so that might be a time to add index too. And that might make a huge difference on your performance of that page.

Joel Clermont (07:11):
Yeah It could be eye-opening, how dramatically faster a query will be with the right index. I mean, just night and day... We're not talking like, "Oh, it's going to be 5% faster." I mean, it might go from taking 10 seconds to being done in like 35 milliseconds. I mean, it really is a good place to start when looking at this.

Aaron Saray (07:31):
I think we should be responsible though and kind of say that you can't just add an index to every single column in your table. There is some give and take, there's some expense, some data and speed that happened in different parts of the application and usage of adding indexes. So don't just add them to every single column. Add them in cases where you need them explicitly.

Joel Clermont (07:53):
Yeah. It's not like if some is good, more is better.

Aaron Saray (07:57):
Right.

Joel Clermont (07:57):
There should be a reason for adding it, for sure. Explain is an excellent tip and it's a pretty deep technical area, so when you first... If you've never run that command before, first time you run it don't be surprised if you don't understand most of what you see. But just know it's a tool and there's some great blog posts and information that will help explain Explain. But now that we've tackled the first couple things we looked at, let's... I remember somebody saying they like rules of three, so I thought maybe if you had a third area to explore that we could tackle that.

Aaron Saray (08:34):
Well, I know that all of my database calls and eloquent queries are tip-top. They are the best, they can be no more efficient but it still seems to take a little bit of time to run some of these. So that's kind of when I then step into the final thing is caching. We can use Laravel's caching tools to cache various different parts of our database queries or really anything that is intensive. The answer to a complicated request, a calculation, a third party, anything like that request we can put in our caching system. I think it's important to understand too that you can mix this up in various different ways too. A lot of times we think of all caching, well will this still be good in an hour or a day or something like that? That's fine, that's great. Some things don't change that often. Other things might change on average just once a minute, so you got to be careful when you're writing your caching rules. First of all, does it even make sense if something is going to change that often? Second, then don't keep the cache time out too long or users are going to get things that they don't understand. Like, why is this an old set of data or whatever? I've heard developers say, "Well, they won't care," or whatever. But users do care and do not understand why does this say something's old? Or, why is it the old spelling when I just changed it or whatever, something like that. You really have to be careful for those similar scenarios.

Joel Clermont (10:09):
Well, you're also saving yourself some trouble. I know you and I were talking through a cache related issue earlier today and you just pointed out that if somebody saw this old data, it might cause a user to report a support issue. And you go to look at it and you're like, "Why can't we reproduce this?" And caching is making that a lot harder as a developer to reproduce it, so you're doing yourself some favors as a developer by tuning those values just right.

Aaron Saray (10:36):
Yeah, I think that's important too. That's the reason why we cover these topics in this order. I've noticed a lot of people will maybe have bad set of queries, so they just throw cache on it instead. "Well, it's only going to run once and we'll cache it." I really think the way to do this is to identify theoretically, like we talked about last episode. Then to identify with hard facts using something like Telescope and then to go and optimize the things that are painful or expensive. And then when we got past that, then we can go and introduce caching and other layers like that that keep copies of our data even past when it might be valid.

Joel Clermont (11:16):
Right, that's a nice little summary. There's your checklist when next time somebody complains something is slow, just come back and listen to this and you'll be in a good spot.

Aaron Saray (11:32):
Maybe I had a little extra time last night to think about life in general. And I started thinking to myself, "What are things in my life that are maybe more embarrassing than my browser history?" Examples I've come up with are my calculator history.

Joel Clermont (11:49):
Okay.

Aaron Saray (11:51):
Like, what numbers did I not know how to add? Its like, "Really, you couldn't figure out 10% of 100?" [inaudible 00:11:58].

Joel Clermont (11:58):
Okay, all right.

Aaron Saray (11:59):
I think maybe the number of shirts I have compared to the number of shirts I actually wear.

Joel Clermont (12:05):
What's the ratio there?

Aaron Saray (12:07):
An embarrassing amount is what I'm saying.

Joel Clermont (12:10):
Okay.

Aaron Saray (12:11):
I would say it's definitely in a double digit ratio. And maybe this isn't such a big thing anymore, but maybe the number of songs or number of movies that I've purchased compared to the ones I actually watch on regular. When we used to purchase a lot of music before streaming, I'd buy a new album, I listen to two songs and I'd go back to my old three albums I listen to all the time. Same thing with movies. I've bought a lot of movies but I never seem to go back and watch them. I'm always watching the newest thing. My movie collection is actually embarrassingly large now. So I was wondering, do you happen to have any things that maybe are more embarrassing to you than what I assume is a pristine browser history on your machine?

Joel Clermont (12:54):
Oh, do I have things to share? The immediate one that came to mind for me was video games. Because I'll get excited like, "Ooh, this new game is coming out." I'll pre-order it, it'll come out on release day and I might not open it for months and it might not even be me that opens it. It's like one of my kids asks to play for it. The most embarrassing purchase-

Aaron Saray (13:16):
I know you mentioned that. You got a package for the switch or something and you had to hide it, and I was like, "How do you not open a package right away?"

Joel Clermont (13:23):
No, it'll just sit there. The worst example I can think of this in recent history is I bought a game, never opened it and wanted to play it and actually forgot I bought it and bought a second copy. My son is like, "Don't we already have that game?" And sure enough we did, so that just shows how much time had elapsed between when I thought I needed it and when I actually got around to playing it.

Aaron Saray (13:49):
You probably have a pretty cool looking eBay account then. A lot of nice things for sale, never opened-

Joel Clermont (13:54):
I should.

Aaron Saray (13:54):
... for real though.

Joel Clermont (13:56):
I should but I still have them sitting on a shelf. But thankfully, I justify it when I have kids that play the games. But the worst is you pay $60 for a game when it comes out, but by the time you play it, they're selling it for like $25 and it's like, "That's $35 I wasted." Very embarrassing. Something else I thought of that's not media related is, I'll want to try out some new recipe and maybe it calls for an ingredient that I don't have on hand, it's not something I use a lot. So I'll buy that ingredient-

Aaron Saray (14:29):
Like potatoes?

Joel Clermont (14:30):
Not like potatoes. No, potatoes are pretty regular in this house. Something like spice or unusual ingredient that is a small component of a recipe but is important. I'll buy that and then it'll sit in the fridge or on the shelf until it goes bad. Then like three years later I'll need that same thing again. There's quite a few ingredients like that, where you buy them, you use them one time and then they just sit there and you look at them.

Aaron Saray (14:58):
Next time you go to use it, it's expired by three years? Oh man.

Joel Clermont (15:01):
Mm-hmm (affirmative), pretty much. Yeah exactly.

Aaron Saray (15:05):
During the podcast, sometimes we talk about code. But it's much easier to kind of see it written down. In cases like that...

Joel Clermont (15:12):
Check out our newsletter. You can sign up for it on our website at nocompromises.io/tips.