|
- #!/bin/bash
-
- # Master + Slave 并发扣费测试脚本
- # 测试场景:Master本地扣费 和 Slave同步扣费 同时对同一用户扣费
- # 用法: ./test_region_sync_concurrent.sh [USER_ID]
-
- MASTER="http://172.22.57.85:23000"
- SLAVE="http://172.22.57.85:13000"
- API_KEY="123456789"
- USER_ID=${1:-1}
-
- echo "=========================================="
- echo " Master/Slave 并发扣费冲突测试"
- echo "=========================================="
- echo "Master: $MASTER"
- echo "Slave: $SLAVE"
- echo "测试用户ID: $USER_ID"
- echo "=========================================="
- echo ""
-
- # 先查询当前余额
- echo "[1] 查询初始余额..."
- INITIAL=$(curl -s -X POST "$MASTER/api/internal/sync/quota/query" \
- -H "Content-Type: application/json" \
- -H "X-Sync-API-Key: $API_KEY" \
- -H "X-Sync-Node: $SLAVE" \
- -d "{\"user_id\": $USER_ID}")
- INITIAL_QUOTA=$(echo $INITIAL | grep -o '"quota":[0-9]*' | grep -o '[0-9]*')
- echo "初始余额: $INITIAL_QUOTA"
- echo ""
-
- # 定义扣费金额
- MASTER_DEDUCT=1000 # Master端扣费 1000
- SLAVE_DEDUCT=1000 # Slave端扣费 1000
- EXPECTED_FINAL=$((INITIAL_QUOTA - MASTER_DEDUCT - SLAVE_DEDUCT))
-
- echo "[2] 并发执行 Master本地扣费($MASTER_DEDUCT) + Slave同步扣费($SLAVE_DEDUCT)..."
- echo " 期望最终余额: $INITIAL_QUOTA - $MASTER_DEDUCT - $SLAVE_DEDUCT = $EXPECTED_FINAL"
- echo ""
-
- # 并发发送两个请求
- # 请求1: 模拟 Slave 同步扣费(调用 Master 的 batch-deduct 接口)
- curl -s -X POST "$MASTER/api/internal/sync/quota/batch-deduct" \
- -H "Content-Type: application/json" \
- -H "X-Sync-API-Key: $API_KEY" \
- -H "X-Sync-Node: $SLAVE" \
- -d "{\"records\": [{\"user_id\": $USER_ID, \"request_id\": \"req-slave-concurrent-$(date +%s)\", \"quota\": $SLAVE_DEDUCT}]}" > /tmp/slave_result.json &
-
- # 请求2: 模拟 Master 本地扣费(通过正常API调用,这里用 DecreaseUserQuota 对应的管理接口)
- # 直接用 batch-deduct 的不同 request_id 模拟第二个并发扣费
- curl -s -X POST "$MASTER/api/internal/sync/quota/batch-deduct" \
- -H "Content-Type: application/json" \
- -H "X-Sync-API-Key: $API_KEY" \
- -H "X-Sync-Node: $SLAVE" \
- -d "{\"records\": [{\"user_id\": $USER_ID, \"request_id\": \"req-master-concurrent-$(date +%s)\", \"quota\": $MASTER_DEDUCT}]}" > /tmp/master_result.json &
-
- wait
- echo "Slave扣费结果: $(cat /tmp/slave_result.json)"
- echo "Master扣费结果: $(cat /tmp/master_result.json)"
- echo ""
-
- # 查询最终余额
- echo "[3] 查询最终余额..."
- FINAL=$(curl -s -X POST "$MASTER/api/internal/sync/quota/query" \
- -H "Content-Type: application/json" \
- -H "X-Sync-API-Key: $API_KEY" \
- -H "X-Sync-Node: $SLAVE" \
- -d "{\"user_id\": $USER_ID}")
- FINAL_QUOTA=$(echo $FINAL | grep -o '"quota":[0-9]*' | grep -o '[0-9]*')
- echo "最终余额: $FINAL_QUOTA"
- echo ""
-
- # 分析结果
- echo "=========================================="
- echo " 结果分析"
- echo "=========================================="
- echo "初始余额: $INITIAL_QUOTA"
- echo "Master扣费: -$MASTER_DEDUCT"
- echo "Slave扣费: -$SLAVE_DEDUCT"
- echo "期望最终余额: $EXPECTED_FINAL"
- echo "实际最终余额: $FINAL_QUOTA"
- echo ""
-
- ACTUAL_DEDUCTED=$((INITIAL_QUOTA - FINAL_QUOTA))
- echo "实际总共扣除: $ACTUAL_DEDUCTED"
- echo "期望总共扣除: $((MASTER_DEDUCT + SLAVE_DEDUCT))"
- echo ""
-
- if [ "$FINAL_QUOTA" -eq "$EXPECTED_FINAL" ]; then
- echo "✅ 测试通过: 余额变化正确,并发扣费无丢失"
- elif [ "$ACTUAL_DEDUCTED" -lt "$((MASTER_DEDUCT + SLAVE_DEDUCT))" ]; then
- LOST=$((MASTER_DEDUCT + SLAVE_DEDUCT - ACTUAL_DEDUCTED))
- echo "❌ 测试失败: 扣费丢失 $LOST,并发扣费存在覆盖问题"
- echo " 可能原因: BatchDeductQuota 使用 FOR UPDATE + 设定新值,"
- echo " 而 DecreaseUserQuota 使用 gorm.Expr 原子减,两者混用会互相覆盖"
- else
- echo "❌ 测试失败: 余额异常(扣多了或变成负数)"
- fi
- echo ""
-
- # 高并发版本: 多次并发测试
- echo "=========================================="
- echo " 高并发压力测试 (10轮)"
- echo "=========================================="
- echo ""
-
- echo "[4] 查询当前余额..."
- BEFORE=$(curl -s -X POST "$MASTER/api/internal/sync/quota/query" \
- -H "Content-Type: application/json" \
- -H "X-Sync-API-Key: $API_KEY" \
- -H "X-Sync-Node: $SLAVE" \
- -d "{\"user_id\": $USER_ID}")
- BEFORE_QUOTA=$(echo $BEFORE | grep -o '"quota":[0-9]*' | grep -o '[0-9]*')
- echo "当前余额: $BEFORE_QUOTA"
- echo ""
-
- echo "[5] 并发发送20个扣费请求(每个100)..."
- for i in {1..20}; do
- curl -s -X POST "$MASTER/api/internal/sync/quota/batch-deduct" \
- -H "Content-Type: application/json" \
- -H "X-Sync-API-Key: $API_KEY" \
- -H "X-Sync-Node: $SLAVE" \
- -d "{\"records\": [{\"user_id\": $USER_ID, \"request_id\": \"req-stress-$i-$(date +%s%N)\", \"quota\": 100}]}" > /dev/null &
- done
- wait
- echo "全部请求发送完成"
- echo ""
-
- echo "[6] 查询最终余额..."
- AFTER=$(curl -s -X POST "$MASTER/api/internal/sync/quota/query" \
- -H "Content-Type: application/json" \
- -H "X-Sync-API-Key: $API_KEY" \
- -H "X-Sync-Node: $SLAVE" \
- -d "{\"user_id\": $USER_ID}")
- AFTER_QUOTA=$(echo $AFTER | grep -o '"quota":[0-9]*' | grep -o '[0-9]*')
- echo "最终余额: $AFTER_QUOTA"
-
- ACTUAL_TOTAL_DEDUCTED=$((BEFORE_QUOTA - AFTER_QUOTA))
- EXPECTED_TOTAL_DEDUCTED=$((20 * 100))
-
- echo ""
- echo "实际总共扣除: $ACTUAL_TOTAL_DEDUCTED"
- echo "期望总共扣除: $EXPECTED_TOTAL_DEDUCTED"
-
- if [ "$ACTUAL_TOTAL_DEDUCTED" -eq "$EXPECTED_TOTAL_DEDUCTED" ]; then
- echo "✅ 高并发测试通过: 所有扣费都正确"
- elif [ "$ACTUAL_TOTAL_DEDUCTED" -lt "$EXPECTED_TOTAL_DEDUCTED" ]; then
- echo "❌ 高并发测试失败: 少扣了 $((EXPECTED_TOTAL_DEDUCTED - ACTUAL_TOTAL_DEDUCTED))"
- else
- echo "❌ 高并发测试失败: 多扣了 $((ACTUAL_TOTAL_DEDUCTED - EXPECTED_TOTAL_DEDUCTED))"
- fi
-
- echo ""
- echo "=========================================="
- echo " 测试完成"
- echo "=========================================="
|