프로그래밍/스팀 게임 퀴즈

#13 비동기 SQLModel

2jun0 2024. 1. 8. 15:29
SQLModel 자체가 비동기를 대단히 지원하지도 않아서 의미있는 내용을 담고 있지 않다.
그냥.. 그렇다 기대 노노

 

SQLModel을 사용하는 코드를 비동기로 다시 작성하다보니 

이 라이브러리에서 비동기 프로그래밍이 좀 어렵다는 사실을 알았다.

 

아직 todo list에서 구현 예정 이라는 말 뿐이고, 좀 기다려야 할 것같다.

그래서 업데이트가 되기 전에 어떻게 비동기를 센스있게 다룰지 고민한다.

 

1. Model 클래스에서 해당 필드를 명시적으로 selectin or join으로 지정하기

class GameScreenshot(CreatedAtMixin, UpdatedAtMixin, SQLModel, table=True):
    __tablename__: str = "game_screenshot"

    id: int | None = Field(default=None, primary_key=True)
    steam_file_id: int = Field(sa_column=Column(BigInteger(), unique=True))
    url: str = Field(max_length=2048)

    game_id: int = Field(foreign_key="game.id")
    game: Game = Relationship(sa_relationship_kwargs={"lazy": "selectin"})

class Quiz(CreatedAtMixin, UpdatedAtMixin, SQLModel, table=True):
    __tablename__: str = "quiz"

    id: int | None = Field(default=None, primary_key=True)

    screenshots: list[GameScreenshot] = Relationship(
        link_model=QuizScreenshotLink, sa_relationship_kwargs={"lazy": "selectin"}
    )

    @property
    def game(self) -> Game:
        return self.screenshots[0].game

 

조회할때 쿼리는 이렇게 나온다.

— 조회
SELECT 
quiz.updated_at, 
quiz.created_at, 
quiz.id 
FROM quiz 
WHERE quiz.created_at >= ? AND quiz.created_at <= ?

— screenshot 조회
SELECT
quiz_1.id AS quiz_1_id, 
game_screenshot.updated_at AS game_screenshot_updated_at, 
game_screenshot.created_at AS game_screenshot_created_at, 
game_screenshot.id AS game_screenshot_id, 
game_screenshot.steam_file_id AS game_screenshot_steam_file_id, 
game_screenshot.url AS game_screenshot_url, 
game_screenshot.game_id AS game_screenshot_game_id 
FROM quiz AS quiz_1 
JOIN quiz_screenshot_link AS quiz_screenshot_link_1 
ON quiz_1.id = quiz_screenshot_link_1.quiz_id 
JOIN game_screenshot 
ON game_screenshot.id = quiz_screenshot_link_1.screenshot_id 
WHERE quiz_1.id IN (?, ?, ?, ?, ?)

— game 조회
SELECT 
game.id AS game_id, 
game.updated_at AS game_updated_at,
game.created_at AS game_created_at,
game.steam_id AS game_steam_id, 
game.name AS game_name,
game.kr_name AS game_kr_name 
FROM game 
WHERE game.id IN (?, ?, ?, ?, ?)
  • 장점
    • 기존 코드에서 손댈곳이 별로 없다
    • 퀴즈를 가져올땐 항상 스크린샷을 가져와야 함
  • 단점: 퀴즈만 조회하고 싶었는데 줄줄이 소세지처럼 가져오게됨

2. sql 구문을 만들때 옵션으로 지정하기

stmts = (
    select(Quiz)
    .where(Quiz.created_at >= start_datetime, Quiz.created_at <= end_datetime)
    .options(selectinload(Quiz.screenshots))  # type: ignore
)
rs = await self._session.exec(stmts)
return rs.all()

 

  • 장점: 필요힌 데이터만 가져올 있다
  • 단점: 다 곳에서 예기치 못한 implicit i/o오류가 나올 있다

결론은 아래를 택했다.

결국 내가 비동기를 택한 이유는 빠른 처리였는데, 왕쿼리 전략을 쓴다면 의미가 퇴색되는 것 같기 때문이다.

예기치 못한 오류는 테스트를 통해 검증하겠다. (아니면 lazy: raise 전략도 있다)