Files
rtgs/sim/s4_order.sh
rtgs 58ca23b5d9 Initialize RTGS prototype repository
3센터 A-A-A RTGS 실시간총액결제 프로토타입 최초 버전관리 시작.
- backend: Kotlin/Gradle 멀티모듈(sequencer, common, 채널/센터 모듈)
- frontend: Vite + TS
- infra: docker-compose, prometheus/grafana, ELK 네이티브 스크립트
- loadtest(k6), sim(장애/순서/정합성 시나리오), docs(PoC 보고서/복원력방안)
2026-07-15 15:37:32 +09:00

64 lines
3.4 KiB
Bash

#!/usr/bin/env bash
# S4 — 순서오류 탐지·교정(기능성 축): 저널(rtgs.journal)에 전역순번을 역전시켜 주입해도
# 원장엔진(Hermes)이 gap을 버퍼링→순서대로 재정렬 적용하는지 검증.
# 복원력방안 부록A S4 재현. 실행: bash sim/s4_order.sh
# ⚠ 순번기(Sequencer)를 잠시 멈추고 저널에 직접 주입한다(2-1 순번 영속화 덕에 재기동 시 안전 복원).
# 수동 기능테스트와 겹치지 않을 때 실행 권장.
cd "$(dirname "$0")"; source ./lib.sh
export KAFKA_HEAP_OPTS="-Xmx256M -Xms256M" # kafka .bat의 wmic 분기 회피(포터블)
FAILED=0
hr; echo "S4 순서오류 탐지·교정 검증 — 저널에 seq 역전 주입"; hr
# 0) 드레인(잔여 없게) + 순번기 정지(경쟁 발번 차단)
wait_drain 30 >/dev/null
SP=$(pid_of sequencer)
echo " Sequencer(PID $SP) 정지 — 주입 동안 경쟁 발번 차단"
[ -n "$SP" ] && taskkill //F //PID "$SP" //T >/dev/null 2>&1
sleep 3
TOTAL_BEFORE=$(q "SELECT sum(balance) FROM account")
M=$(q "SELECT COALESCE(MAX(global_seq),0) FROM journal_log")
echo " 현재 최대 순번 M=$M → 주입 순번 $((M+1)),$((M+2)),$((M+3)) (전달 순서는 역전)"
TS="$(date +%s)000"
B1="20260710S4$(printf '%010d' $((M+1)))" # seq M+1 1002->1003
B2="20260710S4$(printf '%010d' $((M+2)))" # seq M+2 1004->1005
B3="20260710S4$(printf '%010d' $((M+3)))" # seq M+3 1006->1007
entry() { # seq bmi from to amt
echo "{\"globalSeq\":$1,\"seqEpochMillis\":$TS,\"originCenter\":\"DC1\",\"core\":{\"bmi\":\"$2\",\"msgType\":\"pacs.008.001.08\",\"senderCode\":\"$3\",\"receiverCode\":\"$4\",\"debtorAcct\":\"ACC-$3\",\"creditorAcct\":\"ACC-$4\",\"amount\":$5,\"currency\":\"KRW\",\"origHash\":\"simS4\"}}"
}
# 전달 순서: M+2, M+3, M+1 (역전/gap 주입)
{
entry $((M+2)) "$B2" 1004 1005 22
entry $((M+3)) "$B3" 1006 1007 33
entry $((M+1)) "$B1" 1002 1003 11
} > /tmp/s4_inject.txt
echo " 주입 순서: seq $((M+2))$((M+3))$((M+1))"
"$KAFKA_PROD" --topic rtgs.journal --bootstrap-server localhost:9092 < /tmp/s4_inject.txt 2>/dev/null
echo " Hermes 재정렬·적용 대기..."
wait_drain 40 >/dev/null; sleep 2
s1=$(q "SELECT status FROM transfer WHERE bmi='$B1'")
s2=$(q "SELECT status FROM transfer WHERE bmi='$B2'")
s3=$(q "SELECT status FROM transfer WHERE bmi='$B3'")
# journal_log에 순번이 순서대로 기록되었는지
ORDER=$(q "SELECT string_agg(global_seq::text, ',' ORDER BY global_seq) FROM journal_log WHERE global_seq IN ($((M+1)),$((M+2)),$((M+3)))")
TOTAL_AFTER=$(q "SELECT sum(balance) FROM account")
hr; echo "결과"; hr
[ "$s1" = "ACCC" ] && [ "$s2" = "ACCC" ] && [ "$s3" = "ACCC" ] \
&& pass "역전 주입 3건 전건 정상 완결(ACCC) — 재정렬 성공" \
|| fail "완결 실패: seq$((M+1))=$s1 seq$((M+2))=$s2 seq$((M+3))=$s3"
[ "$ORDER" = "$((M+1)),$((M+2)),$((M+3))" ] \
&& pass "저널 순번 순서대로 기록: $ORDER" || fail "순번 기록 이상: $ORDER"
[ "$TOTAL_AFTER" = "$TOTAL_BEFORE" ] && pass "총액 보존: $TOTAL_AFTER" || fail "총액 불일치: $TOTAL_BEFORE -> $TOTAL_AFTER"
# 순번기 재기동(2-1 영속화: MAX(global_seq) 복원해 안전하게 이어서 발번)
echo " Sequencer 재기동(순번 $((M+3)) 이후부터 복원 발번)"
start_svc "RTGS Sequencer :8090 (S4 복귀)" sequencer
hr
[ "$FAILED" = "0" ] && echo "S4 PASS ✅ (gap 버퍼링→순서 재정렬)" || echo "S4 FAIL ❌"
exit $FAILED