#python #postgresql #sqlalchemy #fastapi
#python #postgresql #sqlalchemy #fastapi
Вопрос:
Я хочу понять, как база данных остается согласованной после одновременных вызовов конечной точки, которые обновляют счетчик. Извините за названия классов / методов, которые я скопировал большую часть кода из документации инструментов.
Код сервера выглядит следующим образом
import uvicorn
from fastapi import FastAPI
from sqlalchemy import create_engine
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy import Column, Integer, String
from sqlalchemy.orm import sessionmaker
app = FastAPI()
engine = create_engine('postgresql://postgres:test@localhost/test')
Base = declarative_base()
class User(Base):
__tablename__ = 'users'
id = Column(Integer, primary_key=True)
name = Column(String)
counter = Column(Integer)
def __repr__(self):
return "counter='%s'" % (self.counter)
Base.metadata.drop_all(bind=engine)
Base.metadata.create_all(engine)
Session = sessionmaker(bind=engine)
# create
session = Session()
ed_user = User(name='ed', counter=0)
session.add(ed_user)
session.commit()
session.close()
async def updates():
# update
for i in range(50):
session = Session()
our_user = session.query(User).filter_by(name='ed').first()
c = our_user.counter
our_user.counter = c 1
session.add(our_user)
session.commit()
session.close()
@app.get("/update")
async def update():
await updates()
return {"Hello": "World"}
@app.get("/counter")
async def counter():
session = Session()
our_user = session.query(User).filter_by(name='ed').first()
print(our_user)
if __name__ == '__main__':
uvicorn.run(app, host="0.0.0.0", port=8000)
Я запускаю его, а затем запускаю следующий скрипт:
from threading import Thread
import requests
def info():
r = requests.get('http://localhost:8000/counter')
def thread():
threads = []
for _ in range(100):
t = Thread(target=fetch())
t.start()
threads.append(t)
for t in threads:
t.join()
def fetch():
requests.get("http://localhost:8000/update")
thread()
info()
Результат счетчика в конце всегда правильный, даже при использовании конечных точек и updates()
метода, отличных от asnyc. Я попытался сделать то же самое без запуска Fastapi и использовать только потоки, которые запускают updates()
функцию, но в этом случае счетчик непоследователен.
Итак, по какой-то причине Fastapi творит волшебство, но что именно происходит? И разве база данных не должна также предоставлять какую-то функцию изоляции?
Редактировать
На самом деле это была ошибка в моем коде, когда я неправильно создавал потоки. Я передал результат fetch()
вместо самой функции. Теперь, когда я запускаю его правильно, он ведет себя так, как ожидалось, а не асинхронная версия имеет несогласованный счетчик.
Комментарии:
1. Вы читали Concurrency и async / await в документации fastapi? Это очень хорошо объясняет, как это работает!
2. на самом деле да! Но в моем коде была ошибка, которая сделала неасинхронную версию согласованной. Целью потоков была не функция, а ее результат.
3. Вопрос заключался в том, как одновременные вызовы асинхронных и неасинхронных конечных точек приводят к одному и тому же результату, хотя в неасинхронных должны быть несоответствия из-за доступа к одному и тому же ресурсу. Но причиной такого поведения была ошибка в моем примере кода, я исправил ее, и теперь она ведет себя так, как ожидалось. Итак, вопросов больше нет.
Ответ №1:
На самом деле в моем примере кода была ошибка. Это должно быть
def thread():
threads = []
for _ in range(100):
t = Thread(target=fetch)
t.start()
threads.append(t)
for t in threads:
t.join()
Я передал целевому параметру результат функции, а не саму функцию. Вот почему он вел себя не так, как ожидалось, а асинхронные и неасинхронные конечные точки показали одинаковые результаты. Теперь он ведет себя так, как ожидалось, и неасинхронная конечная точка показывает противоречивые результаты из-за одновременного доступа к одному и тому же ресурсу.