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>
This commit is contained in:
co-authored by
Claude Opus 5
parent
43bccd08a8
commit
b2963105a8
+112
@@ -0,0 +1,112 @@
|
||||
### 여러 키를 읽어 하나에 쓰는 연산 일곱의 서명
|
||||
<V> Set<V> difference(
|
||||
Collection<SetKey<V>> keys,
|
||||
AdvancedOperationPermit permit,
|
||||
MultiKeyPermit multiKeyPermit,
|
||||
OperationBudget budget);
|
||||
<V> Set<V> intersection(
|
||||
Collection<SetKey<V>> keys,
|
||||
AdvancedOperationPermit permit,
|
||||
MultiKeyPermit multiKeyPermit,
|
||||
OperationBudget budget);
|
||||
<V> Set<V> union(
|
||||
Collection<SetKey<V>> keys,
|
||||
AdvancedOperationPermit permit,
|
||||
MultiKeyPermit multiKeyPermit,
|
||||
OperationBudget budget);
|
||||
long bitOperation(
|
||||
BitmapOperation operation,
|
||||
BitmapKey destination,
|
||||
Collection<BitmapKey> sources,
|
||||
MultiKeyPermit permit,
|
||||
OperationBudget budget);
|
||||
<V> long searchStore(
|
||||
GeoKey<V> source,
|
||||
GeoKey<V> destination,
|
||||
GeoSearchRequest<V> request,
|
||||
MultiKeyPermit permit,
|
||||
OperationBudget budget);
|
||||
long count(Collection<? extends HyperLogLogKey<?>> keys, MultiKeyPermit permit);
|
||||
void merge(
|
||||
HyperLogLogKey<?> destination,
|
||||
Collection<? extends HyperLogLogKey<?>> sources,
|
||||
MultiKeyPermit permit);
|
||||
|
||||
### 정책 카탈로그의 위험 등급
|
||||
SDIFF:
|
||||
risk: R2
|
||||
support: ADVANCED_TYPED
|
||||
read-only: true
|
||||
key-spec: "1 -1 1"
|
||||
required-policy: set-algebra
|
||||
SINTER:
|
||||
risk: R2
|
||||
support: ADVANCED_TYPED
|
||||
read-only: true
|
||||
key-spec: "1 -1 1"
|
||||
required-policy: set-algebra
|
||||
SUNION:
|
||||
risk: R2
|
||||
support: ADVANCED_TYPED
|
||||
read-only: true
|
||||
key-spec: "1 -1 1"
|
||||
required-policy: set-algebra
|
||||
BITOP:
|
||||
risk: R2
|
||||
support: ADVANCED_TYPED
|
||||
key-spec: "2 -1 1"
|
||||
required-policy: multi-key-write
|
||||
PFCOUNT:
|
||||
risk: R2
|
||||
support: ADVANCED_TYPED
|
||||
read-only: true
|
||||
key-spec: "1 -1 1"
|
||||
required-policy: multi-key-read
|
||||
PFMERGE:
|
||||
risk: R2
|
||||
support: ADVANCED_TYPED
|
||||
key-spec: "1 -1 1"
|
||||
required-policy: multi-key-write
|
||||
GEOSEARCHSTORE:
|
||||
risk: R2
|
||||
support: ADVANCED_TYPED
|
||||
key-spec: "1 2 1"
|
||||
required-policy: multi-key-write
|
||||
|
||||
### R2 요청에 budget이 비어 있으면
|
||||
}
|
||||
if (request.budget().isEmpty()) {
|
||||
throw new RedisCommandRejectedException(
|
||||
"R2 command requires permit and budget", metadata(policy, OptionalInt.empty()));
|
||||
}
|
||||
|
||||
### 그래서 HyperLogLog 요청은 budget을 스스로 만든다
|
||||
69: Optional.of(context.collectionBudget(rendered.qualified().size(), rendered.requestBytes())),
|
||||
93: Optional.of(context.collectionBudget(qualified.size(), size)),
|
||||
long replyCeiling = (long) elements * limits.maxReplyBytesPerElement();
|
||||
return new OperationBudget(
|
||||
elements, Math.max(1L, requestBytes), replyCeiling, limits.collectionTimeout());
|
||||
|
||||
### 같은 식을 쓰는 다른 두 메서드
|
||||
313: elements, Math.max(1L, requestBytes), replyCeiling, limits.collectionTimeout());
|
||||
336: Math.max(1L, requestBytes),
|
||||
349: 1, Math.max(1L, requestBytes), limits.maxReplyBytesPerElement(), limits.scriptTimeout());
|
||||
|
||||
### 그 설계를 적어 둔 곳
|
||||
/**
|
||||
* The ceilings the typed operations apply when the public signature does not carry a budget.
|
||||
*
|
||||
* <p>Design section 10 gives some R2 methods a caller-supplied {@code OperationBudget} and others a
|
||||
* caller-supplied permit, but {@code CommandPolicyGuard} requires both for every R2 command. These
|
||||
* limits are what the SDK fills in for the half the signature omits, so an R2 command is never
|
||||
* admitted with an unbounded cost. The caller-supplied half always wins; this only supplies what
|
||||
* the caller had no way to pass.
|
||||
*
|
||||
|
||||
### 같은 패키지에서 반대로 적어 둔 곳
|
||||
/**
|
||||
* Explicit bound a caller accepts for one advanced operation.
|
||||
*
|
||||
* <p>Every R2 API requires a budget. The budget is never optional and never defaulted, because the
|
||||
* whole point is that the caller states the cost it is prepared to pay before Redis is asked.
|
||||
*/
|
||||
Reference in New Issue
Block a user