3센터 A-A-A RTGS 실시간총액결제 프로토타입 최초 버전관리 시작. - backend: Kotlin/Gradle 멀티모듈(sequencer, common, 채널/센터 모듈) - frontend: Vite + TS - infra: docker-compose, prometheus/grafana, ELK 네이티브 스크립트 - loadtest(k6), sim(장애/순서/정합성 시나리오), docs(PoC 보고서/복원력방안)
64 lines
3.4 KiB
Bash
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
|