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>
216 lines
15 KiB
Plaintext
216 lines
15 KiB
Plaintext
# 샤딩 게이트웨이의 네 작업
|
|
MongoShardingAdminGateway.java:33 /**
|
|
MongoShardingAdminGateway.java:34 * Enables sharding on a collection.
|
|
MongoShardingAdminGateway.java:35 *
|
|
MongoShardingAdminGateway.java:36 * @throws MongoOperationRejectedException when the key was not approved or its index is missing
|
|
MongoShardingAdminGateway.java:37 */
|
|
MongoShardingAdminGateway.java:38 public void shardCollection(
|
|
MongoShardingAdminGateway.java:39 String collection,
|
|
MongoShardingAdminGateway.java:40 ShardKeyDescriptor shardKey,
|
|
MongoShardingAdminGateway.java:41 ShardKeyReadinessReport readiness,
|
|
MongoShardingAdminGateway.java:42 List<String> supportingIndexFields,
|
|
MongoShardingAdminGateway.java:43 String operator,
|
|
MongoShardingAdminGateway.java:44 String reason,
|
|
MongoShardingAdminGateway.java:45 Supplier<Void> apply) {
|
|
MongoShardingAdminGateway.java:46 Objects.requireNonNull(collection, "collection");
|
|
MongoShardingAdminGateway.java:47 Objects.requireNonNull(shardKey, "shardKey");
|
|
MongoShardingAdminGateway.java:48 Objects.requireNonNull(readiness, "readiness");
|
|
MongoShardingAdminGateway.java:49 if (!readiness.approved()) {
|
|
MongoShardingAdminGateway.java:50 throw MongoOperationRejectedException.of(
|
|
MongoShardingAdminGateway.java:51 "sharding.shard-collection",
|
|
MongoShardingAdminGateway.java:52 "the shard key for '" + collection + "' was not approved: " + readiness.reasons());
|
|
MongoShardingAdminGateway.java:53 }
|
|
MongoShardingAdminGateway.java:54 if (!supportingIndexFields
|
|
MongoShardingAdminGateway.java:55 .subList(0, Math.min(shardKey.fields().size(), supportingIndexFields.size()))
|
|
MongoShardingAdminGateway.java:56 .equals(shardKey.fields())) {
|
|
MongoShardingAdminGateway.java:57 throw MongoOperationRejectedException.of(
|
|
MongoShardingAdminGateway.java:58 "sharding.shard-collection",
|
|
MongoShardingAdminGateway.java:59 "sharding '"
|
|
MongoShardingAdminGateway.java:60 + collection
|
|
MongoShardingAdminGateway.java:61 + "' needs a supporting index prefixed by the shard key "
|
|
MongoShardingAdminGateway.java:62 + shardKey.fields());
|
|
MongoShardingAdminGateway.java:63 }
|
|
MongoShardingAdminGateway.java:64 adminGateway.execute(MongoAdminOperation.SHARD_COLLECTION, collection, operator, reason, apply);
|
|
MongoShardingAdminGateway.java:65 }
|
|
MongoShardingAdminGateway.java:66
|
|
MongoShardingAdminGateway.java:67 /** Adds a field to an existing shard key. Requires the same evidence as a reshard. */
|
|
MongoShardingAdminGateway.java:68 public void refineShardKey(
|
|
MongoShardingAdminGateway.java:69 String collection,
|
|
MongoShardingAdminGateway.java:70 ReshardApproval approval,
|
|
MongoShardingAdminGateway.java:71 String operator,
|
|
MongoShardingAdminGateway.java:72 String reason,
|
|
MongoShardingAdminGateway.java:73 Supplier<Void> apply) {
|
|
MongoShardingAdminGateway.java:74 Objects.requireNonNull(approval, "approval").require();
|
|
MongoShardingAdminGateway.java:75 adminGateway.execute(MongoAdminOperation.REFINE_SHARD_KEY, collection, operator, reason, apply);
|
|
MongoShardingAdminGateway.java:76 }
|
|
MongoShardingAdminGateway.java:77
|
|
MongoShardingAdminGateway.java:78 /** Changes a collection's shard key, rewriting the whole collection. */
|
|
MongoShardingAdminGateway.java:79 public void reshardCollection(
|
|
MongoShardingAdminGateway.java:80 String collection,
|
|
MongoShardingAdminGateway.java:81 ReshardApproval approval,
|
|
MongoShardingAdminGateway.java:82 String operator,
|
|
MongoShardingAdminGateway.java:83 String reason,
|
|
MongoShardingAdminGateway.java:84 Supplier<Void> apply) {
|
|
MongoShardingAdminGateway.java:85 Objects.requireNonNull(approval, "approval").require();
|
|
MongoShardingAdminGateway.java:86 adminGateway.execute(
|
|
MongoShardingAdminGateway.java:87 MongoAdminOperation.RESHARD_COLLECTION, collection, operator, reason, apply);
|
|
MongoShardingAdminGateway.java:88 }
|
|
MongoShardingAdminGateway.java:89
|
|
MongoShardingAdminGateway.java:90 /** Starts or stops the balancer. */
|
|
MongoShardingAdminGateway.java:91 public void controlBalancer(String scope, String operator, String reason, Supplier<Void> apply) {
|
|
MongoShardingAdminGateway.java:92 adminGateway.execute(MongoAdminOperation.BALANCER_CONTROL, scope, operator, reason, apply);
|
|
MongoShardingAdminGateway.java:93 }
|
|
네 메서드가 부르는 것 : 4 개
|
|
|
|
# 그 다섯 인자 오버로드가 하는 일
|
|
MongoAdminGateway.java:40 this.guard = Objects.requireNonNull(guard, "guard");
|
|
MongoAdminGateway.java:41 this.authorization = Objects.requireNonNull(authorization, "authorization");
|
|
MongoAdminGateway.java:42 this.auditSink = Objects.requireNonNull(auditSink, "auditSink");
|
|
MongoAdminGateway.java:43 this.clock = Objects.requireNonNull(clock, "clock");
|
|
MongoAdminGateway.java:44 guard.validate();
|
|
MongoAdminGateway.java:45 }
|
|
MongoAdminGateway.java:46
|
|
MongoAdminGateway.java:47 /**
|
|
MongoAdminGateway.java:48 * Runs one administrative operation.
|
|
MongoAdminGateway.java:49 *
|
|
MongoAdminGateway.java:50 * @param operation what is being done
|
|
MongoAdminGateway.java:51 * @param target the collection, index or database it is being done to
|
|
MongoAdminGateway.java:52 * @param operator who is running it
|
|
MongoAdminGateway.java:53 * @param reason why
|
|
MongoAdminGateway.java:54 * @param body the work
|
|
MongoAdminGateway.java:55 * @throws MongoOperationRejectedException when the operation is not authorized
|
|
MongoAdminGateway.java:56 */
|
|
MongoAdminGateway.java:57 public <T> T execute(
|
|
MongoAdminGateway.java:58 MongoAdminOperation operation,
|
|
MongoAdminGateway.java:59 String target,
|
|
MongoAdminGateway.java:60 String operator,
|
|
MongoAdminGateway.java:61 String reason,
|
|
MongoAdminGateway.java:62 Supplier<T> body) {
|
|
MongoAdminGateway.java:63 return execute(
|
|
MongoAdminGateway.java:64 MongoAdminCommand.routine(
|
|
MongoAdminGateway.java:65 operation, target, operator, reason, clock.instant().plus(COMMAND_VALIDITY)),
|
|
MongoAdminGateway.java:66 null,
|
|
MongoAdminGateway.java:67 body);
|
|
MongoAdminGateway.java:68 }
|
|
|
|
# 실행 경로가 승인을 요구하는 자리
|
|
MongoAdminGateway.java:69
|
|
MongoAdminGateway.java:70 /**
|
|
MongoAdminGateway.java:71 * Runs one administrative command under the audit state machine.
|
|
MongoAdminGateway.java:72 *
|
|
MongoAdminGateway.java:73 * <p>Three things changed from the single-record version. The intent is recorded before the
|
|
MongoAdminGateway.java:74 * command runs and claims nothing; a terminal record follows, saying what actually happened; and
|
|
MongoAdminGateway.java:75 * an audit sink that fails stops the command, because an unauditable administrative operation is
|
|
MongoAdminGateway.java:76 * one nobody can reconstruct afterwards — which for a drop or a reshard is the whole point of
|
|
MongoAdminGateway.java:77 * having the plane at all.
|
|
MongoAdminGateway.java:78 *
|
|
MongoAdminGateway.java:79 * @param approval required for a high-risk operation and bound to this exact command
|
|
MongoAdminGateway.java:80 * @throws MongoOperationRejectedException when authorization, approval, expiry or the audit sink
|
|
MongoAdminGateway.java:81 * refuses
|
|
MongoAdminGateway.java:82 */
|
|
MongoAdminGateway.java:83 public <T> T execute(MongoAdminCommand command, MongoAdminApproval approval, Supplier<T> body) {
|
|
MongoAdminGateway.java:84 Objects.requireNonNull(command, "command");
|
|
MongoAdminGateway.java:85 Objects.requireNonNull(body, "body");
|
|
MongoAdminGateway.java:86 Instant now = clock.instant();
|
|
MongoAdminGateway.java:87 authorization.require(command.operation());
|
|
MongoAdminGateway.java:88 if (command.expiredAt(now)) {
|
|
MongoAdminGateway.java:89 throw MongoOperationRejectedException.of(
|
|
MongoAdminGateway.java:90 "admin.command",
|
|
MongoAdminGateway.java:91 "this admin command expired before it ran; a stale command may describe a cluster that no"
|
|
MongoAdminGateway.java:92 + " longer looks like the one it was written for");
|
|
MongoAdminGateway.java:93 }
|
|
MongoAdminGateway.java:94 if (command.operation().highRisk()) {
|
|
MongoAdminGateway.java:95 if (approval == null) {
|
|
MongoAdminGateway.java:96 throw MongoOperationRejectedException.of(
|
|
MongoAdminGateway.java:97 "admin.approval",
|
|
MongoAdminGateway.java:98 "admin operation "
|
|
MongoAdminGateway.java:99 + command.operation()
|
|
MongoAdminGateway.java:100 + " destroys data or rewrites a collection; it runs under an approval bound to this"
|
|
MongoAdminGateway.java:101 + " exact command or not at all");
|
|
MongoAdminGateway.java:102 }
|
|
MongoAdminGateway.java:103 approval.require(command, now);
|
|
MongoAdminGateway.java:104 // Single use. Without this, one approval for one reshard authorizes every later reshard of
|
|
MongoAdminGateway.java:105 // the same shape, including the one nobody discussed.
|
|
MongoAdminGateway.java:106 if (!consumed.add(command.commandDigest())) {
|
|
MongoAdminGateway.java:107 throw MongoOperationRejectedException.of(
|
|
MongoAdminGateway.java:108 "admin.approval",
|
|
MongoAdminGateway.java:109 "this approval has already been used; a second execution needs a second decision");
|
|
MongoAdminGateway.java:110 }
|
|
MongoAdminGateway.java:111 }
|
|
|
|
# 다섯 인자 오버로드로 넘어가는 연산들의 위험 등급
|
|
MongoAdminOperation.java:3 /**
|
|
MongoAdminOperation.java:4 * The administrative operations the D4 plane can perform (design §5, §22).
|
|
MongoAdminOperation.java:5 *
|
|
MongoAdminOperation.java:6 * <p>A closed enum, and every constant is something no application runtime should be able to reach.
|
|
MongoAdminOperation.java:7 * Grouping them here makes the boundary a list somebody can review rather than an emergent property
|
|
MongoAdminOperation.java:8 * of which methods happen to exist.
|
|
MongoAdminOperation.java:9 */
|
|
MongoAdminOperation.java:10 public enum MongoAdminOperation {
|
|
MongoAdminOperation.java:13 CREATE_COLLECTION(false),
|
|
MongoAdminOperation.java:16 COLL_MOD(false),
|
|
MongoAdminOperation.java:37 SHARD_COLLECTION(true),
|
|
MongoAdminOperation.java:40 REFINE_SHARD_KEY(true),
|
|
MongoAdminOperation.java:43 RESHARD_COLLECTION(true),
|
|
MongoAdminOperation.java:46 BALANCER_CONTROL(false),
|
|
MongoAdminOperation.java:49 MANAGE_ENCRYPTION_KEY(true),
|
|
그 일곱 가운데 highRisk(true) 인 것 : 4 개
|
|
[대조] 열거형 전체에서 highRisk(true) 인 것 : 8 개
|
|
[대조] 열거형 상수 전체 : 15 개
|
|
|
|
# 세 인자 오버로드를 부르는 자리가 있는가
|
|
승인을 실제로 넘기는 main 호출 : 0 개
|
|
execute 를 부르는 main 자리 전부 :
|
|
MongoQueryableEncryptionCollectionManager.java:41 adminGateway.execute(
|
|
MongoQueryableEncryptionCollectionManager.java:63 adminGateway.execute(
|
|
MongoQueryableEncryptionCollectionManager.java:70 adminGateway.execute(MongoAdminOperation.COLL_MOD, collection, operator, reason, apply);
|
|
MongoShardingAdminGateway.java:64 adminGateway.execute(MongoAdminOperation.SHARD_COLLECTION, collection, operator, reason, apply);
|
|
MongoShardingAdminGateway.java:75 adminGateway.execute(MongoAdminOperation.REFINE_SHARD_KEY, collection, operator, reason, apply);
|
|
MongoShardingAdminGateway.java:86 adminGateway.execute(
|
|
MongoShardingAdminGateway.java:92 adminGateway.execute(MongoAdminOperation.BALANCER_CONTROL, scope, operator, reason, apply);
|
|
[대조] 시험에서 세 인자 execute 를 부르는 줄 : 10 개
|
|
MongoAdminApproval 을 만드는 main 줄 : 0 개
|
|
그 이름이 main 에 나오는 자리 전부 :
|
|
MongoAdminApproval.java:19 public record MongoAdminApproval(String commandDigest, String approver, Instant expiresAt) {
|
|
MongoAdminApproval.java:21 public MongoAdminApproval {
|
|
MongoAdminApproval.java:31 public static MongoAdminApproval of(
|
|
MongoAdminApproval.java:33 return new MongoAdminApproval(
|
|
MongoAdminGateway.java:83 public <T> T execute(MongoAdminCommand command, MongoAdminApproval approval, Supplier<T> body) {
|
|
[대조] 시험에서 그것을 만드는 줄 : 4 개
|
|
|
|
# main 에 등장하는 연산 상수와 그 위험 등급
|
|
BALANCER_CONTROL highRisk=false · main 등장 1 줄
|
|
COLL_MOD highRisk=false · main 등장 1 줄
|
|
CREATE_COLLECTION highRisk=false · main 등장 1 줄
|
|
MANAGE_ENCRYPTION_KEY highRisk=true · main 등장 1 줄
|
|
REFINE_SHARD_KEY highRisk=true · main 등장 1 줄
|
|
RESHARD_COLLECTION highRisk=true · main 등장 1 줄
|
|
SHARD_COLLECTION highRisk=true · main 등장 1 줄
|
|
고위험인데 다섯 인자 오버로드로 넘어가는 자리 :
|
|
MongoShardingAdminGateway.java:64 adminGateway.execute(MongoAdminOperation.SHARD_COLLECTION, collection, operator, reason, apply);
|
|
MongoShardingAdminGateway.java:75 adminGateway.execute(MongoAdminOperation.REFINE_SHARD_KEY, collection, operator, reason, apply);
|
|
MongoShardingAdminGateway.java:86 adminGateway.execute(
|
|
MongoShardingAdminGateway.java:87 MongoAdminOperation.RESHARD_COLLECTION, collection, operator, reason, apply);
|
|
MongoQueryableEncryptionCollectionManager.java:34 /** Creates the encrypted collection and its metadata collections. */
|
|
MongoQueryableEncryptionCollectionManager.java:35 public void createEncryptedCollection(
|
|
MongoQueryableEncryptionCollectionManager.java:36 MongoQueryableEncryptionProfile profile,
|
|
MongoQueryableEncryptionCollectionManager.java:37 String operator,
|
|
MongoQueryableEncryptionCollectionManager.java:38 String reason,
|
|
MongoQueryableEncryptionCollectionManager.java:39 Supplier<Void> apply) {
|
|
MongoQueryableEncryptionCollectionManager.java:40 Objects.requireNonNull(profile, "profile");
|
|
MongoQueryableEncryptionCollectionManager.java:41 adminGateway.execute(
|
|
MongoQueryableEncryptionCollectionManager.java:42 MongoAdminOperation.CREATE_COLLECTION, profile.collection(), operator, reason, apply);
|
|
MongoQueryableEncryptionCollectionManager.java:43 }
|
|
MongoQueryableEncryptionCollectionManager.java:61 /** Rotates a data key. Requires its own runbook and evidence. */
|
|
MongoQueryableEncryptionCollectionManager.java:62 public void rotateDataKey(String keyAlias, String operator, String reason, Supplier<Void> apply) {
|
|
MongoQueryableEncryptionCollectionManager.java:63 adminGateway.execute(
|
|
MongoQueryableEncryptionCollectionManager.java:64 MongoAdminOperation.MANAGE_ENCRYPTION_KEY, keyAlias, operator, reason, apply);
|
|
MongoQueryableEncryptionCollectionManager.java:65 }
|
|
|
|
# 두 승인 타입을 잇는 코드가 있는가
|
|
ReshardApproval 이 main 에 나오는 줄 : 4 개
|
|
MongoShardingAdminGateway.java:70 ReshardApproval approval,
|
|
MongoShardingAdminGateway.java:81 ReshardApproval approval,
|
|
ReshardApproval.java:15 public record ReshardApproval(
|
|
ReshardApproval.java:22 public ReshardApproval {
|