2

I have a background task that surely sets a global variable, but FastAPI's endpoint is not able to see that change.

It behaves like FastApi is forking my process, taking initial variable values, eventhough as I read it was only async threaded, thus GIL applies (global variables accessible by threads), also I have workers set to one. Also PID is same in task thread and fastapi's endpoint.

from fastapi import FastAPI
from threading import Thread
from time import sleep

app = FastAPI()
globalVar = 0

@app.get("/")
def read_root():
  global globalVar
  assert globalVar>0   # <----------- FAILS HERE
  return {"Hello": globalVar}

def task():
  global globalVar
  globalVar = 1
  print('globalVar set')

if __name__ == '__main__':
  Thread(target=task, args=()).start()
  sleep(2) # allow thread to set value

  import uvicorn
  uvicorn.run('fasterror:app', host='127.0.0.1', port=80, reload=True, access_log=True, workers=1)
 assert globalVar>0
AssertionError

I am expecting Fastapi endpoint to be able read write global var and share information with background thread.

Based on answer FastAPI: global variable vs module variable it should work.

Update: thread lock on variable access didn't help.

Eva4684
  • 21
  • 3
  • I'm not familiar with `FastAPI`, but are you sure `read_root` gets executed in the same *process*, and nothing is being forked? – chepner Mar 15 '23 at 22:29
  • 1
    `workers=1` implies your script runs an HTTP server, but that server forks (up to) one new process to handle each request. – chepner Mar 15 '23 at 22:30
  • Oh, wait: does `unicorn.run` import your script in order to access `app`? That would mean `read_root` updates `fasterror.globalVar`, not `__main__.globalVar` (one file, two different modules defined from it). – chepner Mar 15 '23 at 22:32
  • But wouldn't uvicorn run the entire file, including my thread and dependencies? If not then how I'm suppose to reuse any existing code. – Eva4684 Mar 15 '23 at 22:33
  • 2
    When you run your file, you tell `uvicorn` to start a separate process/thread where it imports your code and then runs it - _in that process/thread_ any code inside `__main__` will not be invoked, _since your module is being imported_ and not run directly. Use the `@app.on_event("startup")` decorator to make sure code is initialized/started together with your application. If you were to run `uvicorn module:app` from the command line, nothing inside your `__name__` guard would execute either; don't guard initialization code, it will not run when uvicorn launches your application. – MatsLindh Mar 15 '23 at 22:49
  • https://fastapi.tiangolo.com/advanced/events/#startup-event – MatsLindh Mar 15 '23 at 22:49
  • (Sorry, make that `fasterror.read_root` looks at `fasterror.global_var`, but `fasterror.task` never gets executed, only `__main__.task`, so `fasterror.global_var` never gets set to 1.) – chepner Mar 15 '23 at 22:52
  • Thank you all, I didn't realise that REST requires so much effort and it's not just a drop in interface, but I have to build my app around it. – Eva4684 Mar 19 '23 at 15:09
  • You can also use the following code for running uvicorn: `uvicorn.run(app, host='127.0.0.1', port=80)` (dropping workers and reload and passing app instance). With this code, the program works as expected. – Mahdi Sadeghi Aug 26 '23 at 10:29

0 Answers0