DOCS/troubleshooting/250914_admin_robeing_monitor_schema_error.md
happybell80 e8628693b2 docs: robeing-monitor DB 스키마 문제 해결 완료 업데이트
- 상태를 ' 해결됨'으로 변경
- 두 번의 수정 내역 상세 기록
- 테스트 결과 추가
2025-09-19 02:11:45 +09:00

6.5 KiB

robeing-monitor DB 스키마 불일치

작성일: 2025-09-14

작성자: admin

환경: 51124 서버, robeing-monitor, PostgreSQL

서비스 정보

  • 서버: 51124 (192.168.219.52)
  • 포트: 9024
  • 컨테이너: robeing_monitor (38시간 가동 중, healthy)
  • DB 연결: postgresql://robeings:robeings@192.168.219.45:5432/main_db

문제 증상

Gateway에서 stats API 호출 시 매분마다 500 에러 발생

에러 로그

sqlalchemy.exc.ProgrammingError: column robeing.robeing_id does not exist
LINE 1: SELECT robeing.id AS robeing_id_1, robeing.robeing_id AS rob...

원인

robeing-monitor가 잘못된 테이블/컬럼 조회

  • 코드 예상: robeing_stats 테이블의 robeing_id 컬럼
  • 실제 DB: robeing 테이블의 robeing_container_id 컬럼

현재 영향

  • /api/stats/{robeing_id} 엔드포인트 실패 (500 에러)
  • Gateway 프록시: http://192.168.219.52:9024/api/stats/rb8001 호출 실패

Gateway에서 robeing-monitor 호출 경로

# /home/admin/robeing-gateway/app/main.py
@app.get("/api/stats/{robeing_id}")  # 라인 296-297
target_url = f"http://192.168.219.52:9024/api/stats/{robeing_id}"

@app.api_route("/api/preferences/{path:path}")  # 라인 364-391
monitor_url = "http://192.168.219.52:9024"

@app.api_route("/api/items/{path:path}")  # 라인 150-167
monitor_url = os.getenv("MONITOR_URL", "http://192.168.219.52:9024")

robeing-monitor 제공 API

  • GET /api/stats/{robeing_id} - 로빙 스탯 조회 (500 에러)
  • PUT /api/stats/{robeing_id} - 스탯 업데이트
  • GET /api/preferences/{user_id} - 사용자 설정
  • PUT /api/preferences/{user_id} - 설정 업데이트
  • GET /api/items/gmail - Gmail 아이템 조회
  • GET /healthz - 헬스체크 (정상)

상태

해결됨 - 2025-09-19

이전 해결 시도 (2025-09-15)

  • robeing 테이블 관련 코드: robeing_id → robeing_container_id 수정 시도
  • ConversationLog 테이블: robeing_id → robeing_container_id 수정
  • SQLAlchemy 쿼리: 일부 수정
  • 결과: /api/stats는 일시적 해결, /api/items/gmail은 미해결

2025-09-19 최종 해결

해결 내역

  1. 첫 번째 수정 (커밋 9f302f0):

    • gmail_token 조회 시 user 테이블과 JOIN
    • WHERE u.oauth_id = $1 사용
    • robeing 조회 시 robeing_container_id 사용
  2. 두 번째 수정 (커밋 d76f2b7):

    • 엔드포인트 중복 경로 제거 (/items/gmail 중복 선언 제거)
    • unequip API의 slack_user_id 참조도 user JOIN으로 변경
    • 모든 gmail_token 조회를 user.oauth_id 기준으로 통일

테스트 결과

  • /api/items/gmail 200 OK 응답 확인
  • DB 스키마 에러 없음
  • 서버 자동 배포 완료 (17:06:30)

2025-09-19 문제 재발견 및 명확화 (해결 전 상태)

실제로는 두 개의 별개 문제

  1. robeing 테이블 문제 (2025-09-15 부분 해결)

    • 엔드포인트: /api/stats/{robeing_id}
    • 원인: robeing.robeing_id 컬럼 없음 (실제: id, robeing_container_id)
    • 상태: 일부 해결됨
  2. gmail_token 테이블 문제 (2025-09-19 신규 발견)

    • 엔드포인트: /api/items/gmail
    • 에러: asyncpg.exceptions.UndefinedColumnError
    • 원인: gmail_token.robeing_id, gmail_token.slack_user_id 컬럼 없음
    • 컨테이너 ID: 2e52e648f02f

확인된 테이블 구조 (51123 서버 PostgreSQL - 2025-09-19 재확인)

-- robeing 테이블
id                    | uuid (PK)
robeing_container_id  | varchar(64) -- 'rb8001' 형태의 값
level, experience, memory  | integer
-- robeing_id 컬럼 없음

-- gmail_token 테이블 (현재 0 rows)
id                   | uuid (PK)
user_id              | uuid (user 테이블 FK)
token_data           | jsonb
equipped_to          | varchar(50)  -- 현재 값 없음
is_equipped          | boolean
-- robeing_id, slack_user_id, robeing_container_id 컬럼 없음

-- user 테이블
id                   | uuid (PK)
oauth_id            | varchar -- Slack ID ('U'로 시작) 또는 Google ID
-- 총 14명: Slack 8명, Google 6명

robeing-monitor 코드 문제점 (구체적 위치)

  1. /app/state/database.py:

    • SQLAlchemy 모델이 Integer PK 정의 (실제 DB는 UUID)
    • RobeingStats는 클래스명, 실제 테이블은 robeing
  2. /app/api/items.py 에러 발생 지점:

    • 101행: SELECT에서 robeing_id, 조회 → 컬럼 없음 에러
    • 108행: WHERE slack_user_id = $1 → 컬럼 없음
    • 130행: row['robeing_id'] 접근 시도
    • 207행: equipped_to = request.robeing_id 설정 (이건 작동)
  3. 실제 필요한 JOIN:

    • gmail_token.user_id = user.id
    • WHERE user.oauth_id = {slack_id}

2025-09-15 업데이트

DB 스키마 확인 (51123 서버)

-- robeing 테이블 실제 구조
id                    | uuid
product_id            | uuid
team_id               | uuid
name                  | varchar(32)
level                 | integer
experience            | integer
memory                | integer
compute               | integer
react                 | integer
empathy               | integer
leadership            | integer
updated_at            | timestamp
created_at            | timestamp
robeing_container_id  | varchar(64)
robeing_container_url | varchar(128)

확인된 사실

  • robeing_id 컬럼 없음 확인
  • robeing-monitor는 51124 서버에서 실행 중
  • 51123 서버 DB에는 id(UUID) 컬럼만 존재

문제 원인 분석

SQLAlchemy 쿼리 분석:

SELECT robeing.id AS robeing_id_1, robeing.robeing_id AS rob...
  • robeing.id를 robeing_id_1로 별칭 처리
  • 존재하지 않는 robeing.robeing_id 컬럼도 조회 시도
  • 원인: robeing-monitor의 SQLAlchemy 모델에 id와 robeing_id 둘 다 정의됨

해결 방안 (2025-09-19 최종)

즉시 수정 가능한 부분 (items.py):

# 101행: robeing_id, 제거 또는 equipped_to로 변경
SELECT id, user_id, equipped_to, is_equipped, token_data...

# 108행: slack_user_id를 user JOIN으로 변경
FROM gmail_token gt
JOIN "user" u ON gt.user_id = u.id
WHERE u.oauth_id = $1

# 130행: row['robeing_id'] → row.get('equipped_to')로 변경

구조적 개선 필요:

  • database.py: Integer → UUID 타입 수정
  • 모든 robeing_id 참조를 id 또는 robeing_container_id로 통일

로컬 작업 절차:

  1. robeing-monitor 레포 clone
  2. 위 수정사항 적용
  3. Git push → 51124 자동 배포
  4. 테스트: curl http://192.168.219.52:9024/api/items/gmail

테스트:

# 51124 서버에서
curl http://192.168.219.52:9024/api/stats/rb8001
curl http://192.168.219.52:9024/api/items/gmail