docs: prove sessions are shared by PostgreSQL, not Infinispan replication

Experiment 0 with three probes: cross-node refresh/logout, cache counter deltas around a single login, and cache entry ownership. Each node caches only what it handled; cache totals sum exactly to the database count.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
DongHyeonka
2026-09-04 09:58:10 +09:00
co-authored by Claude Opus 5
parent 006da7d490
commit e5ebaeb623
20 changed files with 1114 additions and 0 deletions
+55
View File
@@ -0,0 +1,55 @@
#!/usr/bin/env bash
# Experiment 0c — where does a session entry actually live?
#
# Experiment 0b showed keycloak-1's session cache never moved when keycloak-0
# handled a login. That leaves two explanations:
#
# (a) a DISTRIBUTED cache with owners=1 — entries are spread across nodes by
# consistent hashing, and this one happened to land on keycloak-0;
# (b) a LOCAL cache — each node only ever caches what it handled itself.
#
# They are distinguished by driving logins at the OTHER node. Under (a) the
# entries would keep landing on both nodes regardless of who was asked. Under
# (b) the count rises only on the node that received the request.
set -uo pipefail
NS="${NS:-keycloak-lab}"
N="${N:-5}"
K0_IP=$(kubectl -n "$NS" get pod keycloak-0 -o jsonpath='{.status.podIP}')
K1_IP=$(kubectl -n "$NS" get pod keycloak-1 -o jsonpath='{.status.podIP}')
ADMIN_PW=$(kubectl -n "$NS" get secret keycloak-lab-secrets \
-o jsonpath='{.data.KC_BOOTSTRAP_ADMIN_PASSWORD}' | base64 -d)
echo "수집 시각: $(date '+%Y-%m-%d %H:%M:%S %Z')"
echo " keycloak-0 = $K0_IP ($(kubectl -n "$NS" get pod keycloak-0 -o jsonpath='{.spec.nodeName}'))"
echo " keycloak-1 = $K1_IP ($(kubectl -n "$NS" get pod keycloak-1 -o jsonpath='{.spec.nodeName}'))"
echo
kubectl -n "$NS" run kc-own --rm -i --restart=Never \
--image=curlimages/curl:8.11.1 --quiet --command -- sh -c "
O=/tmp/o; : > \$O
ent() {
curl -s --retry 3 --max-time 20 http://\$1:9000/metrics \
| grep -E '^vendor_statistics_approximate_entries_unique.cache=.sessions' \
| awk '{print \$NF}'
}
login() { i=0; while [ \$i -lt $N ]; do
curl -s -o /dev/null -X POST http://\$1:8080/realms/master/protocol/openid-connect/token \
-d grant_type=password -d client_id=admin-cli \
-d username=admin -d 'password=$ADMIN_PW'
i=\$((i+1)); done; sleep 5; }
{
printf '%-32s %12s %12s\n' '단계' 'k0 entries' 'k1 entries'
printf '%-32s %12s %12s\n' '시작' \"\$(ent $K0_IP)\" \"\$(ent $K1_IP)\"
login $K1_IP
printf '%-32s %12s %12s\n' 'keycloak-1 에 로그인 ${N}회' \"\$(ent $K0_IP)\" \"\$(ent $K1_IP)\"
login $K0_IP
printf '%-32s %12s %12s\n' 'keycloak-0 에 로그인 ${N}회' \"\$(ent $K0_IP)\" \"\$(ent $K1_IP)\"
} >> \$O
cat \$O
" 2>&1 | grep -v '^pod .* deleted$'
echo
echo "=== 대조: PostgreSQL 에는 몇 건인가 ==="
kubectl -n "$NS" exec deploy/postgres -- psql -U keycloak -d keycloak -tAc \
"select count(*) from offline_user_session where offline_flag='0'" 2>/dev/null | sed 's/^/ online 세션 /'