Caching in Flask - Speed up your apps!

Caching in Flask - Speed up your apps!

In this blog, we are going to see how we can implement a simple caching mechanism in our Flask applications which would result in significant performance improvements.

What is caching ?
In simple terms, caching is the act of storing data in a storage which allows easy and quick retrieval without needing to go to the source provided the data isn't going to change very frequently.

Now this storage can be an in-memory cache or external caching systems like Redis or Memcached.

Caching can be done for multiple reasons:

  • Eliminates expensive DB calls.
  • Reduces latency.
  • Avoids network congestion.

We are going to use an in-memory cache for our use-case. We'll be creating a simple Flask app that would have two API's, one for returning all users and one for returning users based on the user id. We'll implement caching and see how it impacts our performance.

Let's get down to the code part. First, we'll create the main app.py file which will have the two endpoints.

from flask import Flask, jsonify
import users
import time

app = Flask(__name__)

@app.route("/users")
def get_all_users():
    time.sleep(3)
    return jsonify(users.get_all_users())

@app.route("/user/<id>")
def get_user(id):
    time.sleep(3)
    return jsonify(users.get_user(int(id)))

if __name__ == "__main__":
    app.run(debug=True)

Next, we'll create the users.py which basically will have the user data. Note this file is just for sample purposes here. This could very well be your DB connection or connecting to another service internally to get the user info and that is precisely the reason we've added a 3-sec sleep, just to show the impact of caching here.

users = [
    {
        "id": 1,
        "name": "Daniel Smith"
    }, {
        "id": 2,
        "name": "Ricky Martin"
    }, {
        "id": 3,
        "name": "Charlie Bond"
    }, {
        "id": 4,
        "name": "Douglas Nuth"
    }
]

def get_all_users():
    return users

def get_user(user_id):
    user_info = [user for user in users if user['id'] == user_id]
    return user_info

Once we run this app and hit the two API's from Postman we'll see them taking around 3 secs each.

users_wo_caching.JPG

user_wo_caching.JPG

Now we'll add the magical caching part. We need to first install the caching library.

pip install flask-caching

Once it's installed we'll import the same and put the basic configs in place before initializing the cache.

from flask_caching import Cache

config = {
    "DEBUG": True,
    "CACHE_TYPE": "simple",
    "CACHE_DEFAULT_TIMEOUT": 300
}

app.config.from_mapping(config)
cache = Cache(app)

We'll now put the cache decorator above our two methods of the API's.

@app.route("/users")
@cache.cached(timeout=10)
def get_all_users():
    time.sleep(3)
    return jsonify(users.get_all_users())


@app.route("/user/<id>")
@cache.cached(timeout=10, query_string=True)
def get_user(id):
    time.sleep(3)
    return jsonify(users.get_user(int(id)))

Notice the query_string=True which we've added for the /user API. This will actually cache response based on each user's id. Pretty neat right!

If we run the app now, after the first hit(that's when the response will be actually cached) we'll see a significant improvement in the response time of the API's certifying that the caching mechanism we've added is working perfectly fine.

users_w_caching.JPG

user_w_caching.JPG

Since we've added the timeout=10 (second), we hit the API's again after 10 secs, it'll take around 3-secs. But we can always configure this based on our use-case i.e., whether we want our objects to be in the cache for an hour, a week etc.

Thanks for reading this article. Hope you learned something new. Cheers!