데이터데이터베이스
트랜잭션
하나의 데이터베이스 세션 안에서는 일반적인 PostgreSQL 트랜잭션을 사용할 수 있습니다. 세션이 다르면 트랜잭션도 분리됩니다.
지원하는 기능
const s = await env.db.session();
try {
await s.exec("begin");
await s.exec("update accounts set balance = balance - $1 where id = $2", [100, 1]);
await s.exec("update accounts set balance = balance + $1 where id = $2", [100, 2]);
await s.exec("commit");
} catch (e) {
await s.exec("rollback");
throw e;
} finally {
await s.close();
}BEGIN,COMMIT,ROLLBACKSAVEPOINT와 savepoint 롤백SET TRANSACTION ISOLATION LEVEL- 여섯 ORM의 트랜잭션 API
- 세션 안의
SET, temporary table,PREPARE
주의: 호출을 나누면 트랜잭션도 나뉩니다
env.db.exec을 두 번 호출한 사이에는 하나의 트랜잭션이 유지되지 않습니다. 호출마다 새 세션을 사용하므로 BEGIN이 다음 호출에 적용되지 않습니다. @runlot/pg의 pool.query()도 같습니다. 트랜잭션에는 pool.connect()를 사용하세요.시간 함수
now()는 트랜잭션이 시작한 시각을 반환합니다. 같은 트랜잭션에서 여러 번 호출해도 값은 같습니다. 현재 시각이 필요하면 clock_timestamp()를 사용하세요.
트랜잭션을 길게 유지하지 마세요
프로젝트당 데이터베이스 요청은 한 번에 하나씩 처리됩니다. 긴 트랜잭션은 프로젝트의 다른 요청을 대기시킵니다. 트랜잭션 안에서 외부 API를 호출하는 작업은 피하세요.
SQL 문을 실행하지 않은 채 30초가 지나면 세션을 회수하고 열린 트랜잭션을 롤백합니다. 하나의 요청 안에서 세션을 열고 닫는 방식을 권장합니다.
오류 코드로 분기하기
const r = await s.tryExec("insert into users (email) values ($1)", [email]);
if (!r.ok && r.error.code === "23505") {
await s.exec("rollback");
return new Response("이미 있는 이메일입니다", { status: 409 });
}Prisma만 고유성 제약 조건 위반을 P2002로 변환합니다. 나머지 ORM은 SQLSTATE 23505를 그대로 반환합니다.