Files
document-haness/docs/n+1liner/tech-log-studio/jpa-feed-query-performance/decision/decision-measure-plan-on-real-postgresql.md
T
DongHyeonkaandClaude Opus 5 b2963105a8 docs(keycloak-session-store): import the session-storage lab as a new project
The keycloak project ended with four open questions that design could not
settle. A two-VM lab was built to answer them by measurement, and this is
that material: 26 experiments, 125 raw command outputs, 22 browser captures.

Follows the import procedure in README.md.

  source/     the originating repository verbatim — 78 documents, 28 SVGs,
              8 manifests, plus .source-revision recording the commit
  final/      the SSOT
    document.md   729 lines written from the 29 experiment documents, not
                  concatenated: what was predicted, what was measured, and
                  where the measurement itself was wrong
    evidence/raw    125 outputs, flattened to <experiment>__<file> because
                    the originals collided (01-baseline.txt appeared three
                    times) and the audit only globs the top level
    evidence/meta   one per raw file; command and exitCode are null and the
                    README says why rather than inventing them
    evidence/browser  22 captures
    assets/       three diagrams through techviz
    .techviz/     their VizSpecs

A separate project rather than an addition to keycloak: the B-layer answers
that project's four questions, but the A, C and D layers are about cluster
failure, SSO and operations, and one document.md should hold one subject.
The four question records there can point here through 관계.

Recorded rather than papered over: only three of the 28 diagrams were
remade. The repository forbids hand-drawn SVG and forbids titles inside the
canvas; all 28 originals carry both, so converting them is redrawing, not
reformatting. They stay in source/ and the gap is written into the document.

verify-pipeline.py passes. audit-records.py reports no issues.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-04 22:51:59 +09:00

3.0 KiB

id, kind, slug, title, topic, topicName, project, status, studio, decisionStatus
id kind slug title topic topicName project status studio decisionStatus
ae6c9bea-d3a3-46e1-bbd4-8d580d336394 PROJECT_DECISION measure-plan-on-real-postgresql Query Plan은 실제 PostgreSQL에서 측정한다 jpa-feed-query-performance JPA 피드 조회 성능 Liner N + 1문제 게시 전 https://hyeonworks.com/studio/documents/ae6c9bea-d3a3-46e1-bbd4-8d580d336394/edit PROPOSED

Query Plan은 실제 PostgreSQL에서 측정한다

조회 성능 측정은 인메모리 대체 DB가 아니라 운영과 같은 PostgreSQL에서 실행한다. 실행계획과 인덱스 동작이 측정 대상이므로 DB는 대체재가 아니라 측정 대상의 일부다.

근거

  • PostgreSQL Query Plan 측정 기준 이 결정을 규칙으로 편 기준이다.
  • Fetch 타입이 아니라 조회 방식이 만든 ToOne N+1 실제 엔진에서 실행계획과 통계 차이를 관측한 기록이다.
  • Visibility OR이 Keyset Index를 깨뜨린 문제 부분 인덱스와 정렬 인덱스 기능에 기댄 측정 기록이다.

결정문

퍼시스턴스 조회 측정은 Testcontainers로 띄운 실제 PostgreSQL에서 수행한다. 인메모리 대체 DB로 실행계획이나 인덱스 동작을 판단하지 않는다.

스키마는 운영 마이그레이션을 그대로 적용하고 엔티티와의 불일치를 조기에 잡는다.

판단 이유

비용 기반 옵티마이저는 가능한 계획의 비용을 추정해 고른다. 그 추정값도, 고를 수 있는 선택지도 엔진마다 다르다. 비용 상수, 수집하는 통계, 저장 구조와 가시성 처리, 사용할 수 있는 인덱스 종류가 모두 갈린다.

이 프로젝트의 측정은 이 축들에 직접 걸린다. 추정 행수와 실제 행수가 500배 차이 난 관측은 통계 수집 방식에 달렸고, 순차 스캔과 인덱스 스캔의 판정은 비용 모델과 선택도 추정의 산물이며, 가시성 조건과 정렬 페이징은 부분 인덱스와 정렬 인덱스 기능에 기댄다.

다른 엔진에서 재면 스캔 방식 선택이 뒤집히고, 한쪽에만 있는 접근 경로가 통째로 사라지며, 그 엔진 특유의 동작이 재현되지 않는다. 세 지점에서 체계적으로 틀린 결론에 이른다.

측정 대상이 계획과 인덱스 동작인 이상 DB를 바꾸면 측정 자체가 달라진다.

영향

  • 측정 실행에 컨테이너 런타임이 필요하다. Docker가 없는 환경에서는 이 테스트가 비활성화된다.
  • 인메모리 DB보다 기동과 실행이 느리다. 컨테이너를 클래스당 하나로 공유해 비용을 줄였다.
  • 재현성을 위해 이미지 태그보다 patch 버전이나 digest를 고정하는 편이 낫다. 같은 태그가 시점에 따라 다른 patch를 가리킬 수 있다.
  • 스키마 검증만으로 모든 드리프트를 막지 못한다. 인덱스 구성, 부분 인덱스 조건, check 제약, 외래키 정책은 따로 확인해야 한다.
  • 측정값은 warm cache 상태의 로컬 값이다. 운영 지연으로 옮겨 읽을 수 없다.