콘텐츠로 이동

종료·정리

Socket Backend에는 서로 다른 세 수명주기가 있습니다.

  • 응답 턴 종료: gate·USER lease·지연 표본을 반납
  • 대화 최종화: 저장된 메시지의 분석·요약을 마무리
  • WebSocket 세션 종료: 세션 맵과 외부 연결을 제거하고 ended_at 기록

이 셋은 같은 시점에 일어나지 않을 수 있습니다. 특히 DIALOG_END와 DM의 session complete는 대화 최종화를 시작하지만 WebSocket 연결을 반드시 닫지는 않습니다.

턴 정리

sequenceDiagram
    participant T as Turn Task
    participant O as TTS/Client Output
    participant L as Turn Latency
    participant G as AudioInputGate
    participant D as DM Processing Guard
    participant A as Immediate Analysis

    T->>O: TTS 오디오 전송
    T->>O: dialog_response 전송
    T->>L: 미완료 표본을 success/cancelled/failed로 확정
    T->>G: resume(이 턴의 PauseToken)
    T->>D: finishProcessing(이 턴의 Lease)
    opt deferred-enabled=false
        T->>A: USER WAV 분석 시작
    end

gate는 유효한 END를 수락한 뒤 닫히고 TTS와 dialog_response 전송이 끝난 뒤 열립니다. 오류나 조기 반환도 finally를 통하므로 token과 lease를 반납합니다. 즉시 분석은 gate resume 뒤에 실행되어 다음 입력을 막지 않습니다.

TurnCompletedEvent는 EPD·오디오 자원 정리에 쓰입니다. TIMEOUT은 응답 수신 스레드에서 먼저 구 EPD 연결을 무효화하므로 늦게 실행된 이벤트가 새 연결을 닫지 않습니다. 그 밖의 turn-complete 이벤트는 EPD 연결과 연결 추적 상태를 정리합니다.

대화 최종화

최종화 트리거는 클라이언트 DIALOG_END, DM session_complete/context.session_ended, 실제 WebSocket 제거로 발행되는 SessionEndEvent입니다. 같은 dialog의 동시 호출은 single-flight로 합치며, 완료 후 새 CONFIRMED 메시지가 생기면 새 입력 버전으로 다시 실행할 수 있습니다.

sequenceDiagram
    participant X as Finalization Trigger
    participant F as DialogFinalizationService
    participant R as Pending Record Tail
    participant A as Analysis
    participant C as Conversation Summary
    participant B as Biomarker Summary
    participant DB as PostgreSQL
    participant M as Maumon

    X->>F: finalizeDialogAsync(sessionId, dialogId)
    F->>R: pending records 대기(최대 5초)
    R-->>F: 완료 또는 timeout
    opt 지연 분석 모드
        F-->>X: analysis_processing(best effort)
    end
    par 분석 마무리
        F->>A: deferred flush 또는 immediate future 대기
        A->>DB: 발화별 분석 저장
        A-->>F: 성공 여부
    and 일반 대화 요약
        F->>C: CONFIRMED 메시지 요약
        C->>DB: 요약·키워드·감정 upsert
        C-->>F: 성공 여부
    end
    F->>F: 두 작업 join
    F->>B: 바이오마커 요약 생성
    B->>DB: 대화 단위 바이오마커 요약 저장
    B-->>F: 성공 여부
    opt 일반 요약 성공
        F-)M: 저장된 요약 write-back 예약
        Note over F,M: 결과를 기다리지 않음
    end
    F-->>X: analysis_complete(best effort, 지연 모드)

일반 요약과 분석은 병렬이며, 바이오마커 요약은 둘의 join 뒤 실행합니다. 일반 요약은 “요약 행이 있는가”만 보지 않고 요약 시각 이후 새 메시지가 있는지 확인해 전체 대화를 다시 요약합니다. 마음온 회신은 이미 저장된 요약만 읽으며, 별도 가상 스레드에서 재시도 없이 실행합니다.

ended_at의 정확한 의미

ended_at은 보편적인 최종화 마지막 단계가 아닙니다

dialog.ended_at은 SessionEndEvent 리스너가 finalizeDialog를 호출하기 직전 최초 한 번 기록합니다. DIALOG_END나 DM 완료만으로 실행된 최종화는 ended_at을 기록하지 않습니다. WebSocket이 실제로 세션 관리자에서 제거되기 전까지 NULL일 수 있습니다.

세션 종료 정리

정상 close, transport error, 유휴 세션 제거는 모두 세션 맵의 원자적 제거로 수렴합니다. 제거에 성공한 한 경로만 SessionEndEvent를 발행하고 활성 연결 게이지를 감소시킵니다. 이벤트 소비자는 다음을 멱등 정리합니다.

  • 선택된 Conversation Engine과 OpenAI upstream
  • DmProcessingGuard, response/utterance generation, AudioInputGate
  • 무응답 타이머와 턴 지연 표본
  • workspace별 연결 지표
  • DM 원격 세션 회수
  • EPD 연결·activity 상태
  • ended_at 기록과 마지막 대화 최종화

OpenAI upstream이 비정상 종료되면 늦게 등록될 임시 artifact까지 record tail 뒤에서 폐기합니다. 로컬 세션 종료는 upstream close handshake를 요청하고 제한 시간 안에 끝나지 않으면 abort합니다.