feat: add optimistic locking and improve type safety
- Implement optimistic locking for deposit balance updates - Prevent race conditions in concurrent deposit requests - Add automatic retry with exponential backoff (max 3 attempts) - Add version column to user_deposits table - Improve type safety across codebase - Add explicit types for Namecheap API responses - Add typed function arguments (ManageDepositArgs, etc.) - Remove `any` types from deposit-agent and tool files - Add reconciliation job for balance integrity verification - Compare user_deposits.balance vs SUM(confirmed transactions) - Alert admin on discrepancy detection - Set up test environment with Vitest + Miniflare - Add 50+ test cases for deposit system - Add helper functions for test data creation - Update documentation - Add migration guide for version columns - Document optimistic locking patterns Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
137
CLAUDE.md
137
CLAUDE.md
@@ -176,6 +176,9 @@ npm run db:init # D1 스키마 초기화 (production) ⚠️ 주의
|
||||
npm run db:init:local # D1 스키마 초기화 (local)
|
||||
npm run tail # Workers 로그 스트리밍
|
||||
npm run chat # CLI 테스트 클라이언트
|
||||
npm test # 단위 테스트 실행 (Vitest)
|
||||
npm run test:watch # Watch 모드
|
||||
npm run test:coverage # 커버리지 리포트
|
||||
```
|
||||
|
||||
**CLI 테스트 클라이언트:**
|
||||
@@ -301,9 +304,60 @@ end(); // duration 자동 기록
|
||||
|
||||
## Testing
|
||||
|
||||
**현재 테스트 스크립트 없음** - 수동 테스트 필수
|
||||
### 자동화된 단위 테스트 (Vitest)
|
||||
|
||||
### 로컬 테스트 절차
|
||||
**프레임워크**: Vitest + Miniflare (Cloudflare Workers 환경 시뮬레이션)
|
||||
|
||||
**실행 명령어**:
|
||||
```bash
|
||||
npm test # 모든 테스트 실행
|
||||
npm run test:watch # Watch 모드 (개발 중)
|
||||
npm run test:coverage # 커버리지 리포트
|
||||
```
|
||||
|
||||
**테스트 파일 구조**:
|
||||
```
|
||||
tests/
|
||||
├── setup.ts # D1 Database 초기화 및 헬퍼 함수
|
||||
└── deposit-agent.test.ts # 예치금 시스템 테스트 (50+ test cases)
|
||||
|
||||
vitest.config.ts # Vitest 설정 (Miniflare 바인딩)
|
||||
```
|
||||
|
||||
**테스트 범위**:
|
||||
| 기능 | 테스트 케이스 | 상태 |
|
||||
|------|--------------|------|
|
||||
| **음수 금액 거부** | 음수/0원 입금 시도 | ✅ |
|
||||
| **동시성 처리** | 동일 사용자 동시 입금, Race condition | ✅ |
|
||||
| **Batch 실패 처리** | db.batch() 부분 실패 시뮬레이션 | ✅ |
|
||||
| **7글자 매칭** | "홍길동아버지님" → "홍길동아버지" 자동 매칭 | ✅ |
|
||||
| **관리자 권한** | 비관리자 confirm/reject/pending 차단 | ✅ |
|
||||
| **거래 상태** | confirmed 거래 취소 차단 | ✅ |
|
||||
| **Edge Cases** | 999,999,999원, 특수문자, 1글자 이름 | ✅ |
|
||||
|
||||
**Mock 전략**:
|
||||
- **D1 Database**: Miniflare in-memory SQLite
|
||||
- **Environment Variables**: `vitest.config.ts`에서 바인딩
|
||||
- **KV Namespace**: Rate Limiting 모킹
|
||||
|
||||
**헬퍼 함수** (`tests/setup.ts`):
|
||||
```typescript
|
||||
createTestUser(telegramId, username) // 테스트용 사용자 생성
|
||||
createBankNotification(depositorName, amount) // 은행 알림 생성
|
||||
createDepositTransaction(userId, amount, status) // 거래 생성
|
||||
getTestDB() // DB 바인딩 가져오기
|
||||
```
|
||||
|
||||
**추가 예정**:
|
||||
- `openai-service.ts` - Function Calling 도구 테스트
|
||||
- `summary-service.ts` - 프로필 시스템 테스트
|
||||
- Integration Tests - 전체 워크플로우 테스트
|
||||
|
||||
---
|
||||
|
||||
### 수동 테스트 (Webhook)
|
||||
|
||||
**로컬 테스트 절차**:
|
||||
```bash
|
||||
# 1. 로컬 D1 초기화 (최초 1회)
|
||||
npm run db:init:local
|
||||
@@ -318,7 +372,7 @@ curl -X POST http://localhost:8787/webhook \
|
||||
-d '{"message":{"chat":{"id":123},"text":"테스트"}}'
|
||||
```
|
||||
|
||||
### 배포 후 확인
|
||||
**배포 후 확인**:
|
||||
```bash
|
||||
# 로그 스트리밍
|
||||
npm run tail
|
||||
@@ -327,6 +381,10 @@ npm run tail
|
||||
curl https://telegram-summary-bot.kappa-d8e.workers.dev/webhook-info
|
||||
```
|
||||
|
||||
**수동 테스트 예제** (자동화 예정):
|
||||
- `src/services/__test__/notification.test.ts` - 관리자 알림
|
||||
- `src/utils/__test__/logger.test.ts` - 구조화된 로깅
|
||||
|
||||
---
|
||||
|
||||
## Troubleshooting
|
||||
@@ -999,6 +1057,79 @@ crons = ["0 15 * * *"] # KST 00:00
|
||||
|
||||
**description 필드:** 거래 사유 (예: "도메인 등록: example.com")
|
||||
|
||||
### Transaction Isolation & Optimistic Locking
|
||||
|
||||
**문제:** D1 `batch()`는 진정한 트랜잭션이 아니므로 부분 실패 시 데이터 불일치 가능
|
||||
|
||||
**해결책:** Optimistic Locking 패턴 + 정합성 검증 Job
|
||||
|
||||
**구현:**
|
||||
```
|
||||
user_deposits 테이블에 version 컬럼 추가
|
||||
↓
|
||||
잔액 변경 시마다 version 자동 증가
|
||||
↓
|
||||
UPDATE 쿼리에서 WHERE version = ? 조건 검증
|
||||
↓
|
||||
version 불일치 시 OptimisticLockError 발생
|
||||
↓
|
||||
지수 백오프로 자동 재시도 (최대 3회)
|
||||
↓
|
||||
재시도 실패 시 사용자 친화적 에러 메시지
|
||||
```
|
||||
|
||||
**관련 파일:**
|
||||
| 파일 | 역할 |
|
||||
|------|------|
|
||||
| `utils/optimistic-lock.ts` | Optimistic Locking 유틸리티 (재시도 로직) |
|
||||
| `utils/reconciliation.ts` | 잔액 정합성 검증 (Cron 실행) |
|
||||
| `deposit-agent.ts` | 입금 처리에 Optimistic Locking 적용 |
|
||||
| `migrations/002_add_version_columns.sql` | 스키마 마이그레이션 |
|
||||
|
||||
**적용 대상:**
|
||||
- `request_deposit` (auto_matched case): 은행 알림 자동 매칭 시 잔액 증가
|
||||
- `confirm_deposit`: 관리자 수동 확인 시 잔액 증가
|
||||
|
||||
**정합성 검증 (Reconciliation):**
|
||||
```
|
||||
매일 KST 00:00 Cron 실행
|
||||
↓
|
||||
user_deposits.balance vs SUM(confirmed transactions) 비교
|
||||
↓
|
||||
불일치 발견 시:
|
||||
1. 로그에 상세 기록
|
||||
2. 관리자에게 Telegram 알림
|
||||
3. 검증 리포트 생성
|
||||
```
|
||||
|
||||
**마이그레이션:**
|
||||
```bash
|
||||
# 로컬 테스트
|
||||
wrangler d1 execute telegram-conversations --local --file=migrations/002_add_version_columns.sql
|
||||
|
||||
# 프로덕션 적용 (데이터 백업 권장)
|
||||
wrangler d1 execute telegram-conversations --file=migrations/002_add_version_columns.sql
|
||||
|
||||
# 검증
|
||||
wrangler d1 execute telegram-conversations --command "SELECT user_id, balance, version FROM user_deposits LIMIT 5"
|
||||
```
|
||||
|
||||
**동시성 시나리오 예시:**
|
||||
```
|
||||
사용자 A: 잔액 10,000원 (version=1)
|
||||
↓
|
||||
동시 요청:
|
||||
입금 +5,000원 (요청1)
|
||||
입금 +3,000원 (요청2)
|
||||
↓
|
||||
요청1: version=1 읽음 → UPDATE (version=2) ✅ 성공
|
||||
요청2: version=1 읽음 → UPDATE (version=1) ❌ 실패 (version 불일치)
|
||||
↓
|
||||
요청2 재시도: version=2 읽음 → UPDATE (version=3) ✅ 성공
|
||||
↓
|
||||
최종: 잔액 18,000원 (version=3) ✅ 정합성 보장
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Domain System
|
||||
|
||||
Reference in New Issue
Block a user