测验:并发取款、限额与事务
한국어 원문으로 표시합니다.
출금 처리가 잔액을 SELECT 로 읽어 확인한 뒤 따로 UPDATE 로 차감한다. 같은 계좌에 요청 두 건이 겹쳤을 때 벌어지는 일로 가장 정확한 것은?
- 두 요청 중 하나는 잠금 때문에 실패하므로 한 건만 승인된다
- UPDATE 가 순서대로 실행되므로 잔액은 언제나 정확하게 유지된다
- 둘 다 같은 잔액을 읽고 각각 승인해, 차감은 두 번 반영돼 잔액이 음수가 될 수 있다
- SELECT 가 읽기 잠금을 걸어 두므로 뒤에 온 요청은 대기했다가 새 잔액을 읽는다
조건부 UPDATE 로 고친 뒤, 승인 여부를 무엇으로 판정해야 하는가?
- 갱신 문장이 실제로 바꾼 행 수(changes 또는 rowcount)
- UPDATE 직전에 읽어 둔 잔액이 요청 금액 이상인지
- UPDATE 직후에 다시 읽은 잔액이 음수가 아닌지
- 트랜잭션이 예외 없이 커밋됐는지
SQLite 에서 기본값인 BEGIN DEFERRED 로 트랜잭션을 열고 SELECT 를 먼저 한 다음 UPDATE 를 친다. 공식 문서가 설명하는 위험은?
- 읽기 트랜잭션이라 UPDATE 가 조용히 무시되고 커밋 때 사라진다
- SELECT 가 EXCLUSIVE 잠금을 잡아 다른 연결이 읽지도 못하게 된다
- DEFERRED 는 커밋 시점까지 아무 잠금도 잡지 않아 두 트랜잭션이 서로의 결과를 덮어쓴다
- 읽기 트랜잭션을 쓰기로 승격해야 하는데, 다른 연결이 이미 고치고 있으면 승격이 불가능해 SQLITE_BUSY 로 실패한다
동시 출금 결함을 고쳤다는 것을 증명하려 한다. 재현 방법으로 가장 알맞은 것은?
- 요청 10만 건을 몇 분 동안 흘려 보내 마이너스 잔액이 나오는지 지켜본다
- 라운드마다 두 요청이 모두 읽은 뒤에 쓰도록 맞춰, 같은 잔액을 보게 만든다
- 요청 사이에 임의의 지연을 넣고 여러 번 돌려 평균적으로 몇 번 나는지 센다
- 운영 로그에서 과거에 마이너스 잔액이 났던 시각의 트래픽을 그대로 재생한다
일일 누적 한도를 구현한다. 다음 중 한도가 거짓말을 하게 될 위험이 가장 큰 설계는?
- 원장에서 그날 출금을 세는 하위 질의를 UPDATE 의 조건에 넣는다
- 잔액 조건과 한도 조건을 같은 UPDATE 의 WHERE 에 함께 붙인다
- BEGIN IMMEDIATE 로 트랜잭션을 열고 한도 판정과 원장 기록을 함께 커밋한다
- 누적 사용액을 별도 칸에 두고 애플리케이션이 읽어 판단한 뒤 따로 더한다
재시도 정책을 정한다. 다시 걸어야 하는 실패와 걸면 안 되는 실패를 바르게 가른 것은?
- 잠금 경합은 상한을 두고 다시 걸고, 잔액 부족이나 한도 초과는 다시 걸지 않는다
- 모든 데이터베이스 오류에 무한 재시도를 걸어 요청을 잃지 않게 한다
- 잔액 부족은 곧 입금될 수 있으니 다시 걸고, 잠금 경합은 즉시 실패로 돌려준다
- 재시도는 응답 시간을 늘리므로 어떤 실패에도 걸지 않고 곧바로 오류를 낸다