runlot

세션

호출마다 새 세션입니다. 왜 그런지와, 세션을 직접 쥐어야 할 때.

기본은 호출마다 새 세션

await env.db.exec("select 1");

이 한 줄은 연결 → 질의 → 동기화 → 해제를 모두 합니다. 그래서 호출 사이에 아무것도 남지 않습니다.

  • BEGIN 이 다음 호출로 이어지지 않습니다
  • SET 이 다음 호출에 남지 않습니다
  • prepared statement 와 temp table 도 마찬가지입니다

왜 재사용하지 않나요

세션을 재사용하려면 앞 요청이 남긴 상태를 완전히 지웠다는 것을 보장해야 합니다. DISCARD ALL 이 그 일을 하는데, 이 엔진에서 그것이 무엇을 지우는지 아직 증명하지 않았습니다.

증명 전에 재사용하면, 어떤 요청이 남긴 SET 이나 temp table 이 다음 사용자의 요청에 조용히 섞일 수 있습니다. 그래서 지금은 닫고 다시 여는 쪽을 고릅니다 — 오염이 다음 요청으로 넘어갈 길이 없습니다.

값을 치릅니다

마디비용
새 세션 바닥2.6 ms
$1 파라미터+0.6 ms
실제 표 (카탈로그 워밍)+5 ms
쓰기 (커밋 + flush)+9 ms

카탈로그 워밍 5 ms 는 호출마다 새 세션을 여는 탓이고, 세션 재사용이 증명되면 걷힐 자리입니다. 참고로 wire 로 직접 붙어 세션을 유지하면 같은 질의가 0.55 ms 입니다.

세션을 직접 쥐기

여러 문장이 한 세션에 있어야 하면 핸들을 받습니다.

const s = await env.db.session();
try {
  await s.exec("begin");
  await s.exec("insert into a values ($1)", [1]);
  await s.exec("insert into b values ($1)", [2]);
  await s.exec("commit");
} finally {
  await s.close();
}

@runlot/pg 를 쓰시면 같은 것이 pool.connect() 입니다.

규칙

  • close()멱등입니다. 두 번 불러도 됩니다.
  • close() 는 열려 있던 블록을 롤백합니다. 커밋은 여러분이 COMMIT 을 보내는 것이지 close 의 부수 효과가 아닙니다.
  • 닫힌 핸들로 문장을 보내면 08003 입니다.
  • 문장 없이 30 초가 지나면 세션이 회수되고 열린 블록은 롤백됩니다.

요청보다 오래 살면 안 됩니다

세션 핸들을 전역에 두거나 요청 밖으로 넘기지 마세요. 30 초 TTL 에 걸려 롤백되고, 그 사이 다른 요청을 막습니다.

동시성은 대기열이 됩니다

한 프로젝트의 데이터베이스는 차선이 하나입니다. 세션을 오래 쥔 요청이 있으면 다른 요청이 줄을 섭니다. 긴 트랜잭션은 그 프로젝트 전체의 응답 시간이 됩니다.

문장 하나의 상한은 기본 10 초입니다. 그보다 오래 걸리는 질의는 잘립니다.

이 페이지에서