<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>Job Runner on jyukki's Blog</title><link>https://jyukki.com/tags/job-runner/</link><description>Recent content in Job Runner on jyukki's Blog</description><generator>Hugo -- 0.147.0</generator><language>ko-kr</language><lastBuildDate>Fri, 10 Apr 2026 00:00:00 +0000</lastBuildDate><atom:link href="https://jyukki.com/tags/job-runner/index.xml" rel="self" type="application/rss+xml"/><item><title>백엔드 커리큘럼 심화: Advisory Lock 실무 플레이북, DB 락으로 어디까지 해결하고 언제 다른 조율 방식으로 넘어갈까</title><link>https://jyukki.com/learning/deep-dive/deep-dive-advisory-locks-coordination-playbook/</link><pubDate>Fri, 10 Apr 2026 00:00:00 +0000</pubDate><guid>https://jyukki.com/learning/deep-dive/deep-dive-advisory-locks-coordination-playbook/</guid><description>중복 실행 방지, 단일 작업자 선출, 배치 겹침 차단 같은 문제를 Advisory Lock으로 해결할 때의 설계 기준과 한계를 숫자 중심으로 정리합니다.</description><content:encoded><![CDATA[<p>실무에서 &ldquo;딱 한 번만 실행되어야 하는 작업&quot;은 생각보다 많습니다. 같은 배치가 두 번 돌면 정산 금액이 어긋나고, 같은 사용자의 환불 처리기가 동시에 돌면 상태 전이가 꼬입니다. 메시지 소비기 두 대가 같은 집계 작업을 동시에 잡아도 결과는 비슷하게 망가집니다. 그런데 이 문제를 볼 때 많은 팀이 너무 무거운 해법부터 떠올립니다. Redis 분산 락, 별도 coordinator, workflow engine, leader election 같은 것들입니다.</p>
<p>문제는 대부분의 시스템이 그 정도 복잡도까지는 필요 없다는 점입니다. 이미 서비스의 진실 원본이 DB에 있고, 조율 대상도 그 DB 기준으로 판단되는 경우라면 <strong>Advisory Lock이 가장 싸고 빠른 1차 해법</strong>인 경우가 많습니다. 반대로 락 유지 시간이 길거나, 연결 풀과 세션 경계가 불명확하거나, 조율 범위가 여러 시스템으로 퍼지면 Advisory Lock은 금방 발목을 잡습니다.</p>
<p>이 글은 &ldquo;Advisory Lock을 써도 되나?&rdquo;, &ldquo;어디까지 쓰고 언제 버려야 하나?&ldquo;를 실무 기준으로 정리한 플레이북입니다. 특히 <strong>세션/트랜잭션 경계, 락 키 설계, 풀링 환경, 관측 지표, 대체 패턴 전환 기준</strong>에 집중합니다.</p>
<h2 id="이-글에서-얻는-것">이 글에서 얻는 것</h2>
<ul>
<li>Advisory Lock이 해결하는 문제와 해결하지 못하는 문제를 구조적으로 구분할 수 있습니다.</li>
<li>배치 중복 실행 방지, 단일 작업자 선출, 같은 aggregate 동시 수정 차단에 적용할 수 있는 실무 기준을 얻습니다.</li>
<li>락 대기 시간, 점유 시간, 실패율, 풀링 주의점 같은 운영 기준을 숫자와 우선순위로 바로 가져갈 수 있습니다.</li>
</ul>
<h2 id="핵심-개념이슈">핵심 개념/이슈</h2>
<h3 id="1-advisory-lock은-데이터-락이-아니라-합의용-락이다">1) Advisory Lock은 &ldquo;데이터 락&quot;이 아니라 &ldquo;합의용 락&quot;이다</h3>
<p>행 락(row lock)이나 테이블 락은 특정 데이터 변경을 보호합니다. 반면 Advisory Lock은 DB가 제공하는 <strong>임의 키 기반의 협조적(cooperative) 락</strong>입니다. 즉 DB가 자동으로 무결성을 보장해 주는 게 아니라, 애플리케이션이 같은 규칙으로 같은 키를 잡는다는 전제 위에서만 의미가 있습니다.</p>
<p>예를 들어 아래 같은 상황에서 유용합니다.</p>
<ul>
<li><code>daily-settlement:2026-04-10</code> 배치가 한 번만 돌도록 보장</li>
<li><code>user:12345:refund</code> 처리를 동시에 하나만 허용</li>
<li>동일 상품 재고 보정 잡이 같은 sku에 대해 중복 실행되지 않도록 차단</li>
<li>스케줄러 여러 대 중 한 대만 특정 maintenance job을 수행하도록 선출</li>
</ul>
<p>핵심은 &ldquo;같은 키에 대한 경쟁만 막고 싶다&quot;는 점입니다. 이때 Advisory Lock은 <a href="/learning/deep-dive/deep-dive-idempotency/">멱등성 설계</a>를 대체하지 않습니다. 락은 <strong>동시 실행을 줄이는 장치</strong>이고, 멱등성은 <strong>중복 효과가 나도 안전하게 만드는 장치</strong>입니다. 실무에서는 둘 중 하나만으로는 부족한 경우가 많습니다.</p>
<h3 id="2-advisory-lock이-잘-맞는-조건은-생각보다-좁고-그래서-오히려-강력하다">2) Advisory Lock이 잘 맞는 조건은 생각보다 좁고, 그래서 오히려 강력하다</h3>
<p>Advisory Lock이 잘 맞는 대표 조건은 아래 다섯 가지입니다.</p>
<ol>
<li>조율 기준의 진실 원본이 같은 DB 안에 있다.</li>
<li>락 유지 시간이 짧다. 보통 p95 기준 2초 이하, 길어도 10초 이하가 관리하기 쉽습니다.</li>
<li>락을 잡은 뒤 하는 일이 CPU/네트워크 장기 작업이 아니라 짧은 상태 전이나 enqueue 수준이다.</li>
<li>락 실패 시 즉시 재시도보다 &ldquo;나중에 다시&quot;가 허용된다.</li>
<li>연결 세션 경계를 팀이 정확히 이해하고 있다.</li>
</ol>
<p>예를 들어 주문 상태를 <code>PENDING -&gt; CONFIRMED</code>로 바꾸기 전에 주문 단위로 lock을 잠깐 잡는 건 괜찮습니다. 하지만 3분짜리 외부 API 호출 전체를 락 안에서 수행하는 건 거의 항상 나쁩니다. 그런 흐름은 <a href="/learning/deep-dive/deep-dive-timeout-retry-backoff/">Timeout/Retry/Backoff</a>와 큐 기반 비동기화로 풀어야지, DB 락으로 버티면 연결 풀만 고갈됩니다.</p>
<p>실무 감으로 정리하면 이렇습니다.</p>
<ul>
<li><strong>좋은 사용법</strong>: 짧은 임계 구역, DB 상태 전이, 중복 배치 시작 차단</li>
<li><strong>나쁜 사용법</strong>: 긴 외부 호출 보호, 다중 리전 조율, 사람 승인 대기 포함 작업, 30초 이상 장기 워크플로 제어</li>
</ul>
<h3 id="3-가장-흔한-사고는-락-알고리즘보다-세션-경계에서-난다">3) 가장 흔한 사고는 락 알고리즘보다 세션 경계에서 난다</h3>
<p>Advisory Lock을 처음 도입한 팀이 가장 자주 겪는 문제는 &ldquo;락이 안 풀렸다&quot;가 아니라 <strong>다른 커넥션에서 락을 잡고 다른 커넥션에서 풀려고 하거나, 풀에 반납된 세션에 락이 남는 문제</strong>입니다.</p>
<p>PostgreSQL 기준으로는 크게 두 종류가 있습니다.</p>
<ul>
<li>세션 단위 락: 커넥션이 살아 있는 동안 유지</li>
<li>트랜잭션 단위 락: 트랜잭션 종료 시 자동 해제</li>
</ul>
<p>짧은 임계 구역이면 보통 <code>pg_try_advisory_xact_lock</code> 같은 <strong>트랜잭션 범위 락을 우선</strong> 고려하는 편이 안전합니다. 이유는 분명합니다.</p>
<ul>
<li><code>commit/rollback</code>과 함께 해제되어 누수 확률이 낮음</li>
<li>커넥션 풀 환경에서 세션 잔존 락 문제를 줄임</li>
<li>락 해제 코드를 별도로 빼먹을 가능성이 낮음</li>
</ul>
<p>반대로 세션 락을 써야 하는 경우는 워커 선출처럼 &ldquo;트랜잭션 하나보다 긴 수명&quot;이 필요할 때인데, 이때는 다음을 꼭 지켜야 합니다.</p>
<ul>
<li>락 획득과 해제를 같은 커넥션 객체로 수행</li>
<li>풀 반환 전에 해제 보장</li>
<li>헬스체크/idle timeout이 세션을 끊을 때의 동작 확인</li>
<li>락 보유 중 재배포/프로세스 종료 시 graceful release 또는 failover 확인</li>
</ul>
<p>이 지점에서 사고가 잦다면, 락 자체보다 연결 풀 구조와 워커 생명주기를 먼저 다시 봐야 합니다. 특히 웹 요청 스레드에서 세션 락을 오래 잡는 구조는 거의 항상 문제를 만듭니다.</p>
<h3 id="4-키-설계가-나쁘면-락은-보호-장치가-아니라-병목이-된다">4) 키 설계가 나쁘면 락은 보호 장치가 아니라 병목이 된다</h3>
<p>Advisory Lock은 보통 정수 키나 해시 키로 잡습니다. 여기서 중요한 건 &ldquo;무엇을 직렬화할지&quot;입니다. 키 범위를 너무 넓게 잡으면 불필요한 직렬화가 생기고, 너무 좁게 잡으면 보호가 안 됩니다.</p>
<p>예를 들어 환불 처리에서 아래 둘은 결과가 크게 다릅니다.</p>
<ul>
<li><code>lock(refund)</code></li>
<li><code>lock(refund:user:12345)</code></li>
<li><code>lock(refund:order:98765)</code></li>
</ul>
<p>첫 번째는 모든 환불을 한 줄로 세웁니다. 두 번째는 같은 사용자의 환불만 직렬화합니다. 세 번째는 같은 주문에 대해서만 직렬화합니다. 실무에서 우선순위는 보통 <strong>가장 작은 단위로 직렬화하되, 실제 불일치가 나는 경계보다 더 작게 쪼개지 않는 것</strong>입니다.</p>
<p>권장 기준 예시는 이렇습니다.</p>
<ul>
<li>주문 상태 전이: <code>order_id</code></li>
<li>재고 보정: <code>sku_id</code> 또는 <code>warehouse_id + sku_id</code></li>
<li>일 배치 실행 보장: <code>job_name + business_date</code></li>
<li>정산 close 작업: <code>merchant_id + settlement_cycle</code></li>
</ul>
<p>하나의 키가 15분 이동창에서 50회 이상 경쟁하고, 그 키의 <code>lock_wait_p95</code>가 500ms를 넘기기 시작하면 단순 락보다는 <a href="/learning/deep-dive/deep-dive-queue-visibility-timeout-acknack-playbook/">큐의 visibility timeout/ack-nack 제어</a>나 파티션 기반 순차 처리로 옮기는 편이 낫습니다. 락은 경쟁을 숨기지 못합니다. 경쟁이 크면 병목도 그대로 드러납니다.</p>
<h3 id="5-advisory-lock은-리더-선출에도-쓰이지만-장기-리더십-관리에는-약하다">5) Advisory Lock은 리더 선출에도 쓰이지만, 장기 리더십 관리에는 약하다</h3>
<p>&ldquo;스케줄러 3대 중 1대만 잡을 돌리자&rdquo; 같은 요구에는 Advisory Lock이 꽤 잘 맞습니다. 예를 들어 1분마다 도는 배치에서 <code>job:cleanup-cache:2026-04-10T10:13</code> 같은 키를 잡고 성공한 인스턴스만 실행하게 할 수 있습니다.</p>
<p>하지만 이 구조는 짧은 job에 적합하지, 장시간 리더십을 유지해야 하는 구조에는 한계가 있습니다.</p>
<ul>
<li>프로세스가 멈추지 않았는데 네트워크가 흔들리면 세션 상태 추론이 어려울 수 있음</li>
<li>리더가 오래 잡고 있는 동안 관측이 약하면 &ldquo;죽은 리더처럼 보이는 살아 있는 세션&rdquo; 문제가 생김</li>
<li>다중 리전/다중 DB 환경으로 가면 진실 원본이 하나가 아님</li>
</ul>
<p>이 경우는 leader election을 억지로 DB 락으로 키우기보다, job enqueue만 DB 락으로 보호하고 실제 실행은 별도 워커 체계로 넘기거나, 애초에 workflow engine이나 coordinator로 가는 편이 낫습니다. <a href="/learning/deep-dive/deep-dive-transactional-outbox-cdc/">Transactional Outbox + CDC</a>를 써서 &ldquo;중복 시작만 막고 실행은 비동기 분산&quot;으로 풀면 구조가 훨씬 안정적인 경우가 많습니다.</p>
<h2 id="실무-적용">실무 적용</h2>
<h3 id="1-가장-안전한-시작점은-트랜잭션-범위-락--짧은-임계-구역이다">1) 가장 안전한 시작점은 트랜잭션 범위 락 + 짧은 임계 구역이다</h3>
<p>PostgreSQL 예시는 아래처럼 잡을 수 있습니다.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-sql" data-lang="sql"><span style="display:flex;"><span><span style="color:#ff79c6">BEGIN</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#ff79c6">SELECT</span> pg_try_advisory_xact_lock(hashtext(<span style="color:#f1fa8c">&#39;refund:order:98765&#39;</span>)) <span style="color:#ff79c6">AS</span> locked;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#6272a4">-- locked = false 면 즉시 종료 또는 재큐잉
</span></span></span><span style="display:flex;"><span><span style="color:#6272a4">-- locked = true 면 짧은 상태 전이 수행
</span></span></span><span style="display:flex;"><span><span style="color:#6272a4"></span><span style="color:#ff79c6">UPDATE</span> refund_request
</span></span><span style="display:flex;"><span><span style="color:#ff79c6">SET</span> status <span style="color:#ff79c6">=</span> <span style="color:#f1fa8c">&#39;PROCESSING&#39;</span>, updated_at <span style="color:#ff79c6">=</span> now()
</span></span><span style="display:flex;"><span><span style="color:#ff79c6">WHERE</span> order_id <span style="color:#ff79c6">=</span> <span style="color:#bd93f9">98765</span>
</span></span><span style="display:flex;"><span>  <span style="color:#ff79c6">AND</span> status <span style="color:#ff79c6">=</span> <span style="color:#f1fa8c">&#39;PENDING&#39;</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#ff79c6">COMMIT</span>;
</span></span></code></pre></div><p>이 패턴의 장점은 세 가지입니다.</p>
<ol>
<li>락 수명이 트랜잭션과 묶여 누수 위험이 낮습니다.</li>
<li>같은 DB 트랜잭션 안에서 상태 확인과 갱신을 함께 처리할 수 있습니다.</li>
<li>실패 시 <code>rollback</code>으로 정리돼 운영 복잡도가 낮습니다.</li>
</ol>
<p>권장 순서는 보통 아래입니다.</p>
<ol>
<li>락 시도</li>
<li>락 획득 성공 시 대상 상태 재검증</li>
<li>짧은 상태 전이 또는 작업 enqueue</li>
<li>즉시 commit</li>
<li>긴 작업은 트랜잭션 밖 워커에서 수행</li>
</ol>
<p>즉 <strong>락 안에서는 긴 일을 하지 말고, 긴 일을 시작할 자격만 결정</strong>하는 편이 좋습니다.</p>
<h3 id="2-의사결정-기준숫자조건우선순위">2) 의사결정 기준(숫자·조건·우선순위)</h3>
<p>Advisory Lock 도입 판단 기준을 숫자로 잡으면 흔들림이 줄어듭니다.</p>
<p>도입을 우선 검토할 조건:</p>
<ul>
<li>같은 aggregate에 대한 동시 처리 충돌이 주 1회 이상 보인다.</li>
<li>중복 실행으로 인한 복구 시간이 건당 10분 이상 든다.</li>
<li>조율 대상의 진실 원본이 단일 DB에 있다.</li>
<li>보호할 임계 구역의 p95 실행 시간이 2초 이하이다.</li>
</ul>
<p>반대로 다른 패턴을 먼저 봐야 할 조건:</p>
<ul>
<li>락을 잡은 뒤 외부 API 호출이 p95 3초 이상이다.</li>
<li>하나의 키 경쟁이 초당 20회 이상 발생한다.</li>
<li>다중 리전 또는 다중 writer DB를 사용한다.</li>
<li>락 없이도 <a href="/learning/deep-dive/deep-dive-admission-control-concurrency-limits/">admission control/concurrency limit</a>이나 큐 파티셔닝으로 더 자연스럽게 풀 수 있다.</li>
</ul>
<p>권장 운영 기준 예시:</p>
<ul>
<li><code>lock_acquire_success_rate</code>: 95% 이상</li>
<li><code>lock_wait_p95</code>: 100ms 이하, 경고 300ms, 위험 500ms</li>
<li><code>lock_hold_p95</code>: 2초 이하, 최대 10초 미만 권장</li>
<li>락 실패 후 즉시 재시도 횟수: 0~1회</li>
<li>동일 키 재시도 총 횟수: 3회 이내</li>
<li>세션 락 누수 추정 건수: 0건 목표</li>
</ul>
<p>우선순위는 <strong>데이터 무결성 &gt; 중복 실행 차단 &gt; 연결 풀 안정성 &gt; 처리량</strong> 순으로 두는 편이 안전합니다. 처리량 때문에 락 범위를 넓히거나 장기 락을 허용하면, 결국 풀 고갈과 지연 확산으로 더 큰 비용을 냅니다.</p>
<h3 id="3-실패-시-동작을-미리-정해야-락이-운영-도구가-된다">3) 실패 시 동작을 미리 정해야 락이 운영 도구가 된다</h3>
<p>락 획득 실패를 단순 오류로 보면 운영이 시끄러워집니다. 많은 경우 락 실패는 장애가 아니라 <strong>이미 누군가 처리 중이라는 정상 신호</strong>입니다. 그래서 상태 코드를 아래처럼 나누는 편이 좋습니다.</p>
<ul>
<li><code>LOCK_ACQUIRED</code>: 바로 처리</li>
<li><code>LOCK_BUSY_EXPECTED</code>: 경쟁 중, 재큐잉 또는 skip</li>
<li><code>LOCK_BUSY_SUSPICIOUS</code>: 장시간 점유, 경보 후보</li>
<li><code>LOCK_RELEASE_FAILED</code>: 구현 결함 가능성, 조사 필요</li>
<li><code>LOCK_CONTEXT_MISMATCH</code>: 다른 커넥션/트랜잭션 경계 오류</li>
</ul>
<p>예를 들어 정기 배치라면 <code>LOCK_BUSY_EXPECTED</code>는 실패로 세지 말고 &ldquo;이번 실행 skip&quot;으로 처리하는 편이 맞습니다. 반대로 사용자 요청 플로라면 같은 키가 3회 연속 <code>LOCK_BUSY</code>이면 409/202 응답과 함께 비동기 재처리로 넘기는 편이 낫습니다.</p>
<h3 id="4-락만으로-끝내지-말고-멱등성과-상태-전이를-같이-설계해야-한다">4) 락만으로 끝내지 말고 멱등성과 상태 전이를 같이 설계해야 한다</h3>
<p>실무에서는 락이 있어도 아래 상황이 생깁니다.</p>
<ul>
<li>락 획득 후 작업 직전 프로세스 종료</li>
<li>작업 완료 후 응답 반환 전 네트워크 단절</li>
<li>락 없이 실행된 구버전 워커와 신버전 워커가 동시에 존재</li>
</ul>
<p>그래서 Advisory Lock은 단독 방어선이 아니라 다음과 같이 묶어야 합니다.</p>
<ul>
<li>락으로 동시 진입 축소</li>
<li>상태 머신으로 허용 전이만 통과</li>
<li>멱등 키로 중복 효과 차단</li>
<li>재시도는 큐나 스케줄러에서 관리</li>
</ul>
<p>즉 락은 <strong>입구 제어</strong>, 상태 전이는 <strong>정합성 규칙</strong>, 멱등성은 <strong>사후 안전장치</strong>입니다. 세 개를 같이 설계해야 운영이 편해집니다.</p>
<h3 id="5-3단계-전환-전략-advisory-lock에서-더-큰-구조로-넘어가는-시점">5) 3단계 전환 전략: Advisory Lock에서 더 큰 구조로 넘어가는 시점</h3>
<p>처음부터 복잡한 coordinator를 넣기보다 아래 순서가 대체로 현실적입니다.</p>
<p><strong>1단계, Advisory Lock</strong><br>
짧은 임계 구역과 단일 DB 기준 중복 진입 차단</p>
<p><strong>2단계, Queue/Partition 직렬화</strong><br>
특정 키 경쟁이 높아지면 같은 키를 같은 파티션으로 보내 소비기 수준에서 순차 처리</p>
<p><strong>3단계, Workflow/Orchestration</strong><br>
사람 승인, 보상 트랜잭션, 장기 대기, 다중 시스템 조율이 붙으면 workflow engine 검토</p>
<p>아래 조건 중 2개 이상이면 2단계 이상을 검토할 시점입니다.</p>
<ul>
<li><code>lock_wait_p95</code>가 500ms 이상으로 1주 지속</li>
<li>하나의 작업이 외부 호출 포함 p95 10초 이상</li>
<li>같은 키 충돌이 하루 1,000건 이상</li>
<li>장애 복구 시 운영자가 수동으로 락 상태를 자주 확인해야 함</li>
<li>조율 대상이 DB 밖 시스템 2개 이상으로 퍼짐</li>
</ul>
<h2 id="트레이드오프주의점">트레이드오프/주의점</h2>
<ol>
<li>
<p><strong>구현은 가볍지만, 경계를 잘못 이해하면 위험하다</strong><br>
락 함수 한 줄은 쉽지만, 커넥션 풀과 세션 수명까지 모르면 오히려 더 디버깅이 어려워집니다.</p>
</li>
<li>
<p><strong>짧은 보호에는 강하지만 긴 워크플로에는 약하다</strong><br>
Advisory Lock은 빠른 임계 구역 보호에는 좋지만, 장시간 비즈니스 프로세스를 대표하지는 못합니다.</p>
</li>
<li>
<p><strong>경쟁을 해결하지 않고 노출한다</strong><br>
같은 키 경쟁이 크면 병목이 그냥 드러납니다. 이건 장점이기도 하지만, 처리량 병목을 락 하나로 숨길 수는 없습니다.</p>
</li>
<li>
<p><strong>DB 의존성이 강하다</strong><br>
조율 기준이 DB 밖으로 퍼지면 락의 설명력이 약해집니다. 여러 저장소를 동시에 조율해야 하면 다른 계층을 고려해야 합니다.</p>
</li>
<li>
<p><strong>멱등성 없는 락은 과신하기 쉽다</strong><br>
락을 잡았으니 안전하다고 느끼기 쉽지만, 프로세스 종료나 네트워크 실패는 여전히 남습니다. 락은 중복을 줄여 줄 뿐, 완전히 없애 주지 않습니다.</p>
</li>
</ol>
<h2 id="체크리스트-또는-연습">체크리스트 또는 연습</h2>
<h3 id="체크리스트">체크리스트</h3>
<ul>
<li><input disabled="" type="checkbox"> 락 키가 실제 충돌 경계를 반영한다.</li>
<li><input disabled="" type="checkbox"> 세션 락보다 트랜잭션 락을 먼저 검토했다.</li>
<li><input disabled="" type="checkbox"> 락 안에서는 외부 API 호출이나 긴 네트워크 작업을 하지 않는다.</li>
<li><input disabled="" type="checkbox"> <code>lock_wait_p95</code>, <code>lock_hold_p95</code>, <code>lock_busy_rate</code>를 대시보드로 본다.</li>
<li><input disabled="" type="checkbox"> 락 실패를 오류와 정상 skip로 구분한다.</li>
<li><input disabled="" type="checkbox"> 상태 전이 검증과 멱등 키가 락과 함께 설계돼 있다.</li>
<li><input disabled="" type="checkbox"> 특정 키 경쟁이 높을 때 큐/파티션으로 넘길 기준이 문서화돼 있다.</li>
</ul>
<h3 id="연습-과제">연습 과제</h3>
<ol>
<li>현재 시스템에서 &ldquo;동시에 두 번 돌면 곤란한 작업&rdquo; 3개를 적고, 락 키를 각각 어떤 단위로 잡을지 설계해 보세요.</li>
<li>같은 작업에 대해 <code>세션 락</code>과 <code>트랜잭션 락</code> 중 무엇이 맞는지, 연결 풀 동작까지 포함해 설명해 보세요.</li>
<li>최근 1주 장애나 재처리 사례를 기준으로, Advisory Lock만으로 충분한지 아니면 큐/워크플로로 가야 하는지 판단표를 만들어 보세요.</li>
</ol>
<h2 id="관련-글">관련 글</h2>
<ul>
<li><a href="/learning/deep-dive/deep-dive-idempotency/">멱등성: 안전한 재시도를 위한 API 설계</a></li>
<li><a href="/learning/deep-dive/deep-dive-timeout-retry-backoff/">Timeout/Retry/Backoff 설계: 장애 전파를 막는 3종 세트</a></li>
<li><a href="/learning/deep-dive/deep-dive-transactional-outbox-cdc/">트랜잭션 아웃박스 + CDC: 이중 쓰기 없이 이벤트 일관성 확보하기</a></li>
<li><a href="/learning/deep-dive/deep-dive-queue-visibility-timeout-acknack-playbook/">Queue Visibility Timeout/Ack/Nack 플레이북</a></li>
<li><a href="/learning/deep-dive/deep-dive-admission-control-concurrency-limits/">Admission Control &amp; Concurrency Limit</a></li>
</ul>
]]></content:encoded></item></channel></rss>