Skip to content

perf(rag): 사전 계산 query embedding 기반 KB fan-out 병렬화 - #632

Merged
HyungminYoon1 merged 17 commits into
devfrom
feature/mba-354
Jul 31, 2026
Merged

perf(rag): 사전 계산 query embedding 기반 KB fan-out 병렬화#632
HyungminYoon1 merged 17 commits into
devfrom
feature/mba-354

Conversation

@HyungminYoon1

Copy link
Copy Markdown
Contributor

변경 사항

  • 권한 검증을 통과한 KB 검색을 application scheduler와 gevent native-thread adapter로 최대 5개까지 제한 병렬화했습니다.
  • KB별 독립 read-only session, transaction-local statement_timeout, DBAPI cancellation, rollback/close 경계를 추가했습니다.
  • Child KB 조회를 organization_id + knowledge_base_id로 제한하고, 조직 컨텍스트가 없으면 DB 접근 전에 종료합니다.
  • completion order와 무관한 candidate ordinal 재조립 및 기존 evidence/citation 정렬 계약을 유지했습니다.
  • RAG 단계별 지연 5종을 trace allowlist에 추가하고 일반 Workflow result metadata에서는 제외했습니다.
  • raw retrieval/executor 오류를 고정된 safe 오류로 정규화하고 PostgreSQL CI 선택 경로를 추가했습니다.
  • 1/2/4/10 KB synthetic benchmark 도구와 공식 Knowledge/Audit 문서를 갱신했습니다.

관련 이슈

Closes #589

Linear: MBA-354

변경 유형

  • 버그 수정
  • 새로운 기능
  • 리팩토링
  • 문서 수정
  • 기타: 성능 개선

테스트

  • 로컬에서 테스트 완료
  • 기존 테스트 통과 확인

로컬 검증:

  • 관련 Workflow Engine/Shared/CI 계약 테스트: 310 passed
  • Ruff check: 통과
  • 20ms, 20회 synthetic benchmark:
    • 1 KB p50: sequential 20.3ms / fan-out 21.0ms
    • 2 KB p50: sequential 40.7ms / fan-out 21.1ms
    • 4 KB p50: sequential 81.5ms / fan-out 21.4ms
    • 10 KB p50: sequential 203.3ms / fan-out 42.4ms
    • 최대 active worker: 1/2/4/5

CI 위임:

  • disposable PostgreSQL read-only transaction
  • transaction-local statement timeout 및 pg_sleep 취소
  • rollback 후 pooled connection 재사용과 timeout 비누출

보호 리소스·외부 실행 경계 (해당 시)

  • 적용 여부: [x] 적용 [ ] 비적용
  • 비적용 사유:
  • 완결성 매트릭스의 계약·구현·검증 증거:
    • 정책/조직 범위: runtime candidate resolver + sync retrieval org predicate tests
    • runtime: zero-candidate/provider 0회 및 bounded fan-out node/application tests
    • transaction: session unit tests 통과, disposable PostgreSQL test CI 대기
    • redaction: scheduler/retrieval safe error 및 Shared trace sanitizer tests
  • 저장·GraphMutation과 secret 비저장
  • 관리 API/UI와 user/team 권한 부여·회수
  • Deployment preflight와 runtime/background 재검증
  • Lifecycle·TOCTOU·stable identity/deduplication·crash replay·capability/lease
  • Audit event 생성·canonical action/status·중복 방지·secret/PII redaction
  • 적용 동작 경계마다 구현 위치와 실행 가능한 테스트 증거 기록
  • 미완료 항목 또는 후속 이슈:
    • 신규 durable 저장, 관리 API/UI, deployment preflight, lifecycle mutation은 이번 변경에 해당하지 않습니다.
    • PostgreSQL 환경 검증은 Test Knowledge Runtime PostgreSQL CI에서 확인합니다.
    • query embedding cache는 MBA-355, Authorized Retrieval Port 통합은 MBA-289 범위입니다.
    • CI control workflow 변경으로 현재 head에 대한 독립 승인이 필요할 수 있습니다.

스크린샷 (UI 변경 시)

UI 변경 없음

@linear-code

linear-code Bot commented Jul 22, 2026

Copy link
Copy Markdown

MBA-354

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: d6a0ac7465

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +2832 to +2834
executor_factory=GeventNativeThreadRAGRetrievalExecutor,
cancellation_factory=NativeThreadRAGRetrievalCancellation,
max_workers=MAX_RAG_FANOUT_CONCURRENCY,

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge RAG 실행 간 native thread 수를 전역으로 제한하세요

동시 RAG 실행마다 여기서 새 scheduler를 만들고 execute()가 호출별로 최대 5-thread ThreadPool을 별도 생성합니다. 운영 entrypoint는 Celery --concurrency=100이므로 RAG burst에서 프로세스 하나가 최대 500개 native worker를 만들 수 있지만 SessionLocal의 DB pool은 overflow를 포함해 50 connection뿐이라, 대량의 thread가 connection을 기다리며 메모리와 스케줄링 자원을 소모하고 retrieval timeout/failure를 유발할 수 있습니다. 호출별 제한 외에 process-wide executor 또는 admission limit을 공유해야 합니다.

AGENTS.md reference: apps/AGENTS.md:L10-L15

Useful? React with 👍 / 👎.

break
state = queued.pop(0)
try:
job = executor.submit(lambda state=state: invoke(state))

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge CrossEncoder의 지연 초기화를 fan-out 전에 직렬화하세요

RAG_CROSS_ENCODER_RERANK_ENABLED=true인 상태에서 첫 multi-KB 검색을 실행하면 이 병렬 submit으로 여러 native worker가 RetrievalService._get_cross_encoder_model()의 잠금 없는 class cache를 동시에 통과할 수 있습니다. 그 결과 최대 5개의 대형 CrossEncoder가 중복 로드되어 초기 요청의 메모리 사용량이 급증하거나 OOM 후 rerank fallback이 발생하므로, 모델을 fan-out 전에 한 번 초기화하거나 cache 초기화를 lock으로 보호해야 합니다.

AGENTS.md reference: apps/AGENTS.md:L10-L15

Useful? React with 👍 / 👎.

Comment on lines +64 to +65
for callback in callbacks:
self._invoke_safely(callback)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge DB 취소 callback을 gevent hub 밖에서 실행하세요

PostgreSQL 장애나 packet loss 중 timeout이 발생하면 이 callback은 RAGRetrievalSessionRunner가 등록한 DBAPI connection.cancel()인데, scheduler의 gevent greenlet에서 동기 호출됩니다. 이 취소 요청 자체가 block되면 scheduler가 hard deadline을 다시 확인하지 못할 뿐 아니라 monkey-patched Celery 프로세스의 hub까지 멈춰 다른 workflow 실행도 지연되므로, DB 취소를 별도의 bounded native control 경로에서 실행하고 caller deadline과 독립적으로 수렴시켜야 합니다.

AGENTS.md reference: apps/AGENTS.md:L12-L15

Useful? React with 👍 / 👎.

Comment on lines +271 to +272
if not running:
continue

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge 시작 budget이 부족하면 queued loop를 즉시 종료하세요

실행 중인 job이 모두 끝난 시점에 queued task가 남아 있고 search deadline까지 남은 시간이 minimum_start_budget_ms 이하이면 submit loop는 아무 task도 꺼내지 않지만, 이 분기가 계속 continue하여 deadline까지 yield 없이 busy-spin합니다. 기본 설정에서도 최대 약 250ms 동안 Celery의 단일 gevent hub를 점유해 같은 프로세스의 다른 workflow greenlet을 전부 지연시킬 수 있으므로, 이 상태에서는 loop를 종료해 queued task를 timeout으로 수렴시켜야 합니다.

AGENTS.md reference: apps/AGENTS.md:L12-L15

Useful? React with 👍 / 👎.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 7b3f5bf782

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +323 to +324
finally:
self._cancel_safely(stop_signal)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge 외부 종료에서도 실행 중인 검색을 취소하세요

LLM 노드의 설정된 timeout이 fan-out의 30초보다 짧아 workflow_engine.pygevent.Timeout이 발생하면, 이는 Exception이 아닌 BaseException 계열이라 위의 except Exception을 건너뛰고 이 finally로 바로 진입합니다. 여기서는 stop_signal만 취소하고 이미 실행 중인 각 state.cancellation은 취소하지 않으므로 DBAPI cancel callback이 호출되지 않으며, 노드는 timeout으로 실패한 뒤에도 native worker가 최대 statement timeout까지 DB 연결을 점유하거나 rerank를 계속할 수 있습니다. finally에서도 모든 running state의 cancellation을 요청해야 합니다.

AGENTS.md reference: apps/AGENTS.md:L12-L15

Useful? React with 👍 / 👎.

Comment on lines +3242 to +3245
trace_summary.update(
self._rag_stage_latency_summary(
candidate_resolution_latency_ms=candidate_resolution_latency_ms,
)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge 후보 0개 경로에서는 정확한 단계 지연을 제거하세요

Collection의 모든 child가 권한·정책 필터에서 제외되는 경우처럼 후보가 0개이면 이 helper는 hidden/resource-hidden 상태와 정상 empty 상태를 동일한 safe-no-result로 합치지만, 새 코드가 정확한 candidate_resolution_latency_ms를 durable RAG trace에 추가합니다. docs/features/audit-tracing/requirements.md의 hidden 경로 allowlist는 safe reason/scope 등으로 제한되고 stage latency는 authorized retrieval에만 허용되므로, trace 조회자가 반복 실행의 해석 시간으로 hidden 후보의 존재나 규모를 비교하는 timing oracle이 생깁니다. 이 구분 불가능한 경로에서는 stage latency를 생략하거나 hidden allowlist에 맞게 coarse 값으로 축소해야 합니다.

AGENTS.md reference: AGENTS.md:L197-L198

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Contributor Author

CI 실패 원인을 조치했습니다.

  • 직접 원인: Gateway/Agent Builder 테스트 환경에는 gevent가 없는데, LLM entity import가 package __init__.py를 통해 worker 전용 LLMNode와 gevent adapter까지 즉시 로딩했습니다.
  • 근본 조치: data-only entity import와 worker runtime import를 분리하고 LLMNode를 명시적으로 요청할 때만 지연 로딩하도록 변경했습니다.
  • 회귀 방지: Gateway architecture test에서 LLM entity import가 worker runtime module을 로딩하지 않는지 subprocess로 검증합니다.
  • 수정 커밋: e9c9bd1e, 7b3f5bf7
  • 재검증: gateway-and-root-testsagent-builder-postgres 모두 통과했습니다.

trusted-ci-control/base-policy는 코드 실패가 아니라 CI workflow 변경에 대한 정책 차단입니다. 현재 head 7b3f5bf7를 다른 write/maintain/admin 권한자가 Approve한 뒤 /recheck-ci-control 코멘트를 남겨야 통과합니다.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 51e3378124

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

)
if cancellation.cancelled:
raise RAGRetrievalSessionTimeout()
result = operation(session)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge 취소 이후 다음 SQL 실행을 차단하세요

search_documents_sync()는 KB 확인과 hierarchy/vector/keyword 조회 등 여러 SQL을 순차 실행하지만, 여기서는 cancellation을 operation 전후에만 확인하고 DBAPI cancel() callback도 한 번만 dispatch합니다. 취소가 두 statement 사이 또는 직전 cancellation 확인 직후에 도착하면 idle connection에 대한 cancel은 이후 쿼리를 막지 못하므로, 취소된 작업이 다음 statement들을 각각 기존 statement_timeout 한도까지 계속 실행하며 KB별 10초 budget과 cleanup reserve가 지난 뒤에도 프로세스 공용 5-thread pool과 DB connection을 점유할 수 있습니다. 각 statement 전에 cancellation과 남은 절대 deadline을 확인하거나 transaction 전체에 적용되는 deadline을 강제해야 합니다.

AGENTS.md reference: apps/AGENTS.md:L12-L15

Useful? React with 👍 / 👎.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 24c5e4ddbb

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

failure: Exception | None = None
cleanup_failed = False
try:
connection = session.connection()

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge DB checkout에도 retrieval deadline을 적용하세요

DB pool이 소진되거나 pool_pre_ping이 지연되는 경우, 이 session.connection()은 cancel callback을 등록하기 전에 실행되므로 child cancellation과 절대 deadline으로 중단할 수 없습니다. 공용 engine의 pool_timeout은 60초(apps/shared/db/session.py)라 per-KB 10초 및 aggregate 30초가 끝난 뒤에도 native worker가 checkout에 남을 수 있고, 이런 작업 5개면 프로세스 공용 RAG pool을 모두 점유해 후속 RAG 요청까지 timeout시킵니다. Connection checkout 자체를 남은 budget으로 제한하거나 cancellation-aware한 bounded acquisition 경계 안으로 옮겨야 합니다.

AGENTS.md reference: apps/AGENTS.md:L12-L15

Useful? React with 👍 / 👎.

override = getattr(self, "_rag_retrieval_session_runner_override", None)
if override is not None:
return override
return RAGRetrievalSessionRunner(session_factory=SessionLocal)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge 주입된 session factory로 child 검색을 연결하세요

WorkflowEngine은 caller가 제공한 execution_context["db_session_factory"]를 보존하고 candidate resolution과 query-embedding runtime도 이를 사용하지만, 새 child retrieval만 여기서 전역 SessionLocal로 고정됩니다. 별도 DB에 바인딩한 embedded/child 실행이나 테스트 runtime에서는 후보를 주입된 DB에서 승인한 뒤 검색은 환경변수 기반 기본 DB로 보내므로 결과가 전부 누락되거나, 동일 UUID가 존재하면 다른 DB의 데이터를 읽게 됩니다. 각 worker가 독립 session을 갖는 성질은 유지하되 runner의 factory는 execution context에 주입된 factory를 사용해야 합니다.

AGENTS.md reference: apps/AGENTS.md:L7-L7

Useful? React with 👍 / 👎.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 7c8f8d60b4

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

def close(self) -> None:
if self._closed:
return
self._closed = True

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge 종료된 fan-out의 대기 greenlet을 취소하세요

CrossEncoder처럼 cancellation에 협조하지 않는 native 작업이 hard deadline 이후에도 process pool을 점유하면, 후속 invocation마다 submit()이 만든 greenlet들이 _pool.apply()의 빈 slot을 기다립니다. Scheduler가 deadline에 반환해도 이 close()는 flag만 변경하므로 해당 greenlet과 callback/task state가 계속 남고, 반복 요청 시 thread 수는 5개로 제한되어도 대기 greenlet은 누적되어 메모리와 RAG 처리 대기열을 고갈시킬 수 있습니다. 기존 리뷰 후 최신 HEAD에서는 pool thread가 공유되지만 제출된 job을 종료 시 추적·취소하지 않는 것이 새 근거이므로, 아직 시작하지 않은 job을 close에서 취소하거나 process-wide bounded admission을 적용해야 합니다.

AGENTS.md reference: apps/AGENTS.md:L12-L15

Useful? React with 👍 / 👎.

"apps/shared/tests/domain/test_knowledge_runtime_candidates.py",
"apps/shared/tests/services/test_knowledge_permission_runtime_bulk.py",
"apps/workflow_engine/adapters/knowledge_runtime_candidates.py",
"apps/workflow_engine/adapters/rag_retrieval_session.py",

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Connection acquirer 변경에도 PostgreSQL CI를 실행하세요

새 checkout deadline과 late-session 정리를 실제 PostgreSQL에서 검증하는 테스트는 rag_retrieval_connection_acquirer.py의 동작에 직접 의존하지만, 여기에는 session과 scheduler 경로만 추가되어 acquirer 파일 자체가 _KNOWLEDGE_POSTGRES_PATTERNS에서 빠져 있습니다. 따라서 이후 acquirer만 수정하는 PR은 일반 Workflow 단위 테스트만 실행하고 test_rag_connection_checkout_returns_before_shared_pool_timeout을 포함한 disposable PostgreSQL job을 건너뛰며, .github/workflows/test-knowledge-runtime-postgres.yml의 dev push 경로도 동일하게 누락되어 checkout/pool 회귀가 병합될 수 있으므로 두 selector에 해당 파일을 추가해야 합니다.

AGENTS.md reference: AGENTS.md:L117-L119

Useful? React with 👍 / 👎.

@HyungminYoon1

Copy link
Copy Markdown
Contributor Author

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: bdc2504882

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +35 to +36
"apps/workflow_engine/adapters/rag_retrieval_session.py",
"apps/workflow_engine/application/rag_retrieval_fanout.py",

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge 새 RAG DB 어댑터를 PostgreSQL 선택 범위에 포함하세요

rag_retrieval_connection_acquirer.py 또는 rag_retrieval_executor.py만 변경하는 후속 PR에서는 _KNOWLEDGE_POSTGRES_PATTERNS와 워크플로의 push.paths가 모두 일치하지 않아, 이번에 추가한 실제 PostgreSQL checkout·취소·connection 복구 테스트가 실행되지 않습니다. 특히 connection acquirer는 test_rag_retrieval_session_postgres.py가 검증하는 pool 고갈 경로의 직접 구현이므로 두 어댑터 경로를 selector와 워크플로 양쪽에 추가하고 해당 분류 테스트에도 포함해야 합니다.

AGENTS.md reference: AGENTS.md:L206-L207

Useful? React with 👍 / 👎.

Comment on lines +45 to +46
def test_node_factory_import_does_not_require_gevent_runtime() -> None:
"""Gateway-side graph validation must not require worker-only gevent."""

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Gateway import 경계 테스트를 관련 소스 변경에도 실행하세요

apps/workflow_engine/workflow/nodes/llm/__init__.py나 새 RAG 어댑터만 수정하는 후속 PR은 classify_paths()에서 workflow_tests만 선택하므로, apps/gateway/tests에 둔 이 회귀 테스트는 실행되지 않습니다. 그 결과 LLMNode가 다시 eager import되어 Gateway에 worker 전용 gevent 의존성이 유입되는 변경도 선택 CI를 통과할 수 있으므로, 이 테스트를 Workflow Engine 선택 대상에 두거나 해당 import-boundary 소스가 바뀌면 Gateway 테스트도 선택해야 합니다.

AGENTS.md reference: AGENTS.md:L206-L207

Useful? React with 👍 / 👎.

@HyungminYoon1

Copy link
Copy Markdown
Contributor Author

@codex review

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. 👍

Reviewed commit: 2b485b9c37

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@HyungminYoon1

Copy link
Copy Markdown
Contributor Author

/recheck-ci-control

@HyungminYoon1
HyungminYoon1 merged commit 3add789 into dev Jul 31, 2026
18 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Performance][Knowledge/RAG] 사전 계산 query embedding 기반 KB fan-out 병렬화 및 지연 계측

2 participants