Skip to content

[FB-234] 세션·스레드 런타임 잔여 결함 모음(굶음·지연·종료 단계 경합) #655

Closed

Description

@boyism80
  • 심각도: low
  • 근거 강도: 항목별 보통~약함 (경로는 코드로 확인, 대부분 과부하·종료·µs 창 전제)
  • 대상 커밋: 8576836 (대표 항목은 이 HEAD에서 코드로 다시 확인, 나머지는 기록 당시 판단 기준이라 수정 확인 때 항목별로 다시 봐요)
  • 출처: P01·P02 회차(110·139·140·185·189회차 등)

이 이슈는 '기준 미달 후보도 모두 이슈로 등록'(2026-10-16 사용자 지시)에 따라, 과거 정기 검수·push 리뷰에서 기록만 하던 후보를 영역별로 모은 것이에요. 항목마다 데이터 복제·크래시·로그인 불가가 아니어서 이전 기준에는 못 미쳤어요. 이미 고쳐진 것, 기존 이슈와 겹치는 것, 예외 목록·사용자 확인 의도, 크래시 전제 시나리오는 뺐어요. 항목별로 따로 처리하기 원하시면 나눌게요.

항목

  1. thread::on_thread가 큐가 비어야 on_idle을 돌려, 스레드가 포화되면 타이머(버프·몹 AI·100ms 재시도)가 굶음 — server/fb/src/thread.cpp
  2. 큐가 빈 뒤 최대 100ms sleep으로 새 작업 시작이 지연됨(폴링 설계) — thread.cpp
  3. lazy_container Phase 1 try 실패(16버킷 공유 경합)만으로도 HTTP fetch를 하고 최대 10회 재시도 — include/fb/lazy_container.h
  4. NOT_FOUND 결과를 nullptr 묘비로 erase 전까지 영구 캐시 — lazy_container.h
  5. Phase 3 해제~Phase 4 획득 사이 해산 erase가 끼면 'Element not found' 예외(µs 창)
  6. async_shared_mutex unlock 후 wake-all 재시도 barging(쓰기 전용 사용이라 현재는 굶음 없음) — include/fb/synchronized.h
  7. 쓰기 실패 완료 핸들러의 close가 다른 IO 스레드의 async_read와 같은 asio 소켓을 동시에 만짐 — server/fb/src/tcp_socket.cpp
  8. outbound flush 전 버퍼 상한 없음 — server/fb/src/outbound_buffer.cpp
  9. 종료 중 thread timer가 _running을 확인하지 않음, 종료 teardown 뒤 exit된 스레드 큐에 남은 async lock 깨우기는 재개되지 않음
  10. encryption 객체 복사 때 _sequence가 복사되지 않음 — server/fb/src/encryption.cpp
  11. login 서버 비밀번호 잠금 메시지 co_await send 실패 시 close 대신 fatal 로그만 남김
  12. 쓰이지 않는 mutex/dead_lock 코드가 남아 있음(가독성) — include/fb/mutex.h, server/fb/src/dead_lock.cpp
  13. scope::GROUP send 경로 호출처 0(죽은 분기)

수정 방향

  • on_thread에서 큐 처리 중에도 일정 간격으로 타이머를 돌리고, idle sleep은 조건 변수로 깨우게 해요.
  • lazy_container는 Phase 1 경합 시 짧게 재시도한 뒤 fetch하고, 묘비에는 TTL을 둬요.
  • close는 소켓 strand(또는 IO 스레드 post)로 보내요. outbound 버퍼에는 상한과 초과 시 close를 둬요.
  • 종료 단계 타이머·lock 대기는 종료 플래그로 일괄 취소해요. 미사용 코드는 지워요.

Activity

  1. boyism80 commented on Oct 16, 2026

    @boyism80
    OwnerAuthor

    13번 scope::GROUP은 제거하지 않습니다.

    그룹 멤버에게 보내는 패킷은 groups.broadcast가 월드마다 알린 뒤, 각 멤버의 로직 스레드에서 보냅니다. 멤버는 다른 맵, 다른 로직 스레드, 다른 월드에 있을 수 있습니다. scope::GROUP은 이 프로세스에 캐시된 그룹을 읽기 잠금으로 잡은 채 호출한 스레드에서 send하므로, 그 경로를 대신할 수 없습니다. 근처에 보이는 패킷은 PIVOT 또는 MAP이 맞습니다.

    호출처가 지금 없어서 죽은 분기로 보이지만, 나중에 같은 스레드의 로컬 멤버에게만 보내는 경우를 위해 남깁니다.

  2. boyism80 commented on Oct 16, 2026

    @boyism80
    OwnerAuthor

    [FB 리뷰어]

    확인했습니다. 항목 13 scope::GROUP은 의도된 경로로 남기고, 호출처가 없어도 죽은 분기 제거로 재지적하지 않겠습니다.

    그룹 멤버에게 가는 실제 전송은 groups.broadcast(월드마다 알린 뒤 각 멤버 로컬 스레드에서 송신)이고, scope::GROUP은 같은 스레드의 로컬 멤버에게만 보낼 때용으로 이해했습니다. 근처 가시 패킷은 PIVOT/MAP이 맞다는 점도 반영합니다.

    이슈는 항목 1–12가 남아 있어 open으로 둡니다.

  3. boyism80 commented on Oct 16, 2026

    @boyism80
    OwnerAuthor

    [FB 리뷰어] 사용자 지시(2026-10-17 00:10 KST)에 따라 기준 미달 항목은 이슈 대상에서 제외해 not planned로 닫아요. 내용은 audit 문서에 남겨 두고, 앞으로 기준 미달 항목은 묶음 브리핑에 한 줄로만 알려요.

  4. boyism80 commented on Oct 16, 2026

    @boyism80
    OwnerAuthor

    [FB 리뷰어] 사용자 지시(2026-10-17 00:14 KST): 메모리 누수 가능성이 있는 항목은 기준 미달로 분류하지 않아요. 그래서 이슈를 다시 열어요. 남은 항목은 4번(lazy_container.h NOT_FOUND nullptr 묘비가 erase 전까지 영구 캐시돼 계속 쌓임)과 8번(outbound_buffer.cpp flush 전 버퍼 상한 없음)이에요. 나머지 항목은 기준 미달이라 대상에서 빼고 audit 문서에만 남겨요(13번은 의도로 확인됨).

  5. boyism80 commented on Oct 16, 2026

    @boyism80
    OwnerAuthor

    [FB 리뷰어] 3b211e7·11ce23615를 확인했어요. 8번(outbound 버퍼 상한)은 해결됐어요. outbound_buffer::append와 tcp_socket::write가 MAX_PENDING_WRITE_BYTES(4MB)를 넘으면 소켓 strand에서 연결을 닫아요. 4번(NOT_FOUND nullptr 묘비 영구 캐시)은 아직 남아 있어요. 묘비를 지우는 경로가 없고, 이번에 추가된 _as_of 버전 맵도 지워지지 않는 데다 락 없이 공유되고 있어요. 이건 #683(FB-262)으로 따로 올렸어요. 4번이 남아 있어서 open으로 둘게요.

  6. boyism80 commented on Oct 16, 2026

    @boyism80
    OwnerAuthor

    [FB 리뷰어] 12fb2ae08을 확인했어요. 4번(NOT_FOUND·해체 nullptr 묘비 영구 캐시)은 아직 남아 있어요. 이번 커밋은 버전 맵을 clan/group 컨테이너로 옮긴 것이고, 묘비가 사라지는 건 여전히 같은 id로 더 새로운 메시지가 와서 open()이 erase할 때뿐이에요. 해체된 그룹·문파와 없는 id는 다시 오지 않으니 묘비와 _updated_at 항목이 계속 쌓여요(#683과 같은 원인). open으로 둘게요.

  7. boyism80 commented on Oct 16, 2026

    @boyism80
    OwnerAuthor

    [FB 리뷰어] 0a17f9352를 확인했어요. 4번(NOT_FOUND·해체 nullptr 묘비 영구 캐시)이 해결됐어요. NOT_FOUND 결과는 슬롯에 넣지 않고 guard{}로 돌려줘요(lazy_container.cpp:128-133). 문파·그룹 해체는 guard.value() = nullptr 대신 가드를 놓은 뒤 erase해요(clan/events.cpp:220, group/events.cpp:369). 이제 nullptr 슬롯을 쓰는 곳이 없고, 혹시 남아 있으면 조회할 때 지워요(lazy_container.cpp:87-92). 8번은 11ce23615에서 이미 해결됐고, 13번은 의도된 경로로 남기기로 했어요. 나머지 항목은 기준 미달로 대상에서 뺐으니 이슈를 닫을게요.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions