Home Backend Development Python Tutorial Maximize Your FastAPI Efficiency: Blazingly Fast Implementation of Caching and Locking with py-cachify

Maximize Your FastAPI Efficiency: Blazingly Fast Implementation of Caching and Locking with py-cachify

Dec 05, 2024 am 05:47 AM

Maximize Your FastAPI Efficiency: Blazingly Fast Implementation of Caching and Locking with py-cachify

In the fast-paced world of web development, performance is paramount. Efficient caching mechanisms can significantly enhance the responsiveness of your API by reducing redundant computations and database queries. In this article, we’ll explore how to integrate the py-cachify library into a FastAPI application using SQLModel and Redis to implement caching and concurrency control.

Table of Contents:

  • Introduction
  • Project Setup
  • Creating Database Models with SQLModel
  • Building FastAPI Endpoints
  • Caching Endpoint Results
  • Locking Execution of Update Endpoints
  • Running the Application
  • Conclusion

Introduction

Caching is a powerful technique to improve the performance of web applications by storing the results of expensive operations and serving them from a quick-access storage. With py-cachify, we can seamlessly add caching to our FastAPI applications, utilizing Redis for storage. Additionally, py-cachify provides tools for concurrency control, preventing race conditions during critical operations.

In this tutorial, we’ll walk through setting up the py-cachify library in a FastAPI application with SQLModel for ORM and Redis for caching.

Project Setup

Let’s start by setting up our project environment.

Prerequisites

  • Python 3.12
  • Poetry (you can use any package manager you like)
  • Redis server running locally or accessible remotely

Install Dependencies

Start a new project via poetry:

# create new project
poetry new --name app py-cachify-fastapi-demo

# enter the directory
cd py-cachify-fastapi-demo

# point poetry to use python3.12
poetry env use python3.12

# add dependencies
poetry add "fastapi[standard]" sqlmodel aiosqlite redis py-cachify
Copy after login
Copy after login
Copy after login
  • FastAPI: The web framework for building our API.
  • SQLModel aiosqlite: Combines SQLAlchemy and Pydantic for ORM and data validation.
  • Redis: Python client for interacting with Redis.
  • py-cachify: Caching and locking utilities.

Initializing py-cachify

Before we can use py-cachify, we need to initialize it with our Redis clients. We’ll do this using FastAPI’s lifespan parameter.

# app/main.py
from contextlib import asynccontextmanager
from fastapi import FastAPI
from py_cachify import init_cachify
from redis.asyncio import from_url


@asynccontextmanager
async def lifespan(_: FastAPI):
    init_cachify(
        # Replace with your redis url if it differs
        async_client=from_url('redis://localhost:6379/0'),
    )
    yield


app = FastAPI(lifespan=lifespan)
Copy after login
Copy after login
Copy after login

Inside the lifespan, we:

  • Create an asynchronous Redis client.
  • Initialize py-cachify with this client.

Creating Database Models with SQLModel

We’ll create a simple User model to interact with our database.

# app/db.py
from sqlmodel import Field, SQLModel


class User(SQLModel, table=True):
    id: int | None = Field(default=None, primary_key=True)
    name: str
    email: str
Copy after login
Copy after login

Set up the database engine and create the tables in the lifespan function:

# app/db.py

# Adjust imports
from sqlalchemy.ext.asyncio import async_sessionmaker, create_async_engine


# Add the following at the end of the file
sqlite_file_name = 'database.db'
sqlite_url = f'sqlite+aiosqlite:///{sqlite_file_name}'
engine = create_async_engine(sqlite_url, echo=True)
session_maker = async_sessionmaker(engine)


# app/main.py
# Adjust imports and lifespan function
from sqlmodel import SQLModel

from .db import engine


@asynccontextmanager
async def lifespan(_: FastAPI):
    init_cachify(
        async_client=from_url('redis://localhost:6379/0'),
    )
    # Create SQL Model tables
    async with engine.begin() as conn:
        await conn.run_sync(SQLModel.metadata.create_all)

    yield
Copy after login
Copy after login

Note: We’re using SQLite for simplicity, but you can use any database supported by SQLAlchemy.

Building FastAPI Endpoints

Let’s create endpoints to interact with our User model.

# create new project
poetry new --name app py-cachify-fastapi-demo

# enter the directory
cd py-cachify-fastapi-demo

# point poetry to use python3.12
poetry env use python3.12

# add dependencies
poetry add "fastapi[standard]" sqlmodel aiosqlite redis py-cachify
Copy after login
Copy after login
Copy after login

Caching Endpoint Results

Now, let’s cache the results of the read_user endpoint to avoid unnecessary database queries.

The endpoint code will look like this:

# app/main.py
from contextlib import asynccontextmanager
from fastapi import FastAPI
from py_cachify import init_cachify
from redis.asyncio import from_url


@asynccontextmanager
async def lifespan(_: FastAPI):
    init_cachify(
        # Replace with your redis url if it differs
        async_client=from_url('redis://localhost:6379/0'),
    )
    yield


app = FastAPI(lifespan=lifespan)
Copy after login
Copy after login
Copy after login

With the @cached decorator:

  • We specify a unique key using the user_id.
  • Set the TTL (time-to-live) to 5 minutes (300 seconds).
  • Subsequent calls to this endpoint with the same user_id within 5 minutes will return the cached result.

Resetting Cache on Updates

When a user’s data is updated, we need to reset the cache to ensure clients receive the latest information. To make it happen, let’s modify the update_user endpoint.

# app/db.py
from sqlmodel import Field, SQLModel


class User(SQLModel, table=True):
    id: int | None = Field(default=None, primary_key=True)
    name: str
    email: str
Copy after login
Copy after login

By calling read_user.reset(user_id=user_id), we:

  • Clear the cached data for the specific user_id.
  • Ensure subsequent GET requests fetch fresh data from the database.

Underneath, the cached decorator dynamically wraps your function, adding the .reset method. This method mimics the function’s signature, and type, this way it will be either sync or async depending on the original function and will accept the same arguments.

The .reset method uses the same key generation logic defined in the cached decorator to identify which cached entry to invalidate. For example, if your caching key pattern is user-{user_id}, calling await read_user.reset(user_id=123) will specifically target and delete the cache entry for user_id=123.

Locking Execution of Update Endpoints

To prevent race conditions during updates, we’ll use the once decorator to lock the execution of the update endpoint.

# app/db.py

# Adjust imports
from sqlalchemy.ext.asyncio import async_sessionmaker, create_async_engine


# Add the following at the end of the file
sqlite_file_name = 'database.db'
sqlite_url = f'sqlite+aiosqlite:///{sqlite_file_name}'
engine = create_async_engine(sqlite_url, echo=True)
session_maker = async_sessionmaker(engine)


# app/main.py
# Adjust imports and lifespan function
from sqlmodel import SQLModel

from .db import engine


@asynccontextmanager
async def lifespan(_: FastAPI):
    init_cachify(
        async_client=from_url('redis://localhost:6379/0'),
    )
    # Create SQL Model tables
    async with engine.begin() as conn:
        await conn.run_sync(SQLModel.metadata.create_all)

    yield
Copy after login
Copy after login

With once:

  • We lock the function based on user_id.
  • If another request tries to update the same user concurrently, it will immediately return the response with a 226 IM Used status code.
  • This prevents simultaneous updates that could result in inconsistent data.

Optionally, you can configure @once to raise an exception or return a specific value if the lock is already acquired.

Running the Application

Now it’s time to run and test our app!

1) Start the Redis Server:

Ensure your Redis server is running locally or is accessible remotely. You can start a local Redis server using Docker:

# app/main.py

# Adjust imports
from fastapi import Depends, FastAPI
from sqlalchemy.ext.asyncio import AsyncSession

from .db import User, engine, session_maker


# Database session dependency
async def get_session():
    async with session_maker() as session:
        yield session


app = FastAPI(lifespan=lifespan)


@app.post('/users/')
async def create_user(user: User, session: AsyncSession = Depends(get_session)) -> User:
    session.add(user)
    await session.commit()
    await session.refresh(user)
    return user


@app.get('/users/{user_id}')
async def read_user(user_id: int, session: AsyncSession = Depends(get_session)) -> User | None:
    return await session.get(User, user_id)


@app.put('/users/{user_id}')
async def update_user(user_id: int, new_user: User, session: AsyncSession = Depends(get_session)) -> User | None:
    user = await session.get(User, user_id)
    if not user:
        return None

    user.name = new_user.name
    user.email = new_user.email

    session.add(user)
    await session.commit()
    await session.refresh(user)

    return user
Copy after login

2) Run the FastAPI Application:

With everything set up, you can launch your FastAPI application using Poetry. Navigate to the root directory of your project and execute the following command:

# app/main.py

# Add the import
from py_cachify import cached


@app.get('/users/{user_id}')
@cached('read_user-{user_id}', ttl=300)  # New decorator
async def read_user(user_id: int, session: AsyncSession = Depends(get_session)) -> User | None:
    return await session.get(User, user_id)
Copy after login

3) Testing and Playing with Caching and Locking:

Caching: Add delay (e.g., using asyncio.sleep) in the read_user function to simulate a long-running computation. Observe how the response time drastically improves once the result is cached.

Example:

# create new project
poetry new --name app py-cachify-fastapi-demo

# enter the directory
cd py-cachify-fastapi-demo

# point poetry to use python3.12
poetry env use python3.12

# add dependencies
poetry add "fastapi[standard]" sqlmodel aiosqlite redis py-cachify
Copy after login
Copy after login
Copy after login

Concurrency and Locking: Similarly, introduce a delay in the update_user function to observe the behavior of locks when concurrent update attempts are made.

Example:

# app/main.py
from contextlib import asynccontextmanager
from fastapi import FastAPI
from py_cachify import init_cachify
from redis.asyncio import from_url


@asynccontextmanager
async def lifespan(_: FastAPI):
    init_cachify(
        # Replace with your redis url if it differs
        async_client=from_url('redis://localhost:6379/0'),
    )
    yield


app = FastAPI(lifespan=lifespan)
Copy after login
Copy after login
Copy after login

These delays can help you see the effectiveness of caching and locking mechanisms in action, as subsequent reads should be faster due to caching, and concurrent writes to the same resource should be managed effectively through locking.

Now, you can test your endpoints using a tool like Postman or by going to http://127.0.0.1:8000/docs (when the app is running!), and observe the performance improvements and concurrency controls in action.

Enjoy experimenting with your enhanced FastAPI app!

Conclusion

By integrating py-cachify into our FastAPI application, we’ve unlocked a plethora of benefits that enhance both the performance and reliability of our API.

Let’s recap some of the key strengths:

  • Enhanced Performance: Caching repetitive function calls reduces redundant computations and database hits, drastically improving response times.
  • Concurrency Control: With built-in locking mechanisms, py-cachify prevents race conditions and ensures data consistency — crucial for applications with high concurrent access.
  • Flexibility: Whether you’re working with synchronous or asynchronous operations, py-cachify seamlessly adapts, making it a versatile choice for modern web applications.
  • Ease of Use: The library integrates smoothly with popular Python frameworks like FastAPI, allowing you to get started with minimal friction.
  • Full Type Annotations: py-cachify is fully type-annotated, aiding in writing better, more maintainable code with minimal effort.
  • Minimal Setup: As demonstrated in this tutorial, adding py-cachify requires just a couple of additional lines on top of your existing setup to leverage its capabilities fully.

For those eager to explore further, check out py-cachify’s GitHub repository and the official documentation for more in-depth guidance, tutorials, and examples.

You can access the full code for this tutorial on GitHub here. Feel free to clone the repository and play around with the implementation to suit your project’s needs.

If you find py-cachify beneficial, consider supporting the project by giving it a star on GitHub! Your support helps drive further improvements and new features.

Happy coding!

The above is the detailed content of Maximize Your FastAPI Efficiency: Blazingly Fast Implementation of Caching and Locking with py-cachify. For more information, please follow other related articles on the PHP Chinese website!

Statement of this Website
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn

Hot AI Tools

Undresser.AI Undress

Undresser.AI Undress

AI-powered app for creating realistic nude photos

AI Clothes Remover

AI Clothes Remover

Online AI tool for removing clothes from photos.

Undress AI Tool

Undress AI Tool

Undress images for free

Clothoff.io

Clothoff.io

AI clothes remover

Video Face Swap

Video Face Swap

Swap faces in any video effortlessly with our completely free AI face swap tool!

Hot Tools

Notepad++7.3.1

Notepad++7.3.1

Easy-to-use and free code editor

SublimeText3 Chinese version

SublimeText3 Chinese version

Chinese version, very easy to use

Zend Studio 13.0.1

Zend Studio 13.0.1

Powerful PHP integrated development environment

Dreamweaver CS6

Dreamweaver CS6

Visual web development tools

SublimeText3 Mac version

SublimeText3 Mac version

God-level code editing software (SublimeText3)

Hot Topics

Java Tutorial
1664
14
PHP Tutorial
1268
29
C# Tutorial
1246
24
Python vs. C  : Applications and Use Cases Compared Python vs. C : Applications and Use Cases Compared Apr 12, 2025 am 12:01 AM

Python is suitable for data science, web development and automation tasks, while C is suitable for system programming, game development and embedded systems. Python is known for its simplicity and powerful ecosystem, while C is known for its high performance and underlying control capabilities.

Python: Games, GUIs, and More Python: Games, GUIs, and More Apr 13, 2025 am 12:14 AM

Python excels in gaming and GUI development. 1) Game development uses Pygame, providing drawing, audio and other functions, which are suitable for creating 2D games. 2) GUI development can choose Tkinter or PyQt. Tkinter is simple and easy to use, PyQt has rich functions and is suitable for professional development.

Python vs. C  : Learning Curves and Ease of Use Python vs. C : Learning Curves and Ease of Use Apr 19, 2025 am 12:20 AM

Python is easier to learn and use, while C is more powerful but complex. 1. Python syntax is concise and suitable for beginners. Dynamic typing and automatic memory management make it easy to use, but may cause runtime errors. 2.C provides low-level control and advanced features, suitable for high-performance applications, but has a high learning threshold and requires manual memory and type safety management.

The 2-Hour Python Plan: A Realistic Approach The 2-Hour Python Plan: A Realistic Approach Apr 11, 2025 am 12:04 AM

You can learn basic programming concepts and skills of Python within 2 hours. 1. Learn variables and data types, 2. Master control flow (conditional statements and loops), 3. Understand the definition and use of functions, 4. Quickly get started with Python programming through simple examples and code snippets.

Python and Time: Making the Most of Your Study Time Python and Time: Making the Most of Your Study Time Apr 14, 2025 am 12:02 AM

To maximize the efficiency of learning Python in a limited time, you can use Python's datetime, time, and schedule modules. 1. The datetime module is used to record and plan learning time. 2. The time module helps to set study and rest time. 3. The schedule module automatically arranges weekly learning tasks.

Python vs. C  : Exploring Performance and Efficiency Python vs. C : Exploring Performance and Efficiency Apr 18, 2025 am 12:20 AM

Python is better than C in development efficiency, but C is higher in execution performance. 1. Python's concise syntax and rich libraries improve development efficiency. 2.C's compilation-type characteristics and hardware control improve execution performance. When making a choice, you need to weigh the development speed and execution efficiency based on project needs.

Python: Automation, Scripting, and Task Management Python: Automation, Scripting, and Task Management Apr 16, 2025 am 12:14 AM

Python excels in automation, scripting, and task management. 1) Automation: File backup is realized through standard libraries such as os and shutil. 2) Script writing: Use the psutil library to monitor system resources. 3) Task management: Use the schedule library to schedule tasks. Python's ease of use and rich library support makes it the preferred tool in these areas.

Python: Exploring Its Primary Applications Python: Exploring Its Primary Applications Apr 10, 2025 am 09:41 AM

Python is widely used in the fields of web development, data science, machine learning, automation and scripting. 1) In web development, Django and Flask frameworks simplify the development process. 2) In the fields of data science and machine learning, NumPy, Pandas, Scikit-learn and TensorFlow libraries provide strong support. 3) In terms of automation and scripting, Python is suitable for tasks such as automated testing and system management.

See all articles