Skip to content

fix(db): Workers에서 postgres/mysql 연결을 요청당 생성·해제 — 로그인 hang 수정 - #71

Merged
mack-erel merged 2 commits into
mainfrom
fix/workers-db-connection-per-request
Jul 2, 2026
Merged

fix(db): Workers에서 postgres/mysql 연결을 요청당 생성·해제 — 로그인 hang 수정#71
mack-erel merged 2 commits into
mainfrom
fix/workers-db-connection-per-request

Conversation

@mack-erel

Copy link
Copy Markdown
Owner

문제

프로덕션(idp.hyochan.site)에서 POST /login 이 Worker hang → 500 으로 취소됩니다. D1 → PostgreSQL(Hyperdrive) 전환(#70) 이후 발생한 프로덕션 다운 이슈입니다.

관측된 로그:

Error: The Workers runtime canceled this request because it detected that
your Worker's code had hung and would never generate a response.
POST https://idp.hyochan.site/login → 500

근본 원인

src/lib/server/db/index.tsgetDb 가 postgres/mysql 연결을 모듈 전역 싱글턴(pgSql/mysqlPool)으로 캐시하고 닫지 않았습니다.

Cloudflare Workers 는 I/O 객체(TCP 소켓)를 요청 간 공유할 수 없습니다. 첫 요청(GET /login — 폼 렌더 성공)이 만든 소켓을 다음 invocation(POST)이 재사용하려다 죽은 소켓 I/O 에서 영원히 멈추고, Workers 런타임이 hang 을 감지해 요청을 취소합니다. D1(요청별 바인딩)에선 드러나지 않던 결함이라 전환 전엔 보이지 않았습니다.

수정

파일 변경
src/lib/server/db/index.ts getDb{ db, dispose? }(DbHandle) 반환. platform.ctx.waitUntil 유무로 Workers 판별 — Workers 는 요청당 연결 생성 + dispose, Node(adapter-node)는 기존 전역 재사용 유지. mysql 은 Workers 에서 disableEval: true 추가. sqlite(libSQL HTTP)/d1 은 소켓 장기점유가 없어 그대로.
src/hooks.server.ts 응답 후 ctx.waitUntil(dispose()) 로 요청당 연결을 백그라운드에서 정리(응답 지연 없음). 아울러 DB 초기화 게이트가 platform.env.DB(D1 전용)로 하드코딩돼 postgres 배포에서 초기화가 통째로 스킵될 수 있던 잠재 버그를 방언 인식 게이트로 교체.

Cloudflare Hyperdrive + postgres-js 권장 패턴(요청당 연결, max: 5, ctx.waitUntil(sql.end()))과 일치합니다.

검증

  • DB_DIALECT=postgres tsc --noEmit — clean (exit 0)
  • DB_DIALECT=postgres bun run build — clean (adapter-cloudflare, exit 0)
  • prettier --check + eslint — clean

⚠️ 한계: 타입·빌드·lint 는 통과했으나, live Hyperdrive+postgres 에 대한 런타임 재현은 이 환경에서 돌리지 못했습니다. 다만 hang 은 소켓 공유 문제로 원인이 결정적이며, 수정은 문서화된 Cloudflare 권장 패턴을 그대로 따릅니다. 머지 후 프로덕션에서 로그인 경로 확인을 권장합니다.

🤖 Generated with Claude Code

mack-erel and others added 2 commits July 2, 2026 12:50
프로덕션(idp.hyochan.site)에서 POST /login 이 Worker hang → 500 으로
취소되는 문제 수정. D1→PostgreSQL(Hyperdrive) 전환 후 발생.

근본 원인: getDb 가 postgres/mysql 연결을 모듈 전역 싱글턴으로 캐시하고
닫지 않았다. Cloudflare Workers 는 I/O 객체(TCP 소켓)를 요청 간 공유할 수
없으므로, 첫 요청이 만든 소켓을 다음 invocation 이 재사용하다 죽은 소켓
I/O 에서 영원히 hang 한다. D1(요청별 바인딩)에선 드러나지 않던 결함.

- getDb 가 { db, dispose? }(DbHandle) 반환. platform.ctx.waitUntil 유무로
  Workers 판별 — Workers 는 요청당 연결 생성 + dispose, Node(adapter-node)는
  기존 전역 재사용 유지. mysql 은 Workers 에서 disableEval:true 추가.
  sqlite(libSQL HTTP)/d1 은 소켓 장기점유가 없어 그대로.
- hooks.server.ts: 응답 후 ctx.waitUntil(dispose()) 로 요청당 연결 정리
  (응답 지연 없음). 아울러 DB 초기화 게이트가 platform.env.DB(D1 전용)로
  하드코딩돼 postgres 배포에서 초기화가 스킵될 수 있던 것을 방언 인식으로 교체.

Cloudflare Hyperdrive + postgres-js 권장 패턴(요청당 연결, max:5,
ctx.waitUntil(sql.end()))과 일치.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
@mack-erel
mack-erel merged commit 4b5b7c6 into main Jul 2, 2026
6 checks passed
@mack-erel
mack-erel deleted the fix/workers-db-connection-per-request branch July 2, 2026 03:58
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant