Summarized using AI

Level Up Performance With Simple Coding Changes

David Henner • September 27, 2024 • Toronto, Canada • Talk

In the video titled "Level Up Performance With Simple Coding Changes," David Henner, at the Rails World 2024 event, shares effective Ruby techniques employed by Zendesk to enhance performance in Rails applications. Henner emphasizes that despite having no dedicated performance team for the first 12 years, small changes can yield significant improvements in application efficiency and resource usage.

Key points discussed include:

- Low-Hanging Fruits: Henner explains how many areas for improvement exist if performance tuning is overlooked, particularly due to the accumulation of outdated practices over the years.

- Observability and Data Tracking: He highlights the importance of using tools like DataDog to monitor application performance and identify bottlenecks. By integrating custom tracing methodologies, Zendesk managed to understand their traffic better and optimize queries accordingly.

- Archiving Strategies: Henner stresses the need for effective data management, advocating for archiving irrelevant data to reduce load on the main data store.

- Caching Techniques: He describes various caching mechanisms, such as rewriting queries to leverage memory storage effectively and using the max-age header for traffic management.

- Query Optimization: Henner introduces testing the number of SQL queries run during processes and optimizing data retrieval practices to minimize response times in applications.

- Avoiding Performance Pitfalls: By replacing common coding patterns with more efficient alternatives (e.g., using pluck for data retrieval), Zendesk developers managed to significantly improve application performance.

- Incremental Improvements: By making numerous small changes over time, significant overall performance gains can be achieved, leading to substantial time and cost savings.

Throughout the presentation, Henner provides examples such as the implementation of a caching strategy to manage ticket fields and how simple syntax alterations led to major performance advancements. He emphasizes the value of careful coding practices and the rigorous testing of performance implications in all development work.

In conclusion, the video presents essential coding strategies for Rails applications, demonstrating how simple adjustments can lead to increased efficiency, reduced costs, and enhanced customer satisfaction. The importance of continuously monitoring and refining performance practices is a critical takeaway for developers and teams focusing on scalability and efficiency.

Level Up Performance With Simple Coding Changes
David Henner • September 27, 2024 • Toronto, Canada • Talk

David Henner highlights some of the major improvements Zendesk has achieved using straightforward #Ruby techniques to get even better performance out of Rails, including data which illustrates how they saved thousands of years of processing time annually, leading to increased customer satisfaction and cost-effectiveness, as reflected in their AWS bills.

#railsatscale #rails #scaling

Thank you Shopify for sponsoring the editing and post-production of these videos. Check out insights from the Engineering team at: https://shopify.engineering/

Stay tuned: all 2024 Rails World videos will be subtitled in Japanese and Brazilian Portuguese soon thanks to our sponsor Happy Scribe, a transcription service built on Rails. https://www.happyscribe.com/

Rails World 2024

00:00:10.360 uh first we're going to start this
00:00:11.480 everybody in the uh the front row I want
00:00:13.759 you to all say hi to my mom because she
00:00:15.799 needs a picture everybody ready three
00:00:18.720 two
00:00:20.000 one there we go thank you all right
00:00:23.160 mom's going to be happy all right my I'm
00:00:25.320 me I'm my name is David hner um I'm
00:00:28.599 going to be talking about performance
00:00:29.679 today
00:00:30.920 um and a lot of people have asked me in
00:00:32.759 the last couple days well this Tech talk
00:00:36.040 more geared towards uh junior people or
00:00:38.280 more senior people I've been telling
00:00:40.039 people like uh it's probably more Junior
00:00:42.719 but now that I think about it I've seen
00:00:44.000 a lot of your senior code out there too
00:00:46.199 and you might not you might know what I
00:00:51.039 am going to present but you don't
00:00:52.840 probably do what I present so it might
00:00:55.320 be a reminder for those senior people
00:00:57.280 but oh I need the uh clicker and and so
00:01:00.600 when I started this talk also
00:01:03.559 um I wanted to show that my face was the
00:01:07.479 same size as errand and that ear is the
00:01:09.520 saying by the way but uh and I wanted to
00:01:12.119 start with a whole bunch of little jokes
00:01:14.000 but then I realized oh I don't have 45
00:01:16.200 minutes to talk I have 30 minutes to
00:01:18.360 talk oh no I'm not going to
00:01:20.920 actually want to get kicked off stage
00:01:22.880 because I think somebody would come up
00:01:24.119 here and maybe uh well tackle my
00:01:28.439 ass anyways so I'm going to start by
00:01:31.159 giving you a little history of zenes
00:01:33.399 zenes has been around about 15 years or
00:01:35.560 so we've had the first 12 years almost
00:01:38.560 no dedicated performance team is
00:01:40.640 actually working on the code so you can
00:01:42.159 imagine there's a lot of low hanging
00:01:43.600 fruit and one of the things I need to
00:01:45.680 talk about uh zenes is our end points
00:01:48.240 aren't what uh I'm guessing they're
00:01:50.240 dirty like some of yours also where our
00:01:53.680 restful one points aren't really restful
00:01:56.039 and by that for example for our uh code
00:01:59.039 base we have an includes user in the
00:02:03.439 ticketing endpoint or includes comments
00:02:06.119 so our actual endpoint will change the
00:02:09.800 response depending what the C user asked
00:02:12.040 for so it's technically not a restful
00:02:14.560 response there's many different
00:02:16.040 responses that you might get depending
00:02:18.040 on what the includes are that's going to
00:02:20.200 become really important at the end of
00:02:21.800 the talk because all of a sudden we came
00:02:23.400 up with ways of caching that craziness
00:02:27.200 at the end um
00:02:30.319 anything else yeah and we've diverged a
00:02:31.760 lot from Ruby on rail so upgrading is a
00:02:33.599 pain but here's the work that my team
00:02:36.280 can do and this is just three people um
00:02:38.560 and this is P99 data which means it's
00:02:41.239 the worst custo or the customers the
00:02:42.840 biggest customers are and you can see
00:02:45.360 all these Trends you can see when we
00:02:47.959 released code every one of
00:02:50.640 these big staggering uh code changes are
00:02:54.200 actually making a huge difference on our
00:02:55.879 infrastructure cost don't believe I'm
00:02:57.599 allowed to tell you how much our
00:02:58.800 infrastructure costs uh was benefited
00:03:02.080 but think of it more on the tens of
00:03:03.680 millions as opposed to 50,000 or
00:03:06.120 whatever not we so we saved three people
00:03:09.360 and with the help of the infrastructure
00:03:11.440 team you got huge benefits here uh so
00:03:14.680 archiving um a lot of uh people just uh
00:03:18.599 they always have a small app in the
00:03:19.720 beginning they're like okay I'm just
00:03:21.440 going to keep all my data in the main
00:03:24.440 data store whatever that is usually my
00:03:26.000 SQL and they never even think that uh
00:03:29.000 this data is stale after a certain
00:03:31.280 amount of time you should probably have
00:03:33.319 an archiving strategy and in fact uh
00:03:36.239 like a complete removal strategy of data
00:03:39.080 and bring that up when you start your
00:03:41.280 applications because bringing it up
00:03:43.120 later is harder and basically be able to
00:03:44.760 tell your customers hey we're not going
00:03:46.200 to keep your data let's say five years
00:03:48.159 and then for archiving for the Zen desk
00:03:50.799 after a ticket is closed most people
00:03:52.680 don't care about the ticket after a
00:03:54.000 certain amount of time so after a
00:03:55.640 certain amount of time we'll say you
00:03:57.280 know what we're not going to keep that
00:03:58.280 in the MySQL data store we're going to
00:04:00.280 uh put that we do it in Dynamo but
00:04:02.319 whatever store you might want to put and
00:04:04.239 then that will save your application
00:04:06.319 from having to make SQL queres on just a
00:04:08.400 huge amount of data
00:04:10.120 set um so how did we get here we did not
00:04:15.120 have a PM when we started uh this team
00:04:17.239 just three years ago which actually
00:04:19.959 probably was for the better because we
00:04:22.400 let the data lead us as opposed to let's
00:04:24.320 have this Grand project and there's just
00:04:26.160 so many little things you can do within
00:04:27.840 your app to actually get this your app
00:04:29.759 app to actually perform well but the
00:04:32.199 first thing you really need to do in
00:04:34.479 that effort is ADD observability at
00:04:37.680 zendesk we use data dog I'm sure many of
00:04:40.120 you actually use data dog and uh
00:04:42.840 probably are complaining about the cost
00:04:44.360 but um so here's what a typical Trace
00:04:47.160 that we had before we started the team
00:04:49.080 would look like and we actually added a
00:04:51.120 lot of little details in here any place
00:04:52.880 that you see uh anything yellow below
00:04:56.360 the purple um is stuff that we added
00:05:00.080 custom but the problem with this data is
00:05:03.000 it's still left a lot of empty spaces
00:05:05.720 but to fill in all that data would just
00:05:07.520 cost a lot of money because data dog
00:05:10.479 charges for the amount of uh the the
00:05:13.400 amount of traces you actually put in
00:05:14.800 there so we need some flexibility and
00:05:17.120 I'll go into the flexibility that we
00:05:18.800 added and then um essentially these data
00:05:22.240 dog traces that we actually do do add
00:05:24.520 are work in
00:05:27.120 progress new features will come up you
00:05:29.759 need to add more um traces new areas
00:05:34.720 that become slow in the application for
00:05:36.840 some P99 customer add more traces so you
00:05:39.600 never can say that you're done but the
00:05:41.600 adding this uh observability was
00:05:43.720 absolutely crucial to find the problems
00:05:45.199 that we were doing so how did we do that
00:05:48.440 here's an example method it's actually
00:05:50.120 not a real method in our application but
00:05:51.800 you'd have a new ticket method for
00:05:53.360 example and this is just a syntax I'm
00:05:55.960 really showing you here but the syntax
00:05:57.840 is essentially you have a a method
00:05:59.960 called rapy dogging Trace you pass in
00:06:03.080 new ticket and new ticket is the uh
00:06:05.680 method name and then the second new
00:06:07.639 ticket uh parameter is actually what you
00:06:09.800 want to call it that Trace in data dog
00:06:12.199 so you might not want the same um Trace
00:06:15.479 name and the last one's really just a
00:06:17.120 color coding we mostly use the same
00:06:19.360 color coding for anything custom that
00:06:20.759 we're doing but uh you can add some
00:06:22.800 color coding so here's the syntax for
00:06:26.199 the block notation that we have and
00:06:29.960 exactly what you see it's just basically
00:06:31.479 a block where you and you add the low
00:06:34.080 method in between the problem with this
00:06:35.840 method is that it clutters up your code
00:06:37.560 you don't want blocks all over your code
00:06:38.880 so the other me uh uh way to actually
00:06:42.919 add traces is better but then let's
00:06:44.840 actually dive more into the actual
00:06:47.160 implementation of this so here's here
00:06:49.800 what we have is the block notation uh
00:06:53.240 implementation so what what you do is
00:06:56.080 the first couple lines there is a
00:06:58.599 software switch we call our software
00:07:00.360 switch uros none of you call your
00:07:02.319 software switch uros anyways essentially
00:07:04.879 you see if you have the feature if you
00:07:06.680 have the feature then essentially what
00:07:08.240 you're doing here is wrapping a trace
00:07:09.639 around your implementation if you don't
00:07:11.960 then you just yield to the block and it
00:07:13.199 just renders like normal and you it's
00:07:14.720 almost like the block is not even
00:07:18.960 there likewise um for WAP de debugging
00:07:22.960 traces which was the implementation that
00:07:24.960 I showed can I go fast yeah right here
00:07:28.720 for RDW uh debugging traces this is what
00:07:31.759 we do essentially what we doing is you
00:07:34.479 redefine the method that you're actually
00:07:36.360 wrapping the the block around so you
00:07:38.520 have the same code that the the block
00:07:40.840 notation in within this and you redefine
00:07:43.720 the method that you're trying to
00:07:44.960 actually uh put traces in and then you
00:07:47.520 call that method within this block with
00:07:49.720 a syntax here
00:07:52.000 um so what you get before is traces that
00:07:55.919 look some somewhat like this and I need
00:07:58.280 a drink CU my water
00:08:03.639 and uh which is gives you a good amount
00:08:05.800 of data but then after you get something
00:08:07.919 like this where you can dive right down
00:08:09.960 into the actual problem you can find the
00:08:11.840 actual method that if you zoom into this
00:08:14.159 you'll see something like this where all
00:08:15.960 of a sudden you can go into merge ticket
00:08:18.000 data in model that's an actual method in
00:08:19.720 our implementation and like uh purple
00:08:22.879 thing on the bottom we know that's a
00:08:24.560 query that query is happening uh each
00:08:26.520 time you call that is it n plus1 do you
00:08:28.479 want to cach that data do you is it an
00:08:30.680 okay query that you expect well now we
00:08:33.839 could have ways to debug every query
00:08:36.360 know exactly where they're coming from
00:08:37.599 and know how to actually remove
00:08:41.080 it the other thing you have to know is
00:08:43.399 as I said earlier these traces are not
00:08:45.600 free this block on the bottom is not one
00:08:47.640 Trace it's actually 5,000 it's just
00:08:50.680 they're so damn close together it looks
00:08:52.160 like one now ironically enough the
00:08:55.120 latency added to 5,000 extra traces with
00:08:57.959 data dog data dog is amazing it does not
00:09:00.160 actually give you much extra latency
00:09:01.880 that is even noticeable that being said
00:09:04.519 I've released code that I'm not so proud
00:09:08.079 of that added 200,000 extra traces it's
00:09:11.839 only a little while we only turn the
00:09:13.200 software switch on for a little while
00:09:15.079 but uh that was code a loop within a
00:09:17.680 loop within a loop I think they call it
00:09:19.160 n cubed or something like that where we
00:09:22.279 got 15 seconds extra response times and
00:09:25.320 we turned it off quickly but uh and it
00:09:27.680 was easy to find the problem at that
00:09:28.920 point because like oh
00:09:31.760 but yeah don't do that um I'm going
00:09:35.720 to move on to some testing that we do um
00:09:38.800 we one of the things I don't see in a
00:09:40.920 lot of applications is testing for the
00:09:42.440 number of queries we have a very
00:09:44.279 specific sent uh syntax at zenes but
00:09:47.079 there's a whole bunch of Open Source
00:09:48.959 gems that you can actually test the
00:09:50.440 number of queries that you have in your
00:09:51.880 application it's really important for
00:09:53.880 things that you especially expect no
00:09:55.399 queries from but whoa uh but could have
00:09:59.399 queries and the syntax is something like
00:10:01.680 this where you have assert SQL queries
00:10:04.240 you pass in the number of SQL queries
00:10:05.680 you want and then you pass it to Rex it
00:10:08.120 works really really well but we actually
00:10:10.240 do this for memorization also put this
00:10:12.880 in your memorization code you say hey
00:10:14.760 the first time I expect a query the
00:10:16.079 second time it better not have a query
00:10:17.640 and if somebody else changes your code
00:10:19.440 like 10 years from now or what something
00:10:21.200 like that um they're going to actually
00:10:23.200 have a failing test and yes that does
00:10:26.040 happen um and I'm going to I'm going to
00:10:29.640 keep moving cycling through a whole
00:10:31.079 bunch of topics this is another one
00:10:33.279 where a lot of you go oh I want to I
00:10:35.800 want to have do some feature that has
00:10:38.480 some dramatic differences in data dog so
00:10:41.279 I can show my boss what I did for work
00:10:43.680 yeah that's nice but you know what
00:10:45.480 there's end points that we have in our
00:10:47.279 application that get 87 billion I think
00:10:49.839 it's more like 10 and something billion
00:10:52.000 requests per year Well if you you
00:10:54.240 multiply 87 billion Time 1 millisecond
00:10:57.200 you're saving a, days worth of sequel on
00:11:01.360 your machines sure it's not going to
00:11:04.000 look really great to your uh uh boss
00:11:07.000 because he's going to be like one
00:11:07.959 millisecond really but to your
00:11:10.519 infrastructure team they're going to say
00:11:12.160 God bless
00:11:13.920 you
00:11:15.519 um here's something that we do at zenes
00:11:17.959 that I don't think we most people have
00:11:19.519 seen before I don't know but uh anyways
00:11:22.120 we have something our account object is
00:11:24.240 actually what I would call a god object
00:11:26.200 you can call it a Singleton but we don't
00:11:27.839 follow the exact Singleton patterns so I
00:11:29.920 call it a god object because it is God
00:11:32.480 what that means is if you call for the
00:11:35.279 account any place in the uh zenes
00:11:38.839 whether it's user data account ticket
00:11:40.959 data account current account in your um
00:11:44.480 controllers wherever you call it the
00:11:47.320 object is the exact same object it's not
00:11:50.440 just account. ID is the same it's the
00:11:53.320 same object ID which allows you to
00:11:55.760 leverage that and take that object and
00:11:58.279 now you can use that object object
00:11:59.839 anywhere and memorize on top of that
00:12:02.279 object instead of the object that you're
00:12:03.639 getting from someplace else that there's
00:12:05.120 a Code implementation way over here and
00:12:06.920 then there's a Code implementation way
00:12:08.440 over here but you can't like figure out
00:12:10.920 how to actually memorize the the two
00:12:13.760 because they're completely different uh
00:12:15.279 pieces of the code um anyways I'm going
00:12:18.240 to be going into this quite a bit if
00:12:19.760 you're not uh familiar with memorization
00:12:21.560 like this the syntax looks like this if
00:12:23.880 you need help on that you're probably
00:12:25.320 totally confused so I hope that uh you
00:12:28.040 understand how to memorize very simply
00:12:30.320 um here is an imp implementation of um
00:12:34.880 like that we don't uh we didn't uh do
00:12:38.880 any type of uh memorization where we
00:12:41.120 just Loop through a whole bunch of
00:12:42.480 audits we'd call audit. author and then
00:12:46.279 author. user emails most of the time the
00:12:48.480 author was the same author not the same
00:12:50.519 object ID but the same I like the same
00:12:52.320 exact author we called user emails on it
00:12:55.320 and we' get anywhere from one to three
00:12:57.079 queries every time we called this even
00:12:59.560 though it was the same damn author every
00:13:01.079 time our implementation is a lot
00:13:03.160 different than this but this is the
00:13:04.279 condensed
00:13:05.639 version well if you actually leverage
00:13:08.199 the fact that you have that account
00:13:09.600 object that is the same account and you
00:13:12.000 put the user emails method on the
00:13:14.880 account object and just pass in the user
00:13:17.680 well now all of a sudden you can
00:13:19.079 memorize that on the account and if you
00:13:21.079 actually have to use user emails any
00:13:23.639 place in your request cycle you actually
00:13:26.360 get a memorized result all after the
00:13:28.839 first
00:13:32.040 time changing topics again a lot of
00:13:35.160 people in Ruby and rails they're always
00:13:36.880 getting back arrays from their data sets
00:13:40.199 makes sense great but the problem is
00:13:43.720 sometimes an array is not the right
00:13:45.240 thing to use at what for what you're
00:13:47.880 doing and yes in this case there's only
00:13:50.880 three things in the array but what Let's
00:13:52.600 Pretend This is not three uh uh object
00:13:55.600 in your array Let's Pretend There's
00:13:57.560 20,000 then you do some C calculation
00:14:00.519 and then you call Unique well unique is
00:14:02.959 not a free uh uh method when you have a
00:14:06.639 huge array so this is going to create
00:14:08.440 some time in certain some in certain
00:14:10.480 cases in fact in ours it did cause let's
00:14:13.120 say 100 milliseconds of time I don't
00:14:14.480 know what it caused but anyways if you
00:14:16.959 change this to go create a set and then
00:14:20.639 do that same sum calculation but now
00:14:23.600 actually keep on adding to the set every
00:14:26.720 you're guaranteed uniqueness the end of
00:14:29.560 that this set by definition is going to
00:14:31.720 be unique so now instead of actually uh
00:14:34.839 calling the unique method and spending
00:14:36.600 time 100 milliseconds on that you're
00:14:38.440 just iterating through and you get
00:14:40.480 unique values and you don't have to even
00:14:42.000 call Unique at all likewise uh with
00:14:45.680 leveraging a hash I think this is more
00:14:47.519 obvious but it's something that I just
00:14:48.880 want to go through is basically an array
00:14:51.519 you can use a hash and instead just look
00:14:54.079 for the key you have an O of N1 uh uh
00:14:58.240 operation or L of one operation actually
00:15:00.480 constant time to look up for the three
00:15:02.680 and I just have nil here but you could
00:15:04.160 actually have some calculation three
00:15:05.560 equals something and uh your operation
00:15:08.720 is just going to be so much faster
00:15:10.120 because you're leveraging a
00:15:11.680 hash uh I'm adding this also uh flat map
00:15:15.880 versus m flat flatten it's literally
00:15:18.279 because every application I've ever
00:15:20.480 opened up uses map. flatten all over the
00:15:24.440 place when you could use flat map I'm
00:15:27.079 not going to go into the the differences
00:15:28.639 of the to but you do have to know that
00:15:30.079 there is differences but every uh
00:15:32.000 Implement every implementation of map.
00:15:35.120 flatten that I've ever seen can be
00:15:37.920 replaced with flat map I've never seen a
00:15:40.040 case where it hasn't at least in my
00:15:42.519 applications more
00:15:48.120 water so at zendesk we also we obvious
00:15:52.319 well anybody knows zenes is we get a ton
00:15:55.160 of emails coming in for customer tickets
00:15:58.160 in those emails
00:15:59.440 that data
00:16:01.959 is if you've ever looked at emails it
00:16:04.519 can be and you get you get in the
00:16:08.160 emails and there's one customer this
00:16:12.759 whoever did this was just stupid but
00:16:14.639 anyways One customer they had an email
00:16:17.240 signature and at the end of their email
00:16:19.360 signature was 36 at least
00:16:22.160 36,000 empty
00:16:24.240 spaces and our re XX is for whatever
00:16:27.759 reason were run on the out outbound and
00:16:29.319 it made sense to run out the x's on the
00:16:31.399 outbound the problem is our R XS were
00:16:33.480 extremely slow with
00:16:35.639 spaces and well this customer is
00:16:37.759 complaining why am I getting bad slow
00:16:40.040 responses well Jesus Christ
00:16:43.519 what anyways this is not our
00:16:46.160 implementation exactly because we had to
00:16:48.000 take care of uh tabs we had to take care
00:16:50.240 of uh new characters we had to take care
00:16:53.440 of returns um but essentially you can
00:16:57.480 just run a simple sanitizer to some
00:17:00.120 degree whatever your data you're trying
00:17:01.440 to sanitize run it on the inbound of the
00:17:04.880 actual data coming in rather than the
00:17:06.559 outbound and it saves
00:17:09.760 time uh give a pluck so let's picture
00:17:13.240 you have a form that form has a whole
00:17:15.760 bunch of fields and some of those fields
00:17:17.720 are drop- down fields and all those drop
00:17:19.760 down Fields have options so Le let's say
00:17:23.520 oh so they all have options typically a
00:17:26.439 drop down field should have let's say a
00:17:28.120 100 or less in your options but we give
00:17:31.520 our customers a tool and when you give a
00:17:34.720 customer a tool some customers want to
00:17:36.520 shoot themselves in the foot and yes we
00:17:39.000 do have limits in this but at the end of
00:17:40.600 the day some people go way over the
00:17:42.240 limits and I'm talking like 20,000
00:17:44.520 fields in your actual um dropdowns our
00:17:48.799 problem is originally when we took got
00:17:50.799 the data from uh the the options we
00:17:53.600 grabbed the data and we just
00:17:56.159 instantiated 20,000 active record uh
00:17:58.880 objects what can go wrong well a lot and
00:18:02.120 essentially what we did did to change
00:18:04.480 this is we changed this and we actually
00:18:06.880 used the pluck this isn't exact pent be
00:18:09.159 too long for the field but what we did
00:18:10.720 is uh got that data in pluck used to
00:18:13.799 convert it to a hash well when you
00:18:15.600 convert it to a hash that's just so much
00:18:17.600 faster than instantiating 20,000 active
00:18:20.080 record uh objects and then the presenter
00:18:24.400 before it looked like this it was very
00:18:26.320 readable it was active record after it
00:18:28.840 looks like this it's not as readable but
00:18:31.360 it's pretty damn readable and if you're
00:18:33.640 going to save let's say 500 milliseconds
00:18:36.240 on some of the responses it's hella
00:18:39.240 worth it um one of the things you're
00:18:42.159 probably not asking but when I gave this
00:18:44.120 talk and internally they were asking uh
00:18:47.000 what is this ticket field store so in
00:18:49.400 ticket the ticket field store in uh our
00:18:52.400 application well it looks like just like
00:18:54.679 an object that we store ticket fields in
00:18:57.080 well one of the things you got to know
00:18:59.039 zendesk our ticket life or request cycle
00:19:02.559 you go in and you let's say you have in
00:19:04.640 the beginning uh some validations in the
00:19:08.000 middle you do something that we call
00:19:10.159 triggers and in the end you have a
00:19:12.600 presenter well each one of them needs a
00:19:14.600 different subset of uh ticket Fields the
00:19:18.200 beginning you need maybe all the ticket
00:19:20.360 fields in the middle you need a subset
00:19:22.039 of ticket fields in the end you might
00:19:23.480 need all of them again or some some
00:19:25.159 combination of the above well what we
00:19:27.440 did is hey let's create a ticket for
00:19:30.559 store we know the the account object is
00:19:33.400 this SC object that I was talking about
00:19:35.240 we can memorize it and we can reuse
00:19:37.679 these this data throughout the request
00:19:39.480 cycle so what you have in the beginning
00:19:42.520 is that that you're using one subset of
00:19:46.360 the data another subset of the data
00:19:49.000 another subset of the data and at the
00:19:50.760 end of the day like you're making one
00:19:53.039 query for all that data instead of 1 2 3
00:19:57.840 instantiating them objects because that
00:19:59.520 ain't free either and you're saving a
00:20:01.280 hell of a lot of time and if you want to
00:20:03.760 take that one step further you can
00:20:06.360 actually leverage active support memory
00:20:08.080 store I wish I actually had time to go
00:20:10.600 with the implementation of this but
00:20:12.440 active support memory store what you can
00:20:14.440 do is now when you make that uh initial
00:20:17.440 call you actually check to see if you
00:20:19.600 cach this up already the very because we
00:20:21.960 never change or very rarely change
00:20:23.760 ticket fields we can actually store that
00:20:25.559 in memory and if you store that from
00:20:27.480 memory instead of actually making a call
00:20:29.520 in the beginning in the middle in the
00:20:31.440 end we never make a call we just get it
00:20:33.520 from memory every time we actually make
00:20:35.320 the request no requests throughout the
00:20:36.720 whole life life cycle for um many of our
00:20:39.720 implementations of ticket
00:20:42.559 fields that is technically the code I
00:20:45.320 don't have time to go through that but
00:20:47.039 and it wouldn't make sense unless I
00:20:48.280 actually added a heck a lot more code if
00:20:50.159 it was a 45 minute talk
00:20:52.320 though anybody in a t anyways um I'm
00:20:56.240 going to move on to max age if you're
00:20:58.960 you may or may not be familiar with max
00:21:00.600 age but the syntax in the Ruby code is
00:21:02.960 something like this where you say
00:21:04.120 expires in 60 seconds if you don't know
00:21:06.200 what this is this is really important
00:21:09.360 because I'm going to go back here this
00:21:12.400 is the amount of traffic we reduced
00:21:14.559 after implementing uh max age what max
00:21:18.159 age does is it's if your uh browser
00:21:21.240 makes a request let's say it request the
00:21:23.520 tickets and then you send back the
00:21:25.600 tickets and expires in 60 Seconds uh is
00:21:30.080 set in rails when you make the call the
00:21:32.400 second time your browser stops the call
00:21:34.440 and says hey YY yo you asked for this 60
00:21:36.640 second be within 60 seconds I'm just
00:21:38.760 going to give you the same data back so
00:21:40.919 the fastest traffic you can ever have in
00:21:42.679 your application is the traffic that
00:21:44.919 never hits your application so that was
00:21:48.520 a big win um last and oh my god I've
00:21:52.559 went so freaking fast did you add
00:21:56.240 time either that I'm just stocking SP
00:21:58.760 fast but anyways um stale stale in rails
00:22:02.679 is amazing if you're not uh if you don't
00:22:05.840 know what it is well I guess I have time
00:22:08.360 to explain it but anyways um stale
00:22:11.120 unfortunately because of the reasons
00:22:13.559 what I was talking before uh does not
00:22:15.840 work on our application because what
00:22:17.279 happens is stale is supposed to see if
00:22:19.400 the E tag that you that's sent in is
00:22:21.919 stale and if it is if if it's if you're
00:22:25.640 using the same an e tag that is not
00:22:28.600 stale you don't render what you do is
00:22:31.760 active record takes or the uh the
00:22:34.799 implementation um takes Brands and says
00:22:37.880 oh you know what this is uh not stale
00:22:40.159 data so it doesn't render that it goes
00:22:42.159 back to the middleware and then uh
00:22:45.919 converts B I'm saying this wrong anyways
00:22:49.440 at the end of the day in the middleware
00:22:51.480 what happens is the 200 that response
00:22:54.840 returned here is changed to a 304 and
00:22:57.520 what what 304 means is no content is
00:22:59.799 sent back so you didn't need to actually
00:23:01.200 render anything to the customer which
00:23:03.240 saves you a lot of time unfortunately
00:23:05.559 that does not work at zenes um I kind of
00:23:08.559 gave a little bit of a history but
00:23:10.120 anyways um so the history of zenes the
00:23:13.279 beginning uh or essentially this is what
00:23:16.120 middleware looks like and your
00:23:17.400 controller looks like typically the
00:23:19.279 stale method is called here which means
00:23:21.360 all that at time at the end does not
00:23:23.960 have to be actually used you return
00:23:26.200 really fast from your responses
00:23:27.880 unfortunately
00:23:29.000 at zendesk we have to calculate the eag
00:23:31.440 value at the very end of the response
00:23:34.159 because depending on the parameters that
00:23:36.720 were passed in we have no idea what the
00:23:39.320 actual output will be so we're looking
00:23:42.240 at the output and seeing what the eag
00:23:44.000 would value be but all we spent all this
00:23:46.200 time yeah sure you get less traffic in
00:23:49.159 the um um
00:23:55.240 water you get less traffic over the wire
00:23:57.679 going back to your customers but you're
00:23:59.200 not taking the advantage of the redu
00:24:00.880 time you can do here so what we do oh
00:24:03.840 yeah and by the way this is not because
00:24:05.200 of complete mistakes there's a lot of
00:24:07.000 times your product managers will go in
00:24:09.200 and say Hey
00:24:11.080 listen I need this and I need it
00:24:13.559 tomorrow because we're going to lose a
00:24:15.080 customer or something like that and by
00:24:17.320 the way when zenes began there was no
00:24:19.120 such thing there might have been such
00:24:20.640 thing as eex but it wasn't in rails so
00:24:23.279 we had to create our own implementation
00:24:25.000 well before rails actually started we
00:24:26.880 didn't create it correctly but hey it it
00:24:31.320 worked and it was a heck of a lot better
00:24:32.919 than it was there before so wow uh I
00:24:37.720 just described that by the way I
00:24:39.640 actually sent these to this uh anyways
00:24:43.640 good so the real solution here is for us
00:24:45.840 to actually go in and start using rails
00:24:48.080 the right way unfortunately um that is
00:24:51.159 hard it takes years our customers are
00:24:53.039 used to the our apis the way they are
00:24:55.360 today we can't just go in there and say
00:24:57.399 hey here's a new API you can't use a
00:24:59.120 thing that you us 15 years breaking
00:25:01.440 changes are dreadfully hard when you
00:25:04.559 have a quarter of a million
00:25:06.720 customers um so what we did is we cached
00:25:12.480 that eag at the very end right oh here
00:25:17.640 we cash that value and
00:25:21.120 then uh we took that value and we saved
00:25:26.039 it in cash and then when we had a new
00:25:27.960 request Quest we would take a look at
00:25:30.000 the cach D tags that we had actually and
00:25:33.480 we by the way we have phenomenal cash
00:25:35.399 busting at zenes it's a very personal
00:25:37.559 thing to every customer or to every uh
00:25:39.880 implementation so your implementation of
00:25:42.320 how you cash bust is going to be very
00:25:44.640 personal to you but we have phenomenal
00:25:46.120 cash busting so anyways we check the E
00:25:50.279 tags that have came in and we compare it
00:25:52.600 to the eag that's coming in from the
00:25:54.679 request if it matches we know right at
00:25:57.440 the beginning of the life cycle that we
00:25:59.880 don't have
00:26:01.080 to look at the presenter all of the data
00:26:04.640 is fresh or is good or not stale so what
00:26:10.080 we do is we set the headers to the tag
00:26:12.360 that's appropriate and then we have we
00:26:14.840 turn 304 not modified and I was saying
00:26:17.360 in rails it's actually the middleware
00:26:19.679 that kind of takes care of switching
00:26:21.200 from 200 to a 304 and then sending no
00:26:25.080 content back but in our implementation
00:26:27.320 we kind of do it much more at the
00:26:28.600 controller
00:26:30.120 level and again this is my last slide
00:26:33.120 kind of um where uh your caching
00:26:37.120 strategy is very important got to talk
00:26:39.480 to probably everybody on your team get
00:26:41.480 get a good caching strategy and you're
00:26:43.279 going to be able to uh leverage a lot of
00:26:47.080 just great work and make it make make
00:26:49.279 your caching strategy easy I can't it's
00:26:52.480 a whole new talk and probably many talks
00:26:54.919 on how to create a good CRA caching
00:26:56.760 strategy unfortunately
00:26:58.720 um I'm supposed to stop here but I do
00:27:01.840 have extra 5 minutes and I had planned
00:27:04.960 to make this a joke slide where somebody
00:27:06.720 was going to come and tackle me off this
00:27:09.039 stage but I guess I can actually talk
00:27:11.080 about this also um I don't know if I
00:27:13.640 have time for both all of these but um
00:27:15.919 as
00:27:16.960 if when you cash a lot of times you cash
00:27:20.520 on a single object and you go okay I
00:27:23.360 cash that and then you like hey I cash I
00:27:26.080 did a really good great job I cashed
00:27:27.880 this user for example or something like
00:27:30.120 that but then in your implementation
00:27:31.880 you're in the index uh action and you
00:27:34.720 call your cash again and again and again
00:27:38.799 and you make a 100 calls to uh to cash
00:27:41.399 well guess what if you make a 100 calls
00:27:43.279 to your cash you're actually
00:27:46.480 getting um you're basically have an N
00:27:49.240 plus1 but it's not in my SQL it's now in
00:27:51.480 your caching layer but reing leveraging
00:27:54.360 read multi you can actually get all that
00:27:56.640 cash at one time
00:27:58.880 so this is really important and if
00:28:02.039 you're look actually looking at your
00:28:03.200 data dog slides or however you monitor
00:28:06.000 your stuff uh you will save a lot of
00:28:09.000 time by uh grabbing data using read
00:28:13.000 multi instead of uh uh however else
00:28:16.000 you're doing it and last thing and I
00:28:18.960 since I do have time I'll talk about
00:28:20.440 forcing indexes uh damn this was my code
00:28:24.519 I was wishing I I wasn't supposed to
00:28:26.120 actually present this I I went a little
00:28:28.159 little fast anyways um yeah let's say
00:28:33.440 seven years ago there was code in our B
00:28:36.360 uh there's code that uh we implemented
00:28:40.320 and uh we forced indexes because at the
00:28:43.240 time it made sense
00:28:46.320 well as time goes on new features are
00:28:49.080 added you go to Aurora 2 to Aurora 3 or
00:28:53.039 my SQL 5 to8 I think that it was but
00:28:55.440 anyways you change a lot of things in
00:28:57.240 your application
00:28:58.559 well you got to keep on going back to
00:29:00.640 your places that you force the indexes
00:29:02.519 because sometimes they don't make sense
00:29:04.480 the biggest gain that I had in the past
00:29:06.559 year was removing a force index on our
00:29:09.640 ticketing end points it was so sad
00:29:12.720 because I implemented the code to force
00:29:14.760 the code and then seven years ago I'm
00:29:16.919 like who the
00:29:18.519 oh
00:29:20.559 sorry and anyways does anybody want to
00:29:23.240 tackle me off
00:29:25.399 screen that's it
Explore all talks recorded at Rails World 2024
+33