System Design: Create a url shortening service (Part 3): Read API, Load testing and performance optimization
This is part of a blog series where we design, develop, optimize, deploy and test a URL shortener service from scratch
- Part 1: Overview
- Part 2: Design the write API
- Part 3: Read API, Load testing and Performance improvement
- Part 4: Deploy to AWS
- Part 5: Performance testing on AWS
In this article, we’ll discuss:
Read API
Read API is pretty simple. Given a short code, returns the original url.
longUrl(minifiedURL) // Returns the long url that was encoded
Updated model to read from the database
Add a new route to get this data
Seed the data
We’ll enter a million entries into the database to start with. We’ll use Sequelize seeders
We can validate the code by making this GET
request http://localhost:3000/longUrl?code=Pl1pMh
and should get {“originalUrl”:”http://www.thisissomereallylongurl1"}
as response.
Load testing using JMeter
If you are not familiar with JMeter, we will create test plan and configure it.
Step 1: Add users
Let’s call this read url. Add 100 users. I have set this up as below
It’s important to note that I have given 50 seconds as a ramp up time. This means that number of threads starting at each second is 100(users)/50 = 2. Since JMeter and node server is running on the same hardware, this would affect results if this number is low.
Let’s control the maximum number of requests coming in to the server. Add Constant Throughput Timer
for 100 requests/second
Now, before we make request, we want some random short url for which we want to fetch the long url. We can create a CSV file and let JMeter read it recursively.
Fill in the details as below
and code2.csv
is pretty simple
Next: Add HTTP request and read from the csv file.
Add a listener to display the result.
That’s it.
Below are the results I got on my mac
I have other apps running along with this which could affect the performance. This still gives us a good enough estimate of how well the app will perform in the wild with 100 requests per second.
We’ll focus on 90% line
and 95% line
in the above table and optimize it.
Optimization
Optimization 1: We can play with database connections pool size. We can edit our config/config.json
file and increase our connection pool
"development": { "username": "abhinavdhasmana", "password": null, "database": "tinyurl_development", "host": "127.0.0.1", "dialect": "postgres", "pool": { "max": 20, "min": 5, "idle": 10000 }}
Below are the results:
Metrics improved by roughly 20%. At this point of time, I am not sure what is the magic number on max
or min
on the pool connection. If you know more, please leave a comment.
Optimization 2: Node.js is single threaded. One node server cannot use all the CPU cores. This is hardware wastage. Welcome PM2. PM2 can run one node process per CPU core. Its simple to setup and start
npm install pm2
node_modules/.bin/pm2 start -i max src/server.js
Wow! Now I was able to get 50% and 70% improvement on 90% line
and 95% line
respectively.
Optimization 3: The slowest part in the application is the hard disk. Lets minimize its use. We will put this data in Redis. Redis is an in-memory data structure store. Code is modified below to use Redis
and run our load test again
This is fabulous now. I was able to get over 70% and 85% improvement on 90% line
and95% line
respectively
That’s it for the performance optimization. App is now ready to be deployed and be tested in the wild.
The complete code can be found on Github.
If you found this story interesting or useful, please support it by clapping it👏