# CollectionPoller DEFERRED 재폴링 지침서

> **목적**: CollectionPoller가 DEFERRED 상태 항목도 재폴링하여, approve 완료 후 집금이 자동 진행되도록 수정
> **날짜**: 2026-03-22
> **대상 모듈**: node-service (relayer-api, common)
> **발견 계기**: Phase 3 E2E — approve APPROVED 완료 후에도 collection_queue가 DEFERRED에서 멈춤

---

## 1. 문제

```
[현재 흐름]
1. CollectionPoller → QUEUED 항목 조회
2. approve 없음 → PENDING INSERT + collection → DEFERRED
3. wallet-activator → approve TX → APPROVED ✅
4. CollectionPoller → QUEUED만 조회 → DEFERRED 안 잡힘 ❌
   → 집금 영원히 멈춤
```

**원인**: `CollectionPoller`가 `findByStatus('QUEUED')` 만 호출. DEFERRED 항목은 조회 대상에서 제외.

**approve 미완료 시 동작**: `executeCollection()`에서 approve 상태를 확인하고 다시 DEFERRED로 설정 후 return — 3초 후 다시 시도. approve가 완료될 때까지 DEFERRED ↔ 재폴링 반복이며, 오버헤드는 DB SELECT 1회/3초로 미미.

---

## 2. 수정 사항

### 2-1. CollectionQueueRepo — findByStatuses() 추가

**파일**: `node-service/packages/common/src/db/repositories/CollectionQueueRepo.ts`

**추가 메서드**:

```typescript
async findByStatuses(
  statuses: CollectionStatus[],
  options: { limit?: number; orderBy?: string } = {},
): Promise<CollectionQueue[]> {
  const limit = options.limit ?? 20;
  const orderBy = options.orderBy ?? 'created_at ASC';
  const placeholders = statuses.map(() => '?').join(', ');
  return query<CollectionQueueRow[]>(
    `SELECT * FROM collection_queue
     WHERE status IN (${placeholders})
     ORDER BY ${orderBy}
     LIMIT ?`,
    [...statuses, limit],
  );
},
```

### 2-2. CollectionPoller — DEFERRED 포함 폴링

**파일**: `node-service/packages/relayer-api/src/services/CollectionPoller.ts`

**Before** (line ~338):
```typescript
const queued = await collectionQueueRepo.findByStatus('QUEUED', {
  limit: this.batchSize,
});

if (queued.length > 0) {
  logger.info(`Processing ${queued.length} QUEUED collections`);
```

**After**:
```typescript
const queued = await collectionQueueRepo.findByStatuses(['QUEUED', 'DEFERRED'], {
  limit: this.batchSize,
});

if (queued.length > 0) {
  logger.info(`Processing ${queued.length} QUEUED/DEFERRED collections`);
```

---

## 3. 동작 흐름 (수정 후)

```
[수정 후 흐름]
1. CollectionPoller → QUEUED + DEFERRED 항목 조회
2. 첫 번째 폴링: approve 없음 → PENDING INSERT + DEFERRED → return
3. wallet-activator → GAS_SUPPORTING → GAS_READY → APPROVING → APPROVED ✅
4. CollectionPoller 다음 주기: DEFERRED 항목 조회 ✅
   → approve 확인 (APPROVED) → Relayer 선택 → 집금 TX 실행
   → COLLECTING → BROADCASTING → CONFIRMED
```

**approve 미완료 시**: DEFERRED → executeCollection() → approve 아직 PENDING/GAS_SUPPORTING → 다시 DEFERRED → 3초 후 재시도 (무해)

---

## 4. 적용 체크리스트

| # | 작업 | 파일 |
|---|------|------|
| 1 | CollectionQueueRepo에 `findByStatuses()` 추가 (섹션 2-1) | `CollectionQueueRepo.ts` |
| 2 | CollectionPoller 폴링 쿼리 변경 (섹션 2-2) | `CollectionPoller.ts` |
| 3 | relayer-api 재기동 | - |

---

## 5. 검증

```sql
-- 수정 전: DEFERRED에서 멈춤
SELECT id, status FROM collection_queue;
-- id=1, DEFERRED
-- id=2, DEFERRED

-- 수정 후 재기동: approve APPROVED인 항목은 집금 진행
-- (약 3~10초 대기)
SELECT id, status, tx_hash FROM collection_queue;
-- 기대: COLLECTING → BROADCASTING → CONFIRMED
```
