############
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
.. raw:: html
How Caching Works in TgramDND ⁉️
---------------------------------
every main ``App`` instance has a ``cache`` property
which can be any of the available :doc:`Caching Tools `
every ``cache`` has an ``items`` property, which is a map of string to :class:`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
.. raw:: html
Caching Example 🌈
---------------------
if you didn't understand the caching concept yet, we will make an example bot
.. note::
in this example we will use :class:`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, :ref:`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
.. code-block:: python
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
.. code-block:: python
# 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)
.. code-block:: python
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 `_