Go to file
민헌 b4d6cf817b feat(anchoring): dry-run 모드·박제 정합 감시·전환기 점프 절차 — 실배포 준비
종합 피드백에서 남긴 마지막 개선 3종:

- --once --dry-run: 판정·예상 조정(조정예정 라인)·제외 예정 건수를 로그로만
  보고 DB/Redis 를 일절 변경하지 않는 예행 연습 — 첫 운영 실행(레거시 전량
  판정·마킹) 전에 규모를 눈으로 확인하는 안전장치. status=dry_run, 종료코드 0
- 박제 정합 감시: 스캔 시 정수식 tp×(1000−rate)//1000 과 박제 anchor 를 대조,
  불일치 시 WARN + 요약에 snapshot_mismatch — negodata 이식 오류(float 잔재·
  칸 해석 오류)를 적용 첫 주에 자동 감지. 전환기(rate 미박제)엔 자동 스킵
- 전환기 점프 절차화: 인수인계 적용 순서에 "negodata 적용 직전 current_rates
  분포 확인 → 점프 감수/이력 리셋 정책 결정" 단계 삽입 + TODO 등재

테스트 2종 추가(dry-run 무변경·정합 WARN) — 모듈 20개·backend 57개 통과.
리허설 완료: dry-run 예상과 실제 실행 결과 일치 확인(제외 3·조정 1칸 10‰→30‰).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-02 21:13:28 +09:00
agent md 파일 삭제 . 2026-06-24 14:32:38 +09:00
backend fix(anchoring): 3방향 적대 리뷰 반영 — 테스트 격리·Redis 방어·운영 견고화 2026-07-02 20:55:46 +09:00
frontend [fix] 견적 자동마감 동시성·정합성 + 프론트 안정화 (코드리뷰 후속) 2026-06-25 17:22:21 +09:00
negodata [docs] negodata: README 갱신 — 테스트 실행법·API 도메인·프론트 포트 2026-07-01 15:06:55 +09:00
postgres-init fix(anchoring): 3방향 적대 리뷰 반영 — 테스트 격리·Redis 방어·운영 견고화 2026-07-02 20:55:46 +09:00
schedules/anchoring feat(anchoring): dry-run 모드·박제 정합 감시·전환기 점프 절차 — 실배포 준비 2026-07-02 21:13:28 +09:00
.gitignore Agent 서버 구축: 멀티테넌트 협상 PoC (UCB Q-Table 학습 + /chat + 14 API + 학습검증 하네스), config 단일화(local.toml) + 빌드 경량화 2026-06-17 11:10:03 +09:00
docker-compose.yml [feat] negodata/backend: 견적 자동마감 스케줄러 + 다음 라운드 재생성(자동/수동) 2026-06-23 16:36:22 +09:00
README.md [chore] negodata: CORS client_url 예시 + 시드 정비 + README DB 절차 갱신 2026-06-17 15:59:08 +09:00

O2O Negosium

동일 구조의 두 서비스(negosium, negodata)가 하나의 PostgreSQL 인스턴스를 공유한다.

구성

o2o-negosium/
├── docker-compose.yml          # 두 backend (DB 는 외부)
├── postgres-init/        # DB·테이블 셋업 SQL (대상 DB 에 1회 적용)
├── backend/                     # negosium 백엔드 (포트 9300)
├── negodata/backend/            # negodata 백엔드 (포트 9400)
├── agent/  front/               # (예정)
└── negodata/front/              # (예정)

두 백엔드는 같은 코드 골격(MVC · 람다 DB · Depends 주입 · JWT 로그인)을 쓴다. 아키텍처/패턴 상세는 각 서버 README 참고: backend · negodata/backend

DB 는 compose 밖 (config 로 연결)

DB 는 docker-compose 에서 관리하지 않는다. 각 backend 는 config.<APP_ENV>.toml 의 접속 정보대로 외부 PostgreSQL(호스트 로컬 postgres, 또는 따로 떠 있는 docker postgres)에 연결한다. 한 PostgreSQL 안에 서비스별 database 를 둔다.

PostgreSQL (외부, 5432)
├── negosium_db     ← negosium-backend
└── negodata_db     ← negodata-backend
  • 컨테이너(docker env)에서 호스트 DB 접근: host.docker.internal:5432 (config.docker.toml)
  • 로컬 실행/테스트(local·test env): 127.0.0.1:5432 (config.local/test.toml)
  • 계정/database 명은 config 에 맞춘다 (기본 postgres / password).
서비스 서버 docs database
negosium-backend http://localhost:9300 /docs negosium_db
negodata-backend http://localhost:9400 /docs negodata_db

빠른 시작

# 1) DB 준비 (최초 1회) — 사용할 PostgreSQL 에 스키마 + 시드 적용
psql -h 127.0.0.1 -p 5432 -U postgres -f postgres-init/01-schema.sql  # negosium_db + 도메인 schema
psql -h 127.0.0.1 -p 5432 -U postgres -f postgres-init/02-learning-schema.sql  # agent learning 스키마
psql -h 127.0.0.1 -p 5432 -U postgres -f postgres-init/03-seed-negodata.sql   # negodata 전용 시드

# 2) 백엔드 기동
docker compose up -d            # 두 backend (DB 는 config 대로 외부 연결)
docker compose logs -f
docker compose down

테스트

# config.test.toml 의 PostgreSQL(기본 127.0.0.1:5432) 이 떠 있어야 한다
cd backend            # 또는 negodata/backend
pip install pytest pytest-asyncio httpx
python -m pytest
  • httpx ASGITransport 로 네트워크 없이 앱을 직접 호출하는 e2e (각 5개).
  • DB_SESSION_MNG 싱글톤의 커넥션 풀이 첫 이벤트 루프에 묶이므로, 모든 테스트가 단일 session 루프를 공유한다(pytest.ini).

성능 / 벤치마크

/loginbcrypt(CPU 바운드) 가 비용의 대부분이다. 초기에는 bcrypt 가 asyncio 이벤트 루프를 막아 아무 일도 안 하는 /healthz 조차 p99 3.3s 가 나왔다.

두 가지 최적화

  1. bcrypt 를 asyncio.to_thread 로 오프로드 — 이벤트 루프 비차단. bcrypt 는 해싱 중 GIL 을 해제하므로 스레드들이 여러 코어에서 실제 병렬 실행된다.
  2. 워커 수 증가 (process_count 1 → 4) — login 처리량을 코어만큼 확장.

Before / After (동일 부하: 100 users)

지표 Before After 변화
/healthz median 1700ms 2ms 850배 개선
/healthz p99 3300ms 14ms 235배 개선
/me p99 3100ms 12ms 258배 개선
/login median 9900ms 220ms 45배 개선
/login p99 15000ms 1400ms 11배 개선
/login RPS 5.5 19.3 3.5배 개선
전체 RPS 17.6 75.2 4.3배 개선

최적화 후 Locust 차트 (100 users)

RPS 가 ~71 로 안정, p95 ~250ms(bcrypt), 실패 0%. median 은 초기 계정생성 버스트 후 바닥으로 떨어진다.

Locust Benchmark

login 은 여전히 가장 느리다(bcrypt 의 의도된 비용). 핵심은 그게 서버 전체를 막지 않는다는 점. 더 높은 처리량은 워커/인스턴스 수평 확장이 정석이다(bcrypt cost 낮추기는 보안 트레이드오프). ⚠️ to_thread 가 이미 단일 워커에서 멀티코어 병렬화를 하므로, 워커를 코어 수만큼 늘리면서 to_thread 까지 쓰면 워커 x 스레드 가 코어를 넘어 오버서브스크립션이 된다(워커는 코어의 절반 안팎).

부하 재현:

docker compose up -d
cd backend && pip install locust
python -m locust -f loadtest/locustfile.py --host http://localhost:9300 --headless -u 100 -r 10 -t 2m
# 부하 중 커넥션 모니터링: psql -h 127.0.0.1 -U postgres -c "SELECT count(*) FROM pg_stat_activity;"

기술 스택

FastAPI · SQLAlchemy(async) · asyncpg · PostgreSQL 16 · python-jose(JWT) · bcrypt · uvicorn · Docker Compose