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>
62 lines
4.4 KiB
Plaintext
62 lines
4.4 KiB
Plaintext
# 문서가 무엇을 막겠다고 하는가 — 두 가지다
|
|
7:/**
|
|
8: * The rules a dynamic {@link Specification} query must satisfy before it runs (design §23.2).
|
|
9: *
|
|
10: * <p>A specification with no predicate is a full table scan wearing a builder's clothing. It is
|
|
11: * usually the result of every optional filter being absent — a search screen submitted empty — and
|
|
12: * it looks harmless in code review because no single line is wrong. Requiring either a predicate or
|
|
13: * an explicit "yes, scan everything" token makes that case a decision instead of an accident.
|
|
14: *
|
|
15: * <p>A page bound is required for the same reason: an unbounded collection query hydrates whatever
|
|
16: * the table happens to hold today.
|
|
17: */
|
|
# 실제 검사
|
|
31: public static void requireBounded(
|
|
32: Specification<?> specification, Pageable pageable, String allowUnboundedToken) {
|
|
33: Objects.requireNonNull(pageable, "pageable");
|
|
34: if (pageable.isUnpaged()) {
|
|
35: throw new IllegalArgumentException(
|
|
36: "a specification query requires a bounded page; unpaged reads whatever the table holds");
|
|
37: }
|
|
38: if (specification == null && !ALLOW_UNBOUNDED_TOKEN.equals(allowUnboundedToken)) {
|
|
39: throw new IllegalArgumentException(
|
|
40: "a specification query requires a bounded predicate, or the explicit '"
|
|
41: + ALLOW_UNBOUNDED_TOKEN
|
|
42: + "' token");
|
|
43: }
|
|
44: }
|
|
45:
|
|
46: /** Fails when the specification has no predicate at all. */
|
|
47: public static void requirePredicate(Specification<?> specification) {
|
|
48: if (specification == null) {
|
|
49: throw new IllegalArgumentException("a specification query requires a bounded predicate");
|
|
50: }
|
|
51: }
|
|
|
|
# 이 타입을 언급하는 곳 (레포 전체, 언어 무관)
|
|
docs/superpowers/plans/2026-08-11-jpa-persistence-platform-implementation-plan.md:1848:- Create: `modules/jpa/jpa-spring-data/src/main/java/io/backend
|
|
docs/superpowers/plans/2026-08-11-jpa-persistence-platform-implementation-plan.md:1922:git add 'modules/jpa/jpa-querydsl/src/main/java/io/backend/skel
|
|
docs/architecture/jpa-api-surface.txt:321:dev.caskeleton.adapter.outbound.persistence.springdata.SpecificationPolicy
|
|
docs/study/postgresql-jpa-walkthrough.ko.md:874:`SpecificationPolicy`:
|
|
docs/study/postgresql-jpa-walkthrough.ko.md:3249:SpecificationPolicy
|
|
docs/study/postgresql-jpa-walkthrough.ko.md:3257:grep -rn "UpsertExecutor\|WorkClaimExecutor\|RangeQuerySupport\|SpecificationPolicy\|PgRangeJdbcType"
|
|
src/adapter/outbound/persistence-jpa/src/main/java/dev/caskeleton/adapter/outbound/persistence/springdata/SpecificationPolicy.java:18:public final cla
|
|
src/adapter/outbound/persistence-jpa/src/main/java/dev/caskeleton/adapter/outbound/persistence/springdata/SpecificationPolicy.java:23: private Specif
|
|
# 두 검사 메서드를 부르는 코드 (정적 임포트 포함, 레포 전체): 0
|
|
|
|
# 같은 리프의 형제 정책. 같은 토큰을 쓰고 같은 널 비교를 한다.
|
|
adapter/outbound/persistence-jpa/src/main/java/dev/caskeleton/adapter/outbound/persistence/springdata/SpecificationPolicy.java:21: public static final String ALLOW_UNBOUNDED_TOKEN = "allow-unbounded-scan";
|
|
adapter/outbound/persistence-jpa/src/main/java/dev/caskeleton/adapter/outbound/persistence/querydsl/PredicatePolicy.java:20: public static final String ALLOW_UNBOUNDED_TOKEN = "allow-unbounded-scan";
|
|
29: public static void requireBounded(Predicate predicate, QueryPage page) {
|
|
30: Objects.requireNonNull(page, "page");
|
|
31: if (predicate == null && !page.allowsUnboundedScan()) {
|
|
32: throw new IllegalArgumentException(
|
|
33: "a collection query requires a bounded predicate, or the explicit '"
|
|
34: + ALLOW_UNBOUNDED_TOKEN
|
|
35: + "' token");
|
|
36: }
|
|
# 형제 쪽 호출처와 그것을 부르는 테스트
|
|
adapter/outbound/persistence-jpa/src/test/java/dev/caskeleton/adapter/outbound/persistence/querydsl/QuerydslJpaSupportTest.java:17: assertThatThrownBy(() -> PredicatePolicy.requireBounded(null, QueryPage.of(0, 100)))
|
|
adapter/outbound/persistence-jpa/src/test/java/dev/caskeleton/adapter/outbound/persistence/querydsl/QuerydslJpaSupportTest.java:25: assertThatCode(() -> PredicatePolicy.requireBounded(null, QueryPage.unboundedScan(0, 100)))
|
|
adapter/outbound/persistence-jpa/src/main/java/dev/caskeleton/adapter/outbound/persistence/querydsl/QuerydslJpaSupport.java:42: PredicatePolicy.requireBounded(predicate, page);
|