> For the complete documentation index, see [llms.txt](https://wonjoon.gitbook.io/joons-til/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://wonjoon.gitbook.io/joons-til/techtalk-review/woowacon/2024/using-distributed-locks-for-inventory-transfers-in-wms.md).

# Using Distributed Locks for Inventory Transfers in WMS

<https://www.youtube.com/watch?v=3HCVD26zycM\\&list=PLgXGHBqgT2Tu7H-ita\\_W0IHospr64ON\\_a\\&index=23>

### 🏷️ 개요

WMS(Warehouse Management System)는 물류센터 내 재고의 입고-보관-출고 전 과정을 관리하는 시스템입니다. 본 포스트에서는 우아한형제들이 WMS 시스템을 운영하면서 겪었던 동시성 문제를 해결해 나간 과정을 다룹니다. 특히 재고 이관 과정에서 발생한 **할당 vs 취소 요청의 충돌**을 어떻게 분산락과 상태 키를 활용해 해결했는지 그 과정을 상세히 풀어보려 합니다.

***

### 🏢 WMS와 물류센터 구조 이해

#### 🏬 WMS란?

WMS는 Warehouse Management System의 약자로, 창고 내 재고의 입고, 저장, 출고를 관리하는 시스템입니다. 우아한형제들의 **B마트** 상품은 이 시스템을 통해 운영됩니다.

#### 📦 물류 흐름 요약

1. **중앙물류기지**: 공급사로부터 상품이 입고됨
2. **피패킹 센터(Picking + Packing)**: 지역 거점 물류센터, 실제 고객 주문이 처리되는 현장
3. **재고 이관**: 중앙물류기지 → 피패킹 센터로 재고가 이동

***

### 🔄 재고 이관 프로세스

#### 📝 이관 요청서 생성

재고 이관의 시작은 **이관 요청서**를 생성하는 것입니다. 하나의 요청서에는 수십\~수백 개의 상품이 포함될 수 있습니다.

#### 🔍 재고 선점(할당)

* 이관을 위해선 특정 위치에 있는 재고를 미리 선점해야 함
* 이는 **재고 할당 작업**이라 불리며, 위치별로 흩어진 동일 상품 중에서 적절한 위치의 재고를 자동 선택하여 할당합니다.

***

### ⚠️ 장애 발생: 할당 vs 취소 충돌

#### 💥 문제 상황

하나의 이관 요청서에 대해 두 명의 관리자가 동시에:

* A: 할당 요청
* B: 취소 요청

결과:

* 재고는 할당됨
* 상태는 '취소'로 덮어쓰기됨 ❌

#### 🧪 원인 분석

* 할당: **분산락으로 보호됨** (이관요청서 ID 기준)
* 취소: **락 없이 상태 변경**

즉, **락이 없던 취소 로직이 할당 상태를 덮어쓴 것**이 문제였습니다.

***

### 🔐 1단계 개선: 분산락 추가

![Image](https://github.com/user-attachments/assets/4dae2212-054e-42b4-a071-cf3a5dd04f78)

#### 🛠️ 해결 방안 1: 취소에도 분산락 적용

* 할당과 취소 모두 동일한 분산락 키 사용 → 동시성 제어 성공

#### 🧱 부작용

* 하나의 요청서에 여러 상품이 할당될 경우, **락이 중복되어 병렬 할당 불가**
* 예: 사과는 할당되었으나, 케이크는 락 획득 실패 → 요청서 상태가 '부분 할당'으로 종료

***

### 🕐 2단계 개선: 락 대기

![Image](https://github.com/user-attachments/assets/7f9d6d94-e0bf-4309-a77b-035cdf1c7da2)

#### ⏳ 해결 방안 2: 락 대기 허용

* 여러 상품 할당 시 **락 획득을 대기**
* 병렬은 불가능하지만, 결국 모두 할당됨

#### 🐢 단점

![Image](https://github.com/user-attachments/assets/de3044cf-a5a0-43cf-a46a-e1d2150705c3)

* **상품 수가 많아질수록 전체 할당 시간 증가**
* 100개 상품이면 100번 순차적으로 락 대기 → 성능 저하

***

### 🚀 3단계 개선: 상태 키 활용 + 병렬 처리

![Image](https://github.com/user-attachments/assets/4fa84584-d7a4-4796-9e15-f7dd1e117175)

#### 💡 핵심 아이디어

* 실제 재고 할당은 락 밖에서 처리
* 대신 요청 유형(`할당`, `취소`)만 **상태 키로 관리**

#### ⚙️ 구현 방식

1. **요청 시작 시**: 락 획득하여 상태 키 확인 및 설정
2. **현재 상태와 동일한 유형이면**: 유효시간 갱신하고 병렬 처리 허용
3. **다르면**: 예외 발생 → 이후 로직 중단

#### ⛓️ 병렬 예시 흐름

* 사과 할당 요청 → 락 획득 → 상태를 '할당 중'으로 설정 → 락 해제 → 재고 할당 시작
* 케이크 할당 요청 → 락 획득 → 동일 상태 확인 → 병렬 처리 시작
* 두 재고 할당 완료 후 상태 변경 (부분 할당 → 할당)

***

### 🔄 상태 키 로직 상세 분석

#### 🧾 상태 키 조회 로직

```java
lockService.withLock(requestId, () -> {
    String currentState = stateKey.get(requestId);
    if (currentState == null || currentState.equals(requestType)) {
        stateKey.set(requestId, requestType, TTL);
    } else {
        throw new ConflictException();
    }
});

```

#### 🤝 병렬 처리 가능 조건

* 동일 요청 유형끼리는 병렬 허용
* 상이한 요청 유형(할당 vs 취소)은 병렬 불가 → 예외 발생

***

### 📈 성능과 안정성 향상

#### ✅ 동시성 문제 해결

* 상이한 유형 간의 충돌 제거 (락으로 보호)
* 동일 유형 병렬 처리 허용 (성능 개선)

#### 📉 처리 시간 감소

* 분산락으로 전체 처리 병목 제거
* 다수 상품 병렬 할당 가능

***

### 📚 기술 키워드 설명

#### 🔐 분산락 (Distributed Lock)

* 분산 시스템에서 동일한 자원에 대한 접근을 순차적으로 보장하기 위한 기술
* Redis 기반으로 주로 구현 (ex. Redisson)

#### 📌 상태 키(State Key)

* 특정 리소스의 상태를 관리하기 위한 키값
* `"재고이관요청서:ID" -> 할당중/취소중` 형태로 저장

#### ⚙️ 병렬 처리

* 작업을 동시에 수행하여 처리 시간을 단축시키는 방식
* 단, 상태 충돌 방지를 위해 사전 제어 필요

***

### 🧠 배운 점 & 인사이트

#### ✅ 트랜잭션보다 락의 타이밍이 중요하다

* DB 커밋 완료 전/후 락 설정 타이밍에 따라 결과가 완전히 달라짐

#### ✅ 락은 범위와 목적에 따라 다르게 써야 한다

* 단순히 '락을 걸면 된다'가 아니라, **어디에, 언제, 무엇을 위해 락을 거는가**가 중요

#### ✅ 일관성과 성능은 트레이드오프다

* 1단계는 일관성에 집중 → 성능 저하
* 3단계는 병렬성 확보하면서도 일관성 유지 → 최적

#### ✅ 상태 기반 동시성 제어 패턴은 확장성이 뛰어나다

* 단일 락 키 → 병렬 제약
* 상태 기반 접근 → **더 넓은 병렬성과 안전성 확보**

***

## 느낀점

이번 개선 과정을 통해 분산 시스템에서의 동시성 제어는 단순한 트랜잭션 처리만으로는 부족하다는 것을 배웠고,\
특히 요청 단위의 상태 일관성을 유지하기 위해 분산락과 상태 키를 조합해 사용하는 전략을 사용할 수 있다는 것을 배웠습니다.\
동시에 병렬성과 정합성 사이의 트레이드오프를 조율하는 것이 시스템 설계의 핵심인것 같습니다.\
도메인 지식이 부족해서 이해가 어려웠던 점이 아쉬웠습니다.
