Cache is your friend 😄
A lot of performance issues can be improved using cache, there is a lot of tools out here but for this brief tutorial I'll choose the well-known Nginx.
Setup 💻
You can clone the branch nginx-cache
or follow this tutorial (I'll only cover the nginx stuff)
App
I'll use an app from previous post of mine, which calculates the nth fibonacci result given a number. The app it self is very simple and you can see it very small piece of code from the (repository)[https://github.com/iamseki/dev-to] that I mentioned before.
Nginx
nginx/nginx.conf
events {
worker_connections 4096;
}
http {
proxy_cache_path /tmp/nginx/cache levels=1:2 keys_zone=fibo_cache:10m inactive=60m use_temp_path=off;
server {
listen 3000;
location /fibonacci {
proxy_cache fibo_cache;
proxy_cache_use_stale error timeout http_500 http_502 http_503 http_504;
add_header X-Proxy-Cache $upstream_cache_status;
proxy_pass http://api:8080/fibonacci;
}
location /hc {
proxy_pass http://api:8080/hc;
}
}
}
nginx/dockerfile
FROM nginx
COPY nginx.conf /etc/nginx/nginx.conf
Docker-compose
docker-compose.yml
version: "3"
services:
nginx:
build: nginx
ports:
- "3000:3000"
volumes:
- "./cache:/tmp/nginx/cache"
container_name: "nginx"
api:
build: api
ports:
- "8080:8080"
container_name: "api"
Proving cache is our friend
Using k6 we can simulate a bunch of requests that go through Nginx and others that don't, comparing them.
The test will reach 100 requests in the first 30 seconds(about stages in k6) that will randomly retrieve the nth sequence with 7 digits between (9999991 ~ 9999999) to force the usage of API resources and Nginx caching.
k6-test.js
import http from 'k6/http';
import { sleep } from 'k6';
const SLEEP_DURATION = 0.1;
export let options = {
stages: [
{ duration: "30s", target: 100 },
{ duration: "30s", target: 0 },
],
thresholds: {
http_req_duration: ['p(99)<100'] // 99% request must complete below 100ms
}
}
const NGINX_LOCAL_URL = "http://localhost:3000"
const API_LOCAL_URL = "http://localhost:8080"
const BASE_URL = __ENV.API_URL === "NGINX" ? NGINX_LOCAL_URL : API_LOCAL_URL
const HEADERS = { "Content-Type": "application/json" }
const randomInteger = (min, max) => Math.floor(Math.random() * (max - min + 1)) + min;
export default () => {
http.get(`${BASE_URL}/fibonacci/${randomInteger(9999991,9999999)}`);
sleep(SLEEP_DURATION);
}
Running 📋
docker-compose up -d
- Testing with requests passing through Nginx:
k6 run k6-test.js -e API_URL='NGINX'
- Testing requesting directly the API:
k6 run k6-test.js
What if API were down ? Try it out, docker stop api
and run the tests above again.
Nginx will be able to answer some requests (maybe all, due to our scenario), cool right?
Conclusion 📝
As we can see, caching improves considerably the response time. Sometimes the back-end will be able to handle requests even if the API is down, when I test this feature for the first time I thought it was amazing!
This is a simple example but I hope that could be helpful for people who's thinking to cache some API routes.I know this is not applicable to every scenario, but maybe can open a world of possibilities to solve performance issues without change the API it self.
Top comments (0)