짐 그레이의 트랜잭션 처리 아이디어를 실용적으로 살펴보고 은행·커머스·SaaS 시스템에서 ACID 원칙이 신뢰성을 어떻게 지키는지 설명합니다.

짐 그레이는 겉보기에는 단순한 질문에 집착한 컴퓨터 과학자였습니다: 많은 사용자가 동시에 시스템을 쓸 때—그리고 실패는 불가피할 때—결과를 어떻게 정확하게 유지할 것인가?
그의 트랜잭션 처리에 대한 연구는 데이터베이스를 “운이 좋을 때만 가끔 맞는 것”에서 실제로 비즈니스를 세울 수 있는 인프라로 바꿔놓았습니다. 그가 대중화한 개념들—특히 ACID 속성—은 제품 회의에서 ‘트랜잭션’이라는 단어를 써본 적이 없어도 곳곳에 등장합니다.
신뢰할 수 있는 시스템은 사용자가 단지 화면이 아니라 결과를 신뢰할 수 있는 시스템입니다.
다시 말해: 올바른 잔액, 정확한 주문, 누락된 기록 없음.
큐, 마이크로서비스, 서드파티 결제를 쓰는 현대 제품들조차도 핵심 순간에는 트랜잭션 사고에 의존합니다.
우리는 개념을 실용적으로 유지합니다: ACID가 무엇을 보호하는지, 버그가 어디에 숨어 있는지(격리와 동시성), 그리고 로그와 복구가 실패를 어떻게 견디게 하는지.
또한 현대적 절충안—ACID 경계를 어디에 둘지, 분산 트랜잭션이 언제 가치가 있는지, 사가(Saga), 재시도, 멱등성과 같은 패턴이 과도한 설계 없이 ‘충분한’ 일관성을 언제 제공하는지—도 다룹니다.
트랜잭션은 다단계 비즈니스 동작을 하나의 “예/아니오” 단위로 취급하는 방법입니다. 모든 것이 성공하면 커밋합니다. 무엇인가 잘못되면 마치 일어나지 않았던 것처럼 롤백합니다.
당신이 당좌계좌에서 저축계좌로 $50을 옮긴다고 상상해보세요. 그건 한 번의 변경이 아니라 적어도 두 번입니다:
시스템이 ‘한 단계 업데이트’만 한다면 차감은 성공하고 입금 전에 실패할 수 있습니다. 이제 고객은 $50이 없어지고, 고객지원 티켓이 쌓입니다.
일반적인 체크아웃은 주문 생성, 재고 예약, 결제 승인, 영수증 기록을 포함합니다. 각 단계는 다른 테이블(혹은 서로 다른 서비스)을 건드립니다. 트랜잭션 사고가 없다면 “결제됨”으로 표시된 주문이지만 예약된 재고가 없거나, 주문이 생성되지 않았는데 재고가 예약되는 등 이상한 상태가 생길 수 있습니다.
실패는 좀처럼 편한 순간에 발생하지 않습니다. 흔한 분기점:
트랜잭션 처리는 단순한 약속을 보증하기 위해 존재합니다: 비즈니스 동작의 모든 단계가 함께 적용되거나 아무 것도 적용되지 않는다. 이 약속이 바로 신뢰의 기초입니다—돈을 옮기든, 주문을 하든, 구독을 변경하든 마찬가지입니다.
ACID는 ‘트랜잭션’이 신뢰할 만하게 느껴지게 하는 보호 목록입니다. 마케팅 용어가 아니라 중요한 데이터를 변경할 때 무슨 일이 일어나는지에 대한 약속입니다.
원자성은 트랜잭션이 완전히 완료되거나 흔적을 남기지 않는다는 뜻입니다.
은행 이체를 생각해보세요: 계정 A에서 $100을 차감하고 계정 B에 $100을 입금합니다. 시스템이 차감 후 입금 전에 크래시하면, 원자성은 전체 이체를 롤백(아무도 돈을 ‘잃지’ 않도록)하거나 전체 이체가 완료되도록 보장합니다. 한쪽만 일어난 상태는 유효한 종료 상태가 아닙니다.
일관성은 커밋 후 데이터 규칙(제약과 불변식)이 유지된다는 뜻입니다.
예: 당사 제품이 대체로 마이너스 잔액을 금지한다면 잔액은 음수가 될 수 없습니다; 이체의 차변과 대변 합이 일치해야 합니다; 주문 총액은 항목 합 + 세금과 같아야 합니다. 일관성은 일부는 DB(제약조건)의 몫이고 일부는 애플리케이션(비즈니스 규칙)의 몫입니다.
격리성은 여러 트랜잭션이 동시에 실행될 때 보호해 줍니다.
예: 두 고객이 마지막 수량을 동시에 사려고 합니다. 적절한 격리성이 없다면 둘 다 “재고 = 1”을 보고 둘 다 성공해 재고가 -1이 되거나 수동 수정이 필요해질 수 있습니다.
영속성은 “커밋됨”을 본 순간 그 결과가 크래시나 전원 손실 후에도 사라지지 않는다는 뜻입니다. 영수증에 이체 성공이 쓰여 있다면 재부팅 후에도 원장은 이를 보여야 합니다.
‘ACID’는 단일 온/오프 스위치가 아닙니다. 서로 다른 시스템과 격리 수준은 서로 다른 보장을 제공하며, 어떤 작업에 어느 보장을 적용할지 선택하는 일이 종종 필요합니다.
사람들이 ‘트랜잭션’에 관해 이야기할 때 은행업이 가장 명확한 예입니다: 사용자는 잔액이 항상 정확하길 기대합니다. 은행 앱은 약간 느려도 괜찮지만, 틀리면 안 됩니다. 한 번의 잘못된 잔액이 연체료, 놓친 결제, 긴 수작업으로 이어질 수 있습니다.
간단한 은행 이체도 여러 단계로 되어 있고 모두 함께 성공하거나 실패해야 합니다:
ACID 사고는 이를 하나의 단위로 취급합니다. 네트워크 문제, 서비스 크래시, 검증 오류 등 어떤 단계가 실패하면 시스템은 부분적으로 성공하지 않아야 합니다. 그렇지 않으면 A에서 돈이 빠졌지만 B에는 표시되지 않는 상태, B에 돈이 생겼지만 차감 기록이 없는 상태, 또는 어떤 일이 일어났는지 설명할 감사 기록이 없는 상태가 생깁니다.
많은 제품에서는 작은 불일치를 다음 릴리스에서 패치할 수 있습니다. 은행에서는 ‘나중에 고치기’가 분쟁, 규제 위험, 수작업 운영으로 번집니다. 지원 티켓이 급증하고 엔지니어는 인시던트 콜에 소환되며 운영팀은 불일치된 기록을 맞추느라 수시간을 소비합니다.
숫자를 복구할 수 있더라도 그 기록을 설명해야 합니다.
그래서 은행들은 원장과 append-only 기록에 의존합니다: 기록을 덮어쓰는 대신 차변과 대변의 연속을 기록합니다. 불변 로그와 명확한 감사 추적은 복구와 조사 가능성을 만듭니다.
조정(reconciliation)은 독립된 진실 소스를 비교해 문제가 생겼을 때 언제 어디서 차이가 발생했는지 파악하는 안전장치 역할을 합니다.
정확성은 신뢰를 삽니다. 또한 지원량을 줄이고 해결을 빠르게 합니다: 문제가 발생했을 때 깔끔한 감사 추적과 일관된 원장 항목은 “무슨 일이 있었나?”에 빨리 답하게 하고 추측 없이 고칠 수 있게 합니다.
이커머스는 피크 트래픽까지는 단순해 보입니다: 같은 마지막 아이템이 열 개의 장바구니에 있고, 고객은 페이지를 새로고침하며 결제 제공자가 타임아웃을 일으킵니다. 이 지점에서 짐 그레이의 트랜잭션 처리 사고가 실용적이고 현실적인 방식으로 드러납니다.
일반적인 체크아웃은 여러 상태를 건드립니다: 재고 예약, 주문 생성, 결제 청구. 높은 동시성에서는 각 단계가 자체적으로는 올바르더라도 전체적으로 나쁜 결과를 낳을 수 있습니다.
재고를 격리 없이 차감하면 두 체크아웃이 모두 “1개 남음”을 읽고 둘 다 성공해 과판매가 발생합니다. 결제를 먼저 청구하고 주문 생성이 실패하면 고객에게 과금만 되고 이행할 것이 없습니다.
ACID는 데이터베이스 경계에서 가장 큰 도움을 줍니다: 주문 생성과 재고 예약을 하나의 DB 트랜잭션으로 감싸면 둘 다 커밋되거나 둘 다 롤백됩니다. 또한 제약조건(예: “재고는 0 아래로 내려갈 수 없음”)으로 애플리케이션 코드가 잘못 동작해도 DB가 불가능한 상태를 거부하도록 만들 수 있습니다.
네트워크가 응답을 잃고, 사용자가 더블클릭하고, 백그라운드 잡이 재시도합니다. 그래서 시스템 간에서 “정확히 한 번” 처리는 어려우며 목표는: 금전 이동은 최대 한 번(at most once), 나머지는 안전한 재시도입니다.
결제 프로세서와 멱등성 키를 사용하고 주문에 연결된 영구적인 “지불 의도(payment intent)” 기록을 저장하세요. 서비스가 재시도해도 중복 청구가 발생하지 않습니다.
반품, 부분 환불, 차지백은 엣지 케이스가 아니라 비즈니스 사실입니다. 명확한 트랜잭션 경계는 이들을 더 쉽게 만듭니다: 모든 조정은 주문, 결제, 감사 추적과 신뢰성 있게 연결되므로 문제가 생겼을 때 조정이 설명 가능합니다.
SaaS 비즈니스는 고객이 지불한 것을 즉시 예측 가능하게 사용할 수 있다는 약속 위에 서 있습니다. 업그레이드, 다운그레이드, 중간 과금(proration), 환불, 비동기 결제 이벤트가 섞이면 간단해 보이던 것이 복잡해집니다. ACID식 사고는 ‘청구의 진실’과 ‘제품의 진실’을 일치시키는 데 도움을 줍니다.
요금제 변경은 종종 일련의 동작을 트리거합니다: 인보이스 생성/조정, 프레이션 기록, 결제 시도, 권한(기능/좌석/제한) 업데이트. 부분적인 성공이 용납되지 않는다면 이들을 하나의 단위로 다루세요.
업그레이드 인보이스는 생성되었지만 권한이 업데이트되지 않으면(또는 그 반대면) 고객은 지불했지만 접근을 잃거나 지불하지 않았는데 접근을 얻는 일이 발생합니다.
실용적인 패턴은 청구 결정(새 요금제, 적용일, 프레이션 항목)과 권한 결정을 함께 영구 저장하고, 그 커밋된 기록을 바탕으로 다운스트림 프로세스를 실행하는 것입니다. 결제 확인이 나중에 도착하면 기록을 안전하게 앞으로 이동시켜 히스토리를 다시 쓰지 않아도 됩니다.
멀티테넌트 시스템에서는 격리성이 단순한 학술적 문제가 아닙니다: 한 고객의 무거운 활동이 다른 고객을 블록하거나 오염시키면 안 됩니다. 테넌트 스코프의 키, 테넌트별 명확한 트랜잭션 경계, 적절히 선택된 격리 수준을 사용해 테넌트 A의 갱신 급증이 테넌트 B의 일관성 없는 조회를 만들지 않도록 하세요.
지원 티켓은 보통 “왜 결제되었나요?” 또는 “왜 X에 접근할 수 없나요?”로 시작합니다. 누가 언제 무엇을 변경했는지(사용자, 관리자, 자동화)를 append-only 감사 로그로 남기고 이를 인보이스와 권한 전환에 연결하세요.
이렇게 하면 인보이스는 “Pro”로 되어 있는데 권한은 “Basic”인 식의 조용한 drift를 막고, 조정이 조사 작업이 아니라 단순한 쿼리가 됩니다.
격리성은 ACID의 ‘I’이며 시스템이 미묘하고 비용이 큰 방식으로 실패하는 곳입니다. 핵심 아이디어는 단순합니다: 많은 사용자가 동시에 행동하지만 각 트랜잭션은 혼자 실행된 것처럼 동작해야 합니다.
두 명의 계산원과 한 개 남은 상품인 가게를 상상하세요. 만약 두 계산원이 동시에 재고를 확인하고 둘 다 “1개 남음”을 보면 둘 다 팔아버릴 수 있습니다. 아무 것도 ‘크래시’하진 않았지만 결과는 잘못되었습니다—중복 지출과 같은 문제입니다.
데이터베이스도 동일한 문제에 직면합니다. 두 트랜잭션이 같은 행을 동시에 읽고 갱신하면 문제가 생깁니다.
대부분 시스템은 안전성과 처리량 사이의 절충으로 격리 수준을 선택합니다:
실수가 재정적 손실, 법적 노출, 고객에게 보이는 불일치를 만든다면 더 강한 격리(또는 명시적 락/제약)를 사용하세요. 최악의 경우가 일시적 UI 글리치라면 약한 수준이 허용될 수 있습니다.
높은 격리는 데이터베이스가 더 많은 조정—대기, 락, 트랜잭션 중단/재시도—을 해야 하므로 처리량을 낮출 수 있습니다. 그 비용도 실재하며, 잘못된 데이터의 비용도 실재합니다.
시스템이 크래시했을 때 가장 중요한 질문은 ‘왜 크래시했나?’가 아니라 ‘재시작 후 우리는 어떤 상태에 있어야 하는가?’입니다. 짐 그레이의 트랜잭션 처리 연구는 그 답을 실용적으로 만들었습니다: 영속성은 규율 있는 로깅과 복구를 통해 달성됩니다.
트랜잭션 로그(종종 WAL이라고 부름)는 변경의 append-only 기록입니다. 데이터 파일이 쓰이는 도중 전원이 나가더라도 의도와 순서를 보존하므로 복구에 중심적입니다.
재시작 시 데이터베이스는:
이 때문에 “우리는 커밋했다”는 말은 서버가 깨끗하게 종료되지 않았더라도 진실일 수 있습니다.
WAL은 이렇게 말합니다: 데이터 페이지가 쓰이기 전에 로그가 내구성 있는 저장소에 플러시되어야 한다. 실제로 “커밋”은 관련 로그 레코드가 안전하게 디스크(또는 다른 영구 저장소)에 있는 것을 보장하는 것과 연결됩니다.
크래시가 커밋 직후에 발생하면 복구는 로그를 재생해 커밋된 상태를 재구성할 수 있습니다. 크래시가 커밋 이전에 발생하면 로그는 롤백에 도움을 줍니다.
백업은 스냅샷(시점 복사)입니다. 로그는 변경의 역사입니다. 백업은 파괴적 사건(나쁜 배포, 테이블 삭제, 랜섬웨어)에 대비하고, 로그는 최근 커밋된 작업을 복구하고 시점 복구(point-in-time recovery)를 지원합니다: 백업을 복원한 뒤 로그를 재생해 선택한 시점까지 복구합니다.
한 번도 복원해본 적 없는 백업은 계획이 아닙니다. 스테이징 환경에서 정기적으로 복원 연습을 스케줄하고 데이터 무결성을 검증하며 실제 복구 시간이 RTO/RPO 요구를 만족하는지 측정하세요. 만약 만족하지 못하면 보존 정책, 로그 전송, 백업 주기를 조정하세요—사건이 발생한 뒤에 배우는 게 아니라 미리 배우는 게 낫습니다.
ACID는 하나의 데이터베이스가 트랜잭션의 ‘진실 출처(source of truth)’ 역할을 할 때 가장 잘 작동합니다. 하나의 비즈니스 동작을 여러 서비스(결제, 재고, 이메일, 분석 등)에 흩어놓는 순간, 실패는 더 이상 깔끔한 ‘성공’이나 ‘오류’처럼 보이지 않습니다.
분산 환경에서는 부분 실패를 가정해야 합니다: 한 서비스는 커밋했는데 다른 서비스가 크래시할 수 있고, 네트워크 문제는 진짜 결과를 숨길 수 있습니다. 더욱 문제가 되는 점은 타임아웃이 모호하다는 것—상대가 실패한 건지 느린 건지 알기 어렵습니다.
그 모호성이 중복 청구, 과판매, 누락된 권한을 낳습니다.
2PC는 여러 데이터베이스가 ‘하나’처럼 커밋하려는 시도입니다.
팀들은 보통 2PC를 피하려 합니다. 느리고 락을 오래 잡아 처리량을 떨어뜨리며 조정자가 병목이 될 수 있고 시스템 간의 결합을 강하게 만듭니다.
일반적인 접근법은 ACID 경계를 작게 유지하고 교차 서비스 작업을 명시적으로 관리하는 것입니다:
가능하면 하나의 데이터베이스 내부에 강력한 보장(ACID)을 두고, 그 경계를 넘는 모든 것은 재시도, 조정, 그리고 ‘이 단계가 실패하면 무슨 일이 일어나는가?’를 명확히 정의합니다.
실패는 보통 깔끔한 “안 일어났다” 형태가 아닙니다. 요청이 부분적으로 성공하고 클라이언트는 타임아웃을 겪은 뒤 누군가(브라우저, 모바일 앱, 잡 러너, 파트너 시스템)가 재시도합니다.
보호 장치가 없으면 재시도는 가장 성가신 버그를 만듭니다: 얼핏 보면 올바른 코드가 때때로 이중 청구, 이중 배송, 이중 권한 부여를 합니다.
**멱등성(idempotency)**은 같은 작업을 여러 번 수행해도 한 번만 수행한 것과 같은 최종 결과를 내는 성질입니다. 사용자 관점에서는 ‘중복 재시도해도 부작용이 없는 것’입니다.
도움되는 규칙: GET은 자연스럽게 멱등적이어야 하고, 많은 POST 동작은 설계하지 않으면 멱등성이 없습니다.
보통 여러 메커니즘을 조합합니다:
Idempotency-Key: ...). 서버는 그 키로 결과를 저장하고 재요청에 같은 결과를 반환합니다.order_id 당 하나의 결제)이들은 중복 검사와 효과가 가능한 한 같은 DB 트랜잭션 안에 있을 때 가장 잘 동작합니다.
타임아웃이 발생했다고 해서 트랜잭션이 롤백된 것은 아닙니다; 커밋되었지만 응답이 사라졌을 수 있습니다. 그래서 재시도 로직은 서버가 성공했을 수도 있음을 가정해야 합니다.
일반적인 패턴은: 먼저 멱등성 레코드를 쓰거나 잠그고, 부작용을 수행한 뒤 완료 표시를 하는 것입니다—가능하면 하나의 트랜잭션 안에서. 결제 게이트웨이 호출처럼 모든 것을 한 트랜잭션에 넣을 수 없을 때는(외부 호출이 필요할 때) 영구적인 “의도(intent)”를 저장하고 나중에 조정합니다.
시스템이 ‘값이 들쑥날쑥하다’고 느껴지면 근본 원인은 종종 트랜잭션 사고의 부재입니다. 흔한 증상은 결제에 상응하는 주문이 없이 주문이 생기거나, 동시 체크아웃 후 음수 재고, 원장·인보이스·분석 간 합계 불일치 등입니다.
먼저 불변식을 적으세요—항상 참이어야 하는 사실들. 예: “재고는 절대 0 아래로 내려가지 않는다”, “주문은 미결제이거나 결제됨(둘 다 아님)”, “모든 잔액 변경은 대응하는 원장 항목이 있어야 한다.”
그런 다음 그 불변식을 보호하기 위해 원자적으로 묶어야 할 최소 단위를 트랜잭션 경계로 정의하세요. 한 사용자 동작이 여러 행/테이블을 건드린다면 무엇이 함께 커밋되어야 하고 무엇은 안전하게 연기할 수 있는지 결정하세요.
마지막으로 부하 시 충돌을 어떻게 처리할지 선택하세요:
동시성 버그는 해피 패스 테스트에서는 좀처럼 드러나지 않습니다. 압박을 주는 테스트를 추가하세요:
보호하지 못하면 측정할 수 없습니다. 유용한 지표에는 데드락, 락 대기 시간, 롤백 비율(특히 배포 후 급등), 소스-오브-트루스 테이블 간의 조정 차이(원장 vs 잔액, 주문 vs 결제) 등이 있습니다. 이런 메트릭은 고객이 ‘돈이 사라졌다’고 신고하기 몇 주 전에도 경고를 줄 수 있습니다.
짐 그레이의 지속적 기여는 단지 속성 집합만이 아니라 “무엇이 절대 일어나면 안 되는가”에 대한 공통 어휘였습니다. 팀이 필요한 보장(원자성, 일관성, 격리성, 영속성)을 이름으로 표현할 수 있을 때, ‘신뢰할 수 있어야 한다’는 모호한 논쟁은 멈추고 ‘이 업데이트는 저 결제와 함께 원자적이어야 한다’ 같은 실행 가능한 논의로 바뀝니다.
사용자가 단일하고 결정적인 결과를 기대하고 실수가 비용을 치르게 하는 경우 전체 트랜잭션을 사용하세요:
여기서는 처리량을 위해 보장을 약하게 하는 최적화가 결국 지원 티켓, 수동 조정, 신뢰 상실로 비용을 옮길 뿐입니다.
일시적 불일치가 허용되고 쉽게 치유할 수 있는 곳에서는 보장을 완화하세요:
핵심은 ‘진실의 원천’에 명확한 ACID 경계를 두고 나머지는 뒤처지게 하는 것입니다.
프로토타입 단계거나 레거시 파이프라인을 재구축하는 경우, 트랜잭션과 제약조건을 1등 시민으로 다루는 스택에서 시작하는 것이 도움이 됩니다. 예를 들어 Koder.ai는 간단한 채팅으로 React 프론트엔드와 Go + PostgreSQL 백엔드를 생성할 수 있어(멱등성 레코드, 아웃박스 테이블, 롤백 안전 워크플로 포함) 실제 트랜잭션 경계를 초기부터 만들고 전체 마이크로서비스 전개에 투자하기 전에 검증할 수 있게 해줍니다.
더 많은 패턴과 체크리스트가 필요하면 /blog에서 이러한 기대치를 링크하세요. 신뢰성 등급을 티어별로 제공한다면 /pricing에 명시해 고객이 어떤 정합성 보장을 구매하는지 알 수 있도록 하세요.
짐 그레이는 트랜잭션 처리 기술을 실용적이고 널리 이해되도록 만든 컴퓨터 과학자였습니다. 그의 유산은 중요한 다단계 작업(송금, 결제, 구독 변경 등)이 동시성 또는 실패 상황에서도 정확한 결과를 내도록 설계해야 한다는 사고방식입니다.
일상적인 제품 관점에서는: ‘미스터리 상태’가 줄고, 조정 작업이 적어지며, “커밋된(commit)”이 실제로 무엇을 의미하는지에 대한 명확한 보장이 생깁니다.
트랜잭션은 여러 업데이트를 하나의 모두 또는 전무(all-or-nothing) 단위로 묶는 것입니다. 모든 단계가 성공하면 커밋하고, 하나라도 실패하면 롤백합니다.
흔한 사례:
ACID는 트랜잭션을 신뢰할 수 있게 만드는 일련의 보장입니다:
이건 단일 스위치가 아니며—어떤 작업에 어느 정도의 보장이 필요한지 선택하는 문제입니다.
대부분의 ‘운영 환경에서만 발생하는’ 버그는 약한 격리성 하에서 발생합니다.
일반적인 문제 패턴:
실용적인 해결책: 비즈니스 리스크에 맞춰 격리 수준을 정하고, 필요하면 제약조건이나 락으로 안전망을 둡니다.
먼저 평범한 영어 문장으로 불변식(invariants)을 적으세요(항상 참이어야 하는 사실). 그런 다음 그 불변식을 보호하기 위한 가장 작은 트랜잭션 범위를 정의합니다.
함께 잘 동작하는 메커니즘:
제약조건은 애플리케이션 코드가 동시성에서 실수할 때의 안전망으로 다루세요.
Write-ahead logging(WAL)은 데이터베이스가 ‘커밋’을 충돌 후에도 보장하도록 만드는 방식입니다.
운영적 관점:
이 때문에 설계상 ‘커밋됐다면 전력 손실 후에도 남아 있다’고 말할 수 있습니다.
백업은 시점 스냅샷이고, 로그는 그 이후의 변경 이력입니다.
실용적 복구 전략:
한 번도 복원해보지 않은 백업은 계획이 아니라 희망에 불과합니다.
분산 트랜잭션은 여러 시스템이 ‘하나로서’ 커밋되게 하려 하지만, 부분 실패와 모호한 타임아웃 때문에 어렵습니다.
2PC(Two-phase commit)는 보통 다음을 초래합니다:
정말 교차 시스템 원자성을 필요로 하고 운영 비용을 감수할 수 있을 때만 고려하세요.
로컬 ACID 경계를 작게 유지하고 서비스 간 조정을 명시적으로 처리하는 편이 일반적으로 확장성이 좋습니다.
흔한 패턴:
이것으로 재시도와 실패 시 예측 가능한 동작을 만들면서 전역 락을 피할 수 있습니다.
타임아웃은 ‘거절’이 아니라 ‘성공했을 수 있음’을 의미할 수 있습니다. 재시도 설계는 안전해야 합니다.
중복을 막는 도구들:
최선의 방식은 중복 검사와 상태 변경이 가능한 한 같은 DB 트랜잭션 내에 있도록 하는 것입니다.