Caching

What is Caching ⁉️

caching is the process of storing some items temporarily in memory

caching can be used in multiple scenario’s

  1. Database Caching

    caching is useful for storing a database field for example we can cache a user Posts in the caching system instead of querying the database every time for it. thus database efficiency increases

  2. API Calls limiting

    caching can be also used in decreasing the calls to your API

    for example (in IMDB API) we can cache the gross revenue of a certain movie with a ttl (time to live) of 5 seconds

    thus instead of calling the API every time our users query’s this data we will only launch 1 get request every 5 seconds instead of recalling the API every time any user query the data


How Caching Works in TgramDND ⁉️

every main App instance has a cache property which can be any of the available Caching Tools

every cache has an items property, which is a map of string to CachedItem each CachedItem has 2 property’s

value: the value we cached in it

ttl: time_to_live

What Is TTL? TTL (AKA time_to_live) is the value that this cached item will expire in, in seconds for example if we have a CachedItem with a ttl of 10, when trying to get() this item’s value after 10 seconds has passed, the item will return None because it has expired

Note

you can pass -1 in ttl for specifying infinite seconds by this way the item will Never expire


Caching Example 🌈

if you didn’t understand the caching concept yet, we will make an example bot

Note

in this example we will use MemoryCache

The App Idea

we have an api that gives us the current Bitcoin Price (ref)

we want to make a Bot UI for our users to know the price of Bitcoin

But There is a problem ❗

our current API plan is the Demo/Free one, so our CallsPerMinute limit is 30 which is pretty low

so in a scenario where Bob, Alice, Jack (50+ others) make a request at the same time only 30 of them will get the result, and the rest are gonna be blocked because of the CallsPerMinute limit

we can solve this problem by using a Cache, which we will store the Bitcoin price in it every 5 seconds only

in the same scenario above (now using Caching ofc), all of the users will get the current Bitcoin Price.

this way the bot will check the cache

if the current cached price has no remaining ttl (Time to live, eg: expired), the bot will make another request to the API and replace the cached price with the new price and the same 5 second limit

this way we can’t exceed the CallsPerMinute API limit, and our max usage will be 12 CallsPerMinute which is way lower then the Free plan limit

Enough Talking, More Coding 📊

lets make the usual setup, iykyk and pass cache argument to the main App instance

Note

each App has a MemoryCache as a default cache

lets start by importing needed stuff

from tgram_dnd import (
    App,
    MessageFlow,
    MessageBlock,
    Reply
)
from tgram_dnd.utils import run_function

from tgram import TgBot, filters
from tgram.types import Message

now lets make our custom Middleware to update the BTC price in the app cache

# setting api vars
URL = "https://api.coingecko.com/api/v3/coins/bitcoin"
API_KEY = "INSERT-COINGECKO-TOKEN"

async def update_price(
    action: Reply,
    update: Message,
    vars: dict
) -> None:
    # first we will get the cached price
    price = action.app.cache.get("btc_price")

    if price:
        # price exists and didn't expire
        return

    # price expired/does not exist, now we will update it

    # get new price
    data = (await run_function(
        r.get,
        URL,
        headers={
            "accept": "application/json",
            "x-cg-demo-api-key": API_KEY
        }
    )).json()

    price = data["market_data"]["current_price"]["usd"]

    # store it in the cache
    res = action.app.cache.set(
        "btc_price",
        value=str(price),
        ttl=5
    )

as we see in the update_price Middleware above

we check the app cache for “btc_price”, and if the result is None we will query the API for the price. and we will set the “btc_price” to the app Cache with a ttl of 5 seconds

now lets end it with the UI (Flows & Blocks)

app.add_flows(
    MessageFlow(
        MessageBlock(
            [
                Reply(
                    "text",
                    {
                        "text": "Current BTC Price is {{cache.get('btc_price')}}$"
                    },
                    middleware=update_price
                )
            ],
            filter=(filters.command("price", prefixes=[".", "/", "!"]))
        ),
        filter=(filters.command("price", prefixes=[".", "/", "!"]))
    )
)

app.run()

running this bot and trying to type .price, !price or /price should reply with the BTC price

you can test it thorough the Example