Caching in Python
Tags: python, caching, redis
Published: 2022-07-10 14:41:01

*caching is winning the battle without the fight.* 

Caching refers to storing data in a cache for faster retrieval. Caching facilitates easier scaling by enabling you to return responses without computing them all the time.

> A cache is a piece of hardware or software that stores data allowing future responses to be served faster decreasing time taken to return responses.

Caching is used in different systems including the central processing unit (CPU) memory caches, hard drive caches, Linux operating system file caches, database query caches, Domain Name System (DNS) client caches, Hypertext Transfer Protocol (HTTP) browser caches, HTTP proxies and reverse proxies.

It is important to consider when and where to caching to leverage its ability to improve application performance.

**Cache Hit Ratio**
The Cache hit ratio is measure of  how many times you can reuse the same cached response.
For instance, a 90% cache hit ratio with ten requests means that you hit the cache 9 out of 10 times. You only compute the response once and serve the same response for the other nine requests.

A high cache hit ratio is desirable although one must ensure the response does not become stale.
 
### Factors affecting cache hit ratio
#### Data  Size

The number of all possible cache keys an application can generate affects the cache hit ratio because the more unique the cache keys the lower the cache hit ratio.

#### Space

The size of the objects and the size of the cache determine the cache hit ratio because objects have to be evicted to fit the cache. 
The more responses that can fit in the cache the higher the cache hit ratio.

#### Longevity

The average time a response can be stored in cache before expiring or being invalidated determines the cache hit ratio.

A Time to Live(TTL) is defined indicating the time an object can stay in the cache before expiring.
The longer the TTL the higher the cache hit ratio.

> Ideal Cases for Caching

Use cases with a high ratio of reads to writes;  the cached objects can be created once and stored for longer periods of time before becoming invalid. 

> Unideal Cases for Caching

Use cases with data updating very often may render cache useless, as objects in cache may become invalid before they are used again.

### Implementation in Redis
In this post we will explore how to implement caching using Redis.

[Redis](https://redis.io/) is a one of the most popular data stores used as a cache together with Python due to its capabilities. 

To get started with Redis, install redis using 

`pip install redis`

On the command line, run `redis-cli` to connect to redis.
Data in redis is stored as key-value pairs where the keys have to be unique.
> You can think of redis as a glorified dictionary.

#### Useful Commands
To store data use the SET command.
Syntax: `set key value`
The SET command has several [options](https://redis.io/commands/set/) that specify how the data should be stored. 

Use the GET command to retrieve data using the key.
Syntax: `get key`

Use `keys * ` to retrieve all the keys of the stored data.

![Redis](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/65q6nkevm1hgf9exmidd.png)
### connect Python Client to Redis 

Let us see how to use Redis to cache data retrieved from an API

Create a file `caching.py`

```python
import redis
import sys

def connect_redis():
try:
        client = redis.Redis(
            host="localhost",
            port=6379,
            db=0,
            socket_timeout=5,
        )
        ping = client.ping()
        if ping is True:
            print("Connection Established!")
            return client
    except redis.AuthenticationError:
        print("AuthenticationError")
        sys.exit(1)

client = connect_redis()

```
`connect_redis()` tries to connect to the redis server.

Getting data from the jsonplaceholder API and storing it in redis.

```python
def get_data_from_api(url):
    data = requests.get(url).json()
    return data

def set_data_to_cache(data):
    id = data['id']
    title = data['title']
    timeout = 30
    return client.setex(id,title,timeout)

```

Get data from cache and display it 

```python
def get_data_from_cache(id):
    title = client.get(id)
    return title

def display_data(id):
    title = get_data_from_cache(id)
    if title is not None:
        return title
    else:
        data = get_data_from_api(url)
        print(data['title'])
        title = data['title']
        return title
    
client = redis_connect()
url = "https://jsonplaceholder.typicode.com/posts/1"
display_data("1")

```
You can find the code [here]()
Happy Learning !!!


### References
Web Scalability for Startup Engineers.