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>
4.1 KiB
id, kind, slug, title, topic, topicName, project, status, studio
| id | kind | slug | title | topic | topicName | project | status | studio |
|---|---|---|---|---|---|---|---|---|
| 06788903-3dfa-4f70-b159-f1224384fd0b | REFERENCE | keyset-pagination-design | Keyset Pagination 설계 기준 | jpa-feed-query-performance | JPA 피드 조회 성능 | Liner N + 1문제 | 게시 전 | https://hyeonworks.com/studio/documents/06788903-3dfa-4f70-b159-f1224384fd0b/edit |
Keyset Pagination 설계 기준
OFFSET은 건너뛸 행까지 만든 뒤 버린다. keyset은 이전 페이지의 마지막 행을 커서로 삼아 그 지점 이후만 읽는다. 다만 커서와 같은 순서의 정렬키 인덱스가 있어야 이 이점이 생긴다.
관계
- Visibility OR이 Keyset Index를 깨뜨린 문제 이 기준의 전제가 깨지는 조건을 확인한 기록이다.
- Feed Pagination은 Keyset을 사용한다 이 기준에서 나온 결정이다.
- PostgreSQL Query Plan 측정 기준 깊이별 비용을 실행계획으로 확인하는 기준이다.
목적
무한 스크롤에서는 뒤쪽 페이지일수록 OFFSET이 커진다. 정렬키 인덱스가 있어도 건너뛸 튜플을 훑어야 하고, 깊으면 전량 스캔과 정렬로 떨어진다.
페이지 깊이와 무관하게 읽는 행수를 일정하게 유지하려면 커서 방식이 필요하다.
규칙
1. 커서에 정렬키를 모두 담는다
정렬이 여러 컬럼이면 커서도 같은 컬럼을 모두 가진다. 앞 컬럼만 커서로 쓰면 값이 같은 행이 있을 때 경계에서 빠지거나 중복된다.
2. tie-break 컬럼을 정렬과 커서에 넣는다
정렬키에 중복이 있을 수 있으면 유일한 컬럼을 마지막 정렬키로 더한다. 커서에도 같이 담는다.
3. 정렬키, 커서, 인덱스의 컬럼과 방향을 일치시킨다
셋 중 하나라도 어긋나면 인덱스가 순서를 주지 못해 Sort가 다시 생긴다. 방향까지 같아야 한다.
4. 정렬키 전용 인덱스를 확인한다
선두 컬럼이 다른 인덱스는 이 쿼리에 쓰이지 않는다. 필터가 없는 정렬 쿼리라면 정렬키만으로 된 인덱스가 필요하다.
인덱스가 없으면 keyset도 전량을 스캔한다. keyset 문법이 아니라 인덱스가 비용을 줄인다.
5. 깊이별로 훑은 행을 측정한다
OFFSET은 offset에 페이지 크기를 더한 만큼 훑는다. keyset은 페이지 크기만큼 훑는다. 훑은 행은 Limit 하위의 actual rows로 읽는다.
한 페이지만 재면 차이가 보이지 않는다. 깊이를 바꿔 가며 곡선으로 확인한다.
6. 결과가 OFFSET과 같은지 검증한다
커서로 넘긴 페이지가 같은 순서의 같은 행을 반환하는지 대조한다. 페이지 크기, 순서, 식별자를 모두 확인한다.
7. 필터를 얹으면 전제가 깨질 수 있다
선택 조건이 여러 분기로 갈리면 플래너가 분기별로 스캔한 뒤 합치면서 정렬 순서를 잃는다. 이때 Sort가 다시 나타난다.
필터가 있는 keyset은 필터를 포함한 인덱스 설계나 쿼리 분해가 함께 필요하다.
8. 정렬키에 null이 있을 수 있는지 먼저 정한다
정렬키가 nullable이면 null의 정렬 위치와 커서 표현을 정의해야 한다. 이 판단을 미루면 커서 비교식이 경계에서 어긋난다.
적용 조건
- 무한 스크롤이나 깊은 페이지를 지원할 때
- 정렬 순서가 고정돼 있고 인덱스를 만들 수 있을 때
- 전체 페이지 수가 필요하지 않을 때
예외
- 임의 페이지 점프가 필요하면 커서만으로는 부족하다. OFFSET을 함께 두거나 다른 탐색을 설계한다.
- 전체 건수를 화면에 표시해야 하면 count를 별도로 다룬다. 커서 결과에는 전체 건수가 없다.
- 정렬 기준이 자주 바뀌면 기준마다 인덱스가 필요하다. 인덱스 수와 쓰기 비용을 함께 본다.
예시
- OFFSET 훑은 행 : offset + 페이지 크기
- keyset 훑은 행 : 페이지 크기 (깊이 무관)
- 커서 : (정렬키, tie-break) 조합
- 전제 인덱스 : 정렬키와 같은 컬럼·같은 방향
- 인덱스 없는 keyset : 전량 스캔, 이점 없음
- 필터 추가 : 분기가 갈리면 Sort 재등장