Files
document-haness/docs/clean-architecture-backend-template/final/evidence/raw/a10-f005-hyperloglog-merge.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

113 lines
3.8 KiB
Plaintext

### 여러 키를 읽어 하나에 쓰는 연산 일곱의 서명
<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.
*/