question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Persisting memory state between requests in FastAPI

See original GitHub issue

First Check

  • I added a very descriptive title to this issue.
  • I used the GitHub search to find a similar issue and didn’t find it.
  • I searched the FastAPI documentation, with the integrated search.
  • I already searched in Google “How to X in FastAPI” and didn’t find any information.
  • I already read and followed all the tutorial in the docs and didn’t find an answer.
  • I already checked if it is not related to FastAPI but to Pydantic.
  • I already checked if it is not related to FastAPI but to Swagger UI.
  • I already checked if it is not related to FastAPI but to ReDoc.

Commit to Help

  • I commit to help with one of those options 👆

Example Code

from fastapi import FastAPI

class GlobalTest():
    __count = 1

    @classmethod
    def main(self):
        self.__count += 1
        return self.__count

app = FastAPI()
@app.get("/")
async def read_root():
    return GlobalTest.main()

Description

Open two browsers. Go to 127.0.0.1/docs and call this API a couple of times. You just got a counter over different requests. The static class is memorizing the value over different requests.

Is there any solution to this? Is there any other way to create static classes that will not persist over different requests? I think its definitely an issue, and potentially a security concern.

Thanks

Operating System

Linux

Operating System Details

No response

FastAPI Version

0.68.1

Python Version

3.9.7

Additional Context

No response

Issue Analytics

  • State:open
  • Created a year ago
  • Comments:10 (5 by maintainers)

github_iconTop GitHub Comments

5reactions
Kludexcommented, Jun 18, 2022

Is there any solution to this? Is there any other way to create static classes that will not persist over different requests? I think its definitely an issue, and potentially a security concern.

It’s not a security concern. You are running your application in a python process, so it’s normal that your counter goes up.

This could be avoided by setting uvicorn to one worker per request, but it would be too slow.

That’s not true. It cannot be avoided. Those are python processes. The application is not stateless because you’re coding it as non-stateless, it’s not a web framework or server issue, it’s just an implementation issue.

I understand what you mean by this issue, but there’s a misconception that the web framework (or server implementation) should prevent you from doing what you are able to do, but that’s not the case. The worker is a python process, and objects are living there, if you change the value of an object, then that’s it, the value will change.

1reaction
Kludexcommented, Jul 7, 2022

Can you elaborate?

I can try. 😃

Can you guarantee that multiple requests in parallel using the methods of this object will not have any interference between each others?

I cannot guarantee that. But that’s more related to how Python works, than the web framework. There’s nothing we can do to prevent the user to do that.

Considering the scenario you proposed above, we can check this application:

import asyncio

from fastapi import FastAPI


app = FastAPI()


class HeavyObject:
    def __init__(self):
        self.value = 0


obj = HeavyObject()


@app.get("/")
async def home():
    task_id = id(asyncio.current_task())
    print(task_id, obj.value)
    obj.value += 1
    await asyncio.sleep(3)
    print(task_id, obj.value)

If you call this server multiple times in the time frame of 3 seconds, you’re going to see that all of them are going to finish with the same obj.value. That’s fine, it’s meant to be like this.

What we can do to prevent something like this from happening is teaching people that this is the expected behavior when you change a global object. If that’s not enough, you can use some technique to make those objects immutable, and work with the values you’re interested in:

import asyncio
from dataclasses import dataclass

from fastapi import FastAPI


app = FastAPI()


@dataclass(frozen=True)
class HeavyObject:
    value: int


obj = HeavyObject(value=0)


@app.get("/")
async def home():
    value = obj.value
    task_id = id(asyncio.current_task())
    print(task_id, value)
    value += 1
    await asyncio.sleep(3)
    print(task_id, value)
Read more comments on GitHub >

github_iconTop Results From Across the Web

avoid persisting memory state between requests in FastAPI
FastAPI is basically stateless by default. It actually takes extra work to persist data across requests through methods such connection ...
Read more >
Deployments Concepts - FastAPI
Memory per Process​​ And multiple processes normally don't share any memory. This means that each running process has its own things, variables, and...
Read more >
Concurrency with FastAPI - Medium
Here, GET request is made with the await keyword, telling Python that this is a point where it can suspend execution and do...
Read more >
tiangolo/fastapi - Gitter
I'm developing a FastAPI app and was experimenting with Celery as a task queue. However, it seems that it often just leaves tasks...
Read more >
Using Redis with FastAPI | The Home of Redis Developers
This tutorial helps you get started with Redis and FastAPI. ... Redis excels at low-latency access because it's an in-memory database.
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found