virtual-insanity
← 뒤로

800 운영/810 런북/서킷브레이커-해제.md

seedling

서킷브레이커 해제

증상

  • 특정 파이프라인 작업이 circuit breaker OPEN 에러로 실행 거부됨
  • orchestrator_state.json 의 circuit_breakers 에 OPEN 상태 항목 존재
  • 장애 원인이 해소되었지만 서킷브레이커가 자동 복구(half-open)되지 않음
  • 텔레그램에 "서킷브레이커 활성화" 알림 수신

원인

  1. 연속 실패 임계치 초과 — 특정 파이프라인이 N회 연속 실패하여 자동 차단
  2. 외부 의존 일시 장애 — DB, API, 네트워크 문제가 해소된 후에도 OPEN 상태 잔존
  3. half-open 전환 실패 — 타임아웃 설정이 너무 길어 자동 복구 지연
  4. 수동 차단 — 운영자가 의도적으로 OPEN 설정 후 해제를 잊음

해결 단계

1. 현재 서킷브레이커 상태 확인

python3 -c "
import json, os

state_path = os.path.expanduser('~/.openclaw/workspace/memory/orchestrator_state.json')

with open(state_path) as f:
    state = json.load(f)

cbs = state.get('circuit_breakers', {})
if not cbs:
    print('No circuit breakers configured')
else:
    for name, cb in cbs.items():
        status = cb.get('state', 'UNKNOWN')
        failures = cb.get('failure_count', 0)
        last_failure = cb.get('last_failure_at', 'N/A')
        print(f'  {name}: state={status}, failures={failures}, last_failure={last_failure}')
"

2. 근본 원인 해소 확인

서킷브레이커를 해제하기 전에 원인이 해소되었는지 확인하세요.

# DB 접근 확인
sqlite3 ~/.openclaw/data/ops_multiagent.db "SELECT 1;" && echo "DB OK" || echo "DB FAILED"

# 네트워크 확인
curl -s --max-time 5 https://openrouter.ai/api/v1/models > /dev/null && echo "Network OK" || echo "Network FAILED"

# 헬스체크
python3 ~/.openclaw/workspace/scripts/health_check.py

3. 특정 서킷브레이커 수동 해제

# 예: 'blog_monitor' 서킷브레이커 해제
python3 -c "
import json, os, tempfile

state_path = os.path.expanduser('~/.openclaw/workspace/memory/orchestrator_state.json')

with open(state_path) as f:
    state = json.load(f)

target = 'blog_monitor'  # 해제할 서킷브레이커 이름
cbs = state.get('circuit_breakers', {})

if target in cbs:
    cbs[target]['state'] = 'CLOSED'
    cbs[target]['failure_count'] = 0
    cbs[target].pop('last_failure_at', None)
    print(f'Circuit breaker \"{target}\" reset to CLOSED')
else:
    print(f'Circuit breaker \"{target}\" not found')
    print(f'Available: {list(cbs.keys())}')

tmp_fd, tmp_path = tempfile.mkstemp(dir=os.path.dirname(state_path))
with os.fdopen(tmp_fd, 'w') as tmp:
    json.dump(state, tmp, indent=2, ensure_ascii=False)
os.replace(tmp_path, state_path)
print('State saved')
"

4. 전체 서킷브레이커 일괄 해제

주의: 근본 원인이 해소되지 않은 상태에서 일괄 해제하면 장애가 재발합니다.

python3 -c "
import json, os, tempfile

state_path = os.path.expanduser('~/.openclaw/workspace/memory/orchestrator_state.json')

with open(state_path) as f:
    state = json.load(f)

cbs = state.get('circuit_breakers', {})
reset_count = 0

for name, cb in cbs.items():
    if cb.get('state') != 'CLOSED':
        cb['state'] = 'CLOSED'
        cb['failure_count'] = 0
        cb.pop('last_failure_at', None)
        reset_count += 1
        print(f'  Reset: {name}')

tmp_fd, tmp_path = tempfile.mkstemp(dir=os.path.dirname(state_path))
with os.fdopen(tmp_fd, 'w') as tmp:
    json.dump(state, tmp, indent=2, ensure_ascii=False)
os.replace(tmp_path, state_path)
print(f'Total reset: {reset_count} circuit breakers')
"

5. 해제 후 작업 수동 트리거 (선택)

# 해제한 파이프라인을 즉시 한 번 실행하여 정상 동작 확인
# 예: blog_monitor
python3 ~/.openclaw/workspace/scripts/pipeline/blog_monitor.py --once 2>&1 | tail -20

확인 방법

# 서킷브레이커 상태 재확인
python3 -c "
import json
with open('$HOME/.openclaw/workspace/memory/orchestrator_state.json') as f:
    state = json.load(f)
cbs = state.get('circuit_breakers', {})
open_cbs = [n for n, cb in cbs.items() if cb.get('state') != 'CLOSED']
if open_cbs:
    print(f'Still OPEN: {open_cbs}')
else:
    print('All circuit breakers CLOSED')
"

# 전체 시스템 헬스체크
python3 ~/.openclaw/workspace/scripts/health_check.py

# ops DB 상태 확인
python3 ~/.openclaw/workspace/scripts/check_ops_db.py