Files
document-haness/docs/clean-architecture-backend-template/final/evidence/raw/analysis-finding-a06-f015.txt
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

188 lines
12 KiB
Plaintext

# 매니페스트 쪽 TTL
MongoIndexManifest.java:28 String partialFilterExpression,
MongoIndexManifest.java:29 String collationProfile,
MongoIndexManifest.java:30 Duration expireAfter,
MongoIndexManifest.java:31 String wildcardProjection,
MongoIndexManifest.java:32 boolean shardKeySupport,
MongoIndexManifest.java:50 }
MongoIndexManifest.java:51 if (keys.isEmpty()) {
MongoIndexManifest.java:52 throw new IllegalArgumentException("index '" + name + "' declares no keys");
MongoIndexManifest.java:53 }
MongoIndexManifest.java:54 if (expireAfter != null && expireAfter.isNegative()) {
MongoIndexManifest.java:55 throw new IllegalArgumentException("index '" + name + "' declares a negative TTL");
MongoIndexManifest.java:56 }
MongoIndexManifest.java:57 if (metadataOwnership == MongoMetadataOwnership.APPLICATION && expectedUsage.isEmpty()) {
MongoIndexManifest.java:58 throw new IllegalArgumentException(
MongoIndexManifest.java:66 /** Starts an index declaration. */
MongoIndexManifest.java:67 public static Builder named(String name) {
MongoIndexManifest.java:68 return new Builder(name);
MongoIndexManifest.java:69 }
MongoIndexManifest.java:70
MongoIndexManifest.java:71 /** The TTL, when this is a TTL index. */
MongoIndexManifest.java:72 public Optional<Duration> ttl() {
MongoIndexManifest.java:73 return Optional.ofNullable(expireAfter);
MongoIndexManifest.java:74 }
MongoIndexManifest.java:75
MongoIndexManifest.java:76 /** True when this index is a TTL index. */
MongoIndexManifest.java:77 public boolean isTtlIndex() {
MongoIndexManifest.java:78 return expireAfter != null;
MongoIndexManifest.java:79 }
MongoIndexManifest.java:80
MongoIndexManifest.java:186 }
MongoIndexManifest.java:187
MongoIndexManifest.java:188 /** Declares this a TTL index with the given retention. */
MongoIndexManifest.java:189 public Builder expireAfter(Duration retention) {
MongoIndexManifest.java:190 this.expireAfter = Objects.requireNonNull(retention, "retention");
MongoIndexManifest.java:191 return this;
MongoIndexManifest.java:192 }
MongoIndexManifest.java:193
MongoIndexManifest.java:194 /** Declares a wildcard projection, as canonical extended JSON. */
expireAfter 라는 이름이 그 파일에 나오는 줄 : 8 개
그중 값을 거절하는 줄 : 1 개
# 규칙을 가진 쪽
MongoTtlPolicyValidator.java:7 /**
MongoTtlPolicyValidator.java:8 * Rejects the two ways a TTL index is misused (design §21.1, D-13).
MongoTtlPolicyValidator.java:9 *
MongoTtlPolicyValidator.java:10 * <p>The first is treating it as a scheduler: the TTL monitor's sweep interval is unspecified and
MongoTtlPolicyValidator.java:11 * load-dependent, so "delete this at 09:00" is not something it promises. The second is relying on
MongoTtlPolicyValidator.java:12 * it for access control: a document that still exists is still readable, so if expiry must hide
MongoTtlPolicyValidator.java:13 * data the read has to say so.
MongoTtlPolicyValidator.java:14 */
MongoTtlPolicyValidator.java:15 public final class MongoTtlPolicyValidator {
MongoTtlPolicyValidator.java:16
MongoTtlPolicyValidator.java:17 /**
MongoTtlPolicyValidator.java:18 * A TTL reduction below this threshold would expire a large population in one sweep, turning a
MongoTtlPolicyValidator.java:19 * configuration change into an unplanned bulk delete.
MongoTtlPolicyValidator.java:20 */
MongoTtlPolicyValidator.java:21 public static final Duration MINIMUM_SAFE_RETENTION = Duration.ofMinutes(1);
MongoTtlPolicyValidator.java:22
MongoTtlPolicyValidator.java:23 /**
MongoTtlPolicyValidator.java:24 * Validates one TTL policy.
MongoTtlPolicyValidator.java:25 *
MongoTtlPolicyValidator.java:26 * @throws MongoOperationRejectedException when the policy claims a guarantee TTL does not provide
MongoTtlPolicyValidator.java:27 */
MongoTtlPolicyValidator.java:28 public void validate(MongoTtlPolicy policy) {
MongoTtlPolicyValidator.java:29 Objects.requireNonNull(policy, "policy");
MongoTtlPolicyValidator.java:30 if (policy.claimsExactExpiry()) {
MongoTtlPolicyValidator.java:31 throw MongoOperationRejectedException.of(
MongoTtlPolicyValidator.java:32 "ttl.policy",
MongoTtlPolicyValidator.java:33 "the TTL policy on '"
MongoTtlPolicyValidator.java:34 + policy.field()
MongoTtlPolicyValidator.java:35 + "' claims an exact business transition; MongoDB's TTL monitor sweeps on an "
MongoTtlPolicyValidator.java:36 + "unspecified interval, so an exact transition needs its own scheduler and the TTL "
MongoTtlPolicyValidator.java:37 + "index stays physical cleanup");
MongoTtlPolicyValidator.java:38 }
MongoTtlPolicyValidator.java:39 if (!policy.queryChecksLogicalExpiry()) {
MongoTtlPolicyValidator.java:40 throw MongoOperationRejectedException.of(
MongoTtlPolicyValidator.java:41 "ttl.policy",
MongoTtlPolicyValidator.java:42 "the TTL policy on '"
MongoTtlPolicyValidator.java:43 + policy.field()
MongoTtlPolicyValidator.java:44 + "' does not declare a query-time expiry check; an expired document remains readable "
MongoTtlPolicyValidator.java:45 + "until the monitor deletes it, so reads must filter on "
MongoTtlPolicyValidator.java:46 + policy.field()
MongoTtlPolicyValidator.java:47 + " > applicationNow");
MongoTtlPolicyValidator.java:48 }
MongoTtlPolicyValidator.java:49 if (!policy.retention().isZero() && policy.retention().compareTo(MINIMUM_SAFE_RETENTION) < 0) {
MongoTtlPolicyValidator.java:50 throw MongoOperationRejectedException.of(
MongoTtlPolicyValidator.java:51 "ttl.policy",
MongoTtlPolicyValidator.java:52 "a TTL retention of "
MongoTtlPolicyValidator.java:53 + policy.retention()
MongoTtlPolicyValidator.java:54 + " is below the safe minimum of "
MongoTtlPolicyValidator.java:55 + MINIMUM_SAFE_RETENTION
MongoTtlPolicyValidator.java:56 + "; it would expire the existing population in a single sweep");
MongoTtlPolicyValidator.java:57 }
MongoTtlPolicyValidator.java:58 }
MongoTtlPolicyValidator.java:59
MongoTtlPolicyValidator.java:60 /**
MongoTtlPolicyValidator.java:61 * Validates a TTL index descriptor, including the BSON type of its expiry field.
MongoTtlPolicyValidator.java:62 *
MongoTtlPolicyValidator.java:63 * @throws MongoOperationRejectedException when the field type cannot be expired
MongoTtlPolicyValidator.java:64 */
MongoTtlPolicyValidator.java:65 public void validate(MongoTtlIndexDescriptor descriptor) {
MongoTtlPolicyValidator.java:66 Objects.requireNonNull(descriptor, "descriptor");
MongoTtlPolicyValidator.java:67 validate(descriptor.policy());
MongoTtlPolicyValidator.java:68 if (!descriptor.expiryFieldTypeIsSupported()) {
MongoTtlPolicyValidator.java:69 throw MongoOperationRejectedException.of(
MongoTtlPolicyValidator.java:70 "ttl.policy",
MongoTtlPolicyValidator.java:71 "TTL index '"
MongoTtlPolicyValidator.java:72 + descriptor.indexName()
MongoTtlPolicyValidator.java:73 + "' expires on a field stored as "
MongoTtlPolicyValidator.java:74 + descriptor.expiryFieldBsonType()
MongoTtlPolicyValidator.java:75 + "; MongoDB only expires BSON date fields and ignores the rest silently");
MongoTtlPolicyValidator.java:76 }
MongoTtlPolicyValidator.java:77 }
MongoTtlPolicyValidator.java:78
MongoTtlPolicyValidator.java:79 /**
MongoTtlPolicyValidator.java:80 * Validates the read-side predicate that gives expiry its actual guarantee.
MongoTtlPolicyValidator.java:81 *
MongoTtlPolicyValidator.java:82 * @throws MongoOperationRejectedException when reads do not filter on logical expiry
MongoTtlPolicyValidator.java:83 */
MongoTtlPolicyValidator.java:84 public void validate(MongoExpirationAccessPolicy accessPolicy) {
MongoTtlPolicyValidator.java:85 Objects.requireNonNull(accessPolicy, "accessPolicy");
MongoTtlPolicyValidator.java:86 if (!accessPolicy.applied()) {
MongoTtlPolicyValidator.java:87 throw MongoOperationRejectedException.of(
MongoTtlPolicyValidator.java:88 "ttl.access",
MongoTtlPolicyValidator.java:89 "reads of this collection do not apply '"
MongoTtlPolicyValidator.java:90 + accessPolicy.describePredicate()
MongoTtlPolicyValidator.java:91 + "', so an expired document stays visible until the TTL monitor happens to delete it");
MongoTtlPolicyValidator.java:92 }
MongoTtlPolicyValidator.java:93 }
MongoTtlPolicyValidator.java:94 }
# 그 정책 타입이 요구하는 것
MongoTtlPolicy.java:1 package dev.caskeleton.adapter.outbound.mongo.schema.ttl;
MongoTtlPolicy.java:2
MongoTtlPolicy.java:3 import java.time.Duration;
MongoTtlPolicy.java:4 import java.util.Objects;
MongoTtlPolicy.java:5
MongoTtlPolicy.java:6 /**
MongoTtlPolicy.java:7 * What a TTL index is being used for (design §21.1, D-13).
MongoTtlPolicy.java:8 *
MongoTtlPolicy.java:9 * <p>MongoDB's TTL monitor runs roughly once a minute and deletes in batches, so an expired
MongoTtlPolicy.java:10 * document stays readable for an unspecified interval after its expiry — longer under load, longer
MongoTtlPolicy.java:11 * still on a busy secondary. That is fine for reclaiming space and wrong for anything that must
MongoTtlPolicy.java:12 * stop being visible at a particular moment.
MongoTtlPolicy.java:13 *
MongoTtlPolicy.java:14 * <p>Both booleans are therefore part of the declaration. {@code physicalCleanupOnly} says the TTL
MongoTtlPolicy.java:15 * is only reclaiming space; {@code queryChecksLogicalExpiry} says the application filters on {@code
MongoTtlPolicy.java:16 * expiresAt > now} so visibility does not depend on the monitor's timing.
MongoTtlPolicy.java:17 */
MongoTtlPolicy.java:18 public record MongoTtlPolicy(
MongoTtlPolicy.java:19 String field,
MongoTtlPolicy.java:20 Duration retention,
MongoTtlPolicy.java:21 boolean physicalCleanupOnly,
MongoTtlPolicy.java:22 boolean queryChecksLogicalExpiry) {
MongoTtlPolicy.java:23
MongoTtlPolicy.java:24 public MongoTtlPolicy {
MongoTtlPolicy.java:25 Objects.requireNonNull(field, "field");
MongoTtlPolicy.java:26 Objects.requireNonNull(retention, "retention");
MongoTtlPolicy.java:27 if (field.isBlank()) {
MongoTtlPolicy.java:28 throw new IllegalArgumentException("a TTL policy needs an expiry field");
MongoTtlPolicy.java:29 }
MongoTtlPolicy.java:30 if (retention.isNegative()) {
MongoTtlPolicy.java:31 throw new IllegalArgumentException("a TTL retention must not be negative");
MongoTtlPolicy.java:32 }
MongoTtlPolicy.java:33 }
MongoTtlPolicy.java:34
MongoTtlPolicy.java:35 /** The supported shape: physical cleanup, with the application filtering on logical expiry. */
MongoTtlPolicy.java:36 public static MongoTtlPolicy physicalCleanup(String field, Duration retention) {
MongoTtlPolicy.java:37 return new MongoTtlPolicy(field, retention, true, true);
MongoTtlPolicy.java:38 }
MongoTtlPolicy.java:39
MongoTtlPolicy.java:40 /**
# 두 표현 사이에 참조가 있는가
schema/ttl 밖 main 에서 그 네 타입을 언급하는 줄 : 0 개
[대조] schema/ttl 안에서는 : 22 개
MongoIndexManifest 가 그 네 타입을 언급하는 줄 : 0 개
isTtlIndex() 나 ttl() 을 부르는 줄 : 0 개
[대조] 그 두 이름이 나오는 줄 (선언 포함) : 2 개
# 두 표현을 조립하는 main 코드가 있는가
MongoIndexManifest 를 만드는 main 줄 : 1 개
MongoIndexManifest.java:226 return new MongoIndexManifest(
[대조] 시험에서 만드는 줄 : 7 개
MongoTtlPolicyValidator 를 만드는 main 줄 : 0 개
[대조] 시험에서 검증기를 만드는 줄 : 1 개