587 lines
18 KiB
Go
587 lines
18 KiB
Go
|
|
package binanceservice
|
|||
|
|
|
|||
|
|
import (
|
|||
|
|
"context"
|
|||
|
|
"fmt"
|
|||
|
|
"sync"
|
|||
|
|
"testing"
|
|||
|
|
"time"
|
|||
|
|
|
|||
|
|
"github.com/go-admin-team/go-admin-core/logger"
|
|||
|
|
"github.com/go-redis/redis/v8"
|
|||
|
|
"github.com/shopspring/decimal"
|
|||
|
|
"github.com/stretchr/testify/assert"
|
|||
|
|
"github.com/stretchr/testify/suite"
|
|||
|
|
"gorm.io/driver/mysql"
|
|||
|
|
"gorm.io/gorm"
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
// OptimizedSystemTestSuite 优化系统测试套件
|
|||
|
|
type OptimizedSystemTestSuite struct {
|
|||
|
|
suite.Suite
|
|||
|
|
db *gorm.DB
|
|||
|
|
redisClient *redis.Client
|
|||
|
|
config *OptimizedConfig
|
|||
|
|
metricsCollector *MetricsCollector
|
|||
|
|
errorHandler *ErrorHandler
|
|||
|
|
lockManager *LockManager
|
|||
|
|
txManager *TransactionManager
|
|||
|
|
reverseService *ReverseServiceOptimized
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// SetupSuite 设置测试套件
|
|||
|
|
func (suite *OptimizedSystemTestSuite) SetupSuite() {
|
|||
|
|
// 初始化数据库连接(测试环境)
|
|||
|
|
dsn := "root:password@tcp(localhost:3306)/test_exchange?charset=utf8mb4&parseTime=True&loc=Local"
|
|||
|
|
db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})
|
|||
|
|
if err != nil {
|
|||
|
|
suite.T().Skip("跳过集成测试:无法连接数据库")
|
|||
|
|
return
|
|||
|
|
}
|
|||
|
|
suite.db = db
|
|||
|
|
|
|||
|
|
// 初始化Redis连接(测试环境)
|
|||
|
|
suite.redisClient = redis.NewClient(&redis.Options{
|
|||
|
|
Addr: "localhost:6379",
|
|||
|
|
Password: "",
|
|||
|
|
DB: 1, // 使用测试数据库
|
|||
|
|
})
|
|||
|
|
|
|||
|
|
// 测试Redis连接
|
|||
|
|
ctx := context.Background()
|
|||
|
|
if err := suite.redisClient.Ping(ctx).Err(); err != nil {
|
|||
|
|
suite.T().Skip("跳过集成测试:无法连接Redis")
|
|||
|
|
return
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 初始化配置
|
|||
|
|
suite.config = GetDefaultOptimizedConfig()
|
|||
|
|
suite.config.MonitorConfig.Enabled = true
|
|||
|
|
|
|||
|
|
// 初始化组件
|
|||
|
|
suite.metricsCollector = NewMetricsCollector(suite.config)
|
|||
|
|
suite.errorHandler = NewErrorHandler(suite.config, suite.metricsCollector)
|
|||
|
|
suite.lockManager = NewLockManager(suite.config, suite.redisClient, suite.metricsCollector)
|
|||
|
|
suite.txManager = NewTransactionManager(suite.db, suite.metricsCollector, suite.config)
|
|||
|
|
|
|||
|
|
// 启动指标收集
|
|||
|
|
suite.metricsCollector.Start()
|
|||
|
|
|
|||
|
|
// 创建测试表
|
|||
|
|
suite.createTestTables()
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// TearDownSuite 清理测试套件
|
|||
|
|
func (suite *OptimizedSystemTestSuite) TearDownSuite() {
|
|||
|
|
if suite.metricsCollector != nil {
|
|||
|
|
suite.metricsCollector.Stop()
|
|||
|
|
}
|
|||
|
|
if suite.redisClient != nil {
|
|||
|
|
suite.redisClient.Close()
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// SetupTest 设置每个测试
|
|||
|
|
func (suite *OptimizedSystemTestSuite) SetupTest() {
|
|||
|
|
// 清理Redis测试数据
|
|||
|
|
ctx := context.Background()
|
|||
|
|
suite.redisClient.FlushDB(ctx)
|
|||
|
|
|
|||
|
|
// 清理数据库测试数据
|
|||
|
|
suite.db.Exec("DELETE FROM test_orders")
|
|||
|
|
suite.db.Exec("DELETE FROM test_positions")
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// createTestTables 创建测试表
|
|||
|
|
func (suite *OptimizedSystemTestSuite) createTestTables() {
|
|||
|
|
// 创建测试订单表
|
|||
|
|
suite.db.Exec(`
|
|||
|
|
CREATE TABLE IF NOT EXISTS test_orders (
|
|||
|
|
id BIGINT PRIMARY KEY AUTO_INCREMENT,
|
|||
|
|
order_id VARCHAR(100) UNIQUE NOT NULL,
|
|||
|
|
user_id VARCHAR(50) NOT NULL,
|
|||
|
|
symbol VARCHAR(20) NOT NULL,
|
|||
|
|
side VARCHAR(10) NOT NULL,
|
|||
|
|
quantity DECIMAL(20,8) NOT NULL,
|
|||
|
|
price DECIMAL(20,8),
|
|||
|
|
status VARCHAR(20) NOT NULL,
|
|||
|
|
version INT DEFAULT 0,
|
|||
|
|
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
|||
|
|
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
|||
|
|
INDEX idx_user_symbol (user_id, symbol),
|
|||
|
|
INDEX idx_status (status),
|
|||
|
|
INDEX idx_version (version)
|
|||
|
|
)
|
|||
|
|
`)
|
|||
|
|
|
|||
|
|
// 创建测试持仓表
|
|||
|
|
suite.db.Exec(`
|
|||
|
|
CREATE TABLE IF NOT EXISTS test_positions (
|
|||
|
|
id BIGINT PRIMARY KEY AUTO_INCREMENT,
|
|||
|
|
user_id VARCHAR(50) NOT NULL,
|
|||
|
|
symbol VARCHAR(20) NOT NULL,
|
|||
|
|
side VARCHAR(10) NOT NULL,
|
|||
|
|
quantity DECIMAL(20,8) NOT NULL,
|
|||
|
|
avg_price DECIMAL(20,8),
|
|||
|
|
status VARCHAR(20) NOT NULL,
|
|||
|
|
version INT DEFAULT 0,
|
|||
|
|
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
|||
|
|
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
|||
|
|
UNIQUE KEY uk_user_symbol_side (user_id, symbol, side),
|
|||
|
|
INDEX idx_version (version)
|
|||
|
|
)
|
|||
|
|
`)
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// TestLockManager 测试锁管理器
|
|||
|
|
func (suite *OptimizedSystemTestSuite) TestLockManager() {
|
|||
|
|
ctx := context.Background()
|
|||
|
|
|
|||
|
|
// 测试分布式锁
|
|||
|
|
suite.Run("DistributedLock", func() {
|
|||
|
|
config := LockManagerConfig{
|
|||
|
|
Type: LockTypeDistributed,
|
|||
|
|
Scope: ScopeOrder,
|
|||
|
|
Key: "test_order_123",
|
|||
|
|
Expiration: 10 * time.Second,
|
|||
|
|
Timeout: 5 * time.Second,
|
|||
|
|
RetryDelay: 100 * time.Millisecond,
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 获取锁
|
|||
|
|
lock, err := suite.lockManager.AcquireLock(ctx, config)
|
|||
|
|
assert.NoError(suite.T(), err)
|
|||
|
|
assert.NotNil(suite.T(), lock)
|
|||
|
|
assert.True(suite.T(), lock.IsLocked())
|
|||
|
|
|
|||
|
|
// 尝试获取同一个锁(应该失败)
|
|||
|
|
config.Timeout = 1 * time.Second
|
|||
|
|
_, err = suite.lockManager.AcquireLock(ctx, config)
|
|||
|
|
assert.Error(suite.T(), err)
|
|||
|
|
|
|||
|
|
// 释放锁
|
|||
|
|
err = suite.lockManager.ReleaseLock(ctx, lock)
|
|||
|
|
assert.NoError(suite.T(), err)
|
|||
|
|
assert.False(suite.T(), lock.IsLocked())
|
|||
|
|
})
|
|||
|
|
|
|||
|
|
// 测试本地锁
|
|||
|
|
suite.Run("LocalLock", func() {
|
|||
|
|
config := LockManagerConfig{
|
|||
|
|
Type: LockTypeLocal,
|
|||
|
|
Scope: ScopeUser,
|
|||
|
|
Key: "test_user_456",
|
|||
|
|
Expiration: 10 * time.Second,
|
|||
|
|
Timeout: 5 * time.Second,
|
|||
|
|
RetryDelay: 100 * time.Millisecond,
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
lock, err := suite.lockManager.AcquireLock(ctx, config)
|
|||
|
|
assert.NoError(suite.T(), err)
|
|||
|
|
assert.NotNil(suite.T(), lock)
|
|||
|
|
assert.True(suite.T(), lock.IsLocked())
|
|||
|
|
|
|||
|
|
err = suite.lockManager.ReleaseLock(ctx, lock)
|
|||
|
|
assert.NoError(suite.T(), err)
|
|||
|
|
})
|
|||
|
|
|
|||
|
|
// 测试并发锁
|
|||
|
|
suite.Run("ConcurrentLocks", func() {
|
|||
|
|
var wg sync.WaitGroup
|
|||
|
|
successCount := 0
|
|||
|
|
var mu sync.Mutex
|
|||
|
|
|
|||
|
|
for i := 0; i < 10; i++ {
|
|||
|
|
wg.Add(1)
|
|||
|
|
go func(id int) {
|
|||
|
|
defer wg.Done()
|
|||
|
|
|
|||
|
|
config := LockManagerConfig{
|
|||
|
|
Type: LockTypeDistributed,
|
|||
|
|
Scope: ScopeGlobal,
|
|||
|
|
Key: "concurrent_test",
|
|||
|
|
Expiration: 2 * time.Second,
|
|||
|
|
Timeout: 1 * time.Second,
|
|||
|
|
RetryDelay: 50 * time.Millisecond,
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
lock, err := suite.lockManager.AcquireLock(ctx, config)
|
|||
|
|
if err == nil {
|
|||
|
|
mu.Lock()
|
|||
|
|
successCount++
|
|||
|
|
mu.Unlock()
|
|||
|
|
|
|||
|
|
time.Sleep(100 * time.Millisecond)
|
|||
|
|
suite.lockManager.ReleaseLock(ctx, lock)
|
|||
|
|
}
|
|||
|
|
}(i)
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
wg.Wait()
|
|||
|
|
assert.Equal(suite.T(), 1, successCount, "只有一个goroutine应该获得锁")
|
|||
|
|
})
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// TestTransactionManager 测试事务管理器
|
|||
|
|
func (suite *OptimizedSystemTestSuite) TestTransactionManager() {
|
|||
|
|
ctx := context.Background()
|
|||
|
|
|
|||
|
|
// 测试基本事务
|
|||
|
|
suite.Run("BasicTransaction", func() {
|
|||
|
|
config := GetDefaultTransactionConfig()
|
|||
|
|
result := suite.txManager.WithTransaction(ctx, config, func(txCtx *TransactionContext) error {
|
|||
|
|
// 插入测试数据
|
|||
|
|
return txCtx.Tx.Exec(`
|
|||
|
|
INSERT INTO test_orders (order_id, user_id, symbol, side, quantity, status)
|
|||
|
|
VALUES (?, ?, ?, ?, ?, ?)
|
|||
|
|
`, "order_001", "user_001", "BTCUSDT", "BUY", 1.0, "NEW").Error
|
|||
|
|
})
|
|||
|
|
|
|||
|
|
assert.True(suite.T(), result.Success)
|
|||
|
|
assert.NoError(suite.T(), result.Error)
|
|||
|
|
|
|||
|
|
// 验证数据已插入
|
|||
|
|
var count int64
|
|||
|
|
suite.db.Raw("SELECT COUNT(*) FROM test_orders WHERE order_id = ?", "order_001").Scan(&count)
|
|||
|
|
assert.Equal(suite.T(), int64(1), count)
|
|||
|
|
})
|
|||
|
|
|
|||
|
|
// 测试事务回滚
|
|||
|
|
suite.Run("TransactionRollback", func() {
|
|||
|
|
config := GetDefaultTransactionConfig()
|
|||
|
|
result := suite.txManager.WithTransaction(ctx, config, func(txCtx *TransactionContext) error {
|
|||
|
|
// 插入数据
|
|||
|
|
if err := txCtx.Tx.Exec(`
|
|||
|
|
INSERT INTO test_orders (order_id, user_id, symbol, side, quantity, status)
|
|||
|
|
VALUES (?, ?, ?, ?, ?, ?)
|
|||
|
|
`, "order_002", "user_002", "ETHUSDT", "SELL", 2.0, "NEW").Error; err != nil {
|
|||
|
|
return err
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 故意返回错误以触发回滚
|
|||
|
|
return fmt.Errorf("测试回滚")
|
|||
|
|
})
|
|||
|
|
|
|||
|
|
assert.False(suite.T(), result.Success)
|
|||
|
|
assert.Error(suite.T(), result.Error)
|
|||
|
|
|
|||
|
|
// 验证数据未插入
|
|||
|
|
var count int64
|
|||
|
|
suite.db.Raw("SELECT COUNT(*) FROM test_orders WHERE order_id = ?", "order_002").Scan(&count)
|
|||
|
|
assert.Equal(suite.T(), int64(0), count)
|
|||
|
|
})
|
|||
|
|
|
|||
|
|
// 测试乐观锁
|
|||
|
|
suite.Run("OptimisticLock", func() {
|
|||
|
|
// 先插入测试数据
|
|||
|
|
suite.db.Exec(`
|
|||
|
|
INSERT INTO test_positions (user_id, symbol, side, quantity, avg_price, status, version)
|
|||
|
|
VALUES (?, ?, ?, ?, ?, ?, ?)
|
|||
|
|
`, "user_003", "BTCUSDT", "LONG", 1.0, 50000.0, "OPEN", 0)
|
|||
|
|
|
|||
|
|
// 测试正常更新
|
|||
|
|
updates := map[string]interface{}{
|
|||
|
|
"quantity": 2.0,
|
|||
|
|
}
|
|||
|
|
err := suite.txManager.UpdateWithOptimisticLock(ctx, nil, updates,
|
|||
|
|
"user_id = ? AND symbol = ? AND side = ? AND version = ?",
|
|||
|
|
"user_003", "BTCUSDT", "LONG", 0)
|
|||
|
|
assert.NoError(suite.T(), err)
|
|||
|
|
|
|||
|
|
// 测试版本冲突
|
|||
|
|
updates["quantity"] = 3.0
|
|||
|
|
err = suite.txManager.UpdateWithOptimisticLock(ctx, nil, updates,
|
|||
|
|
"user_id = ? AND symbol = ? AND side = ? AND version = ?",
|
|||
|
|
"user_003", "BTCUSDT", "LONG", 0) // 使用旧版本号
|
|||
|
|
assert.Error(suite.T(), err)
|
|||
|
|
assert.Contains(suite.T(), err.Error(), "乐观锁冲突")
|
|||
|
|
})
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// TestErrorHandler 测试错误处理器
|
|||
|
|
func (suite *OptimizedSystemTestSuite) TestErrorHandler() {
|
|||
|
|
ctx := context.Background()
|
|||
|
|
|
|||
|
|
// 测试错误分类
|
|||
|
|
suite.Run("ErrorClassification", func() {
|
|||
|
|
testCases := []struct {
|
|||
|
|
error error
|
|||
|
|
expected ErrorType
|
|||
|
|
}{
|
|||
|
|
{fmt.Errorf("connection refused"), ErrorTypeNetwork},
|
|||
|
|
{fmt.Errorf("database connection failed"), ErrorTypeDatabase},
|
|||
|
|
{fmt.Errorf("context deadline exceeded"), ErrorTypeTimeout},
|
|||
|
|
{fmt.Errorf("too many requests"), ErrorTypeRateLimit},
|
|||
|
|
{fmt.Errorf("unauthorized access"), ErrorTypeAuth},
|
|||
|
|
{fmt.Errorf("invalid parameter"), ErrorTypeValidation},
|
|||
|
|
{fmt.Errorf("insufficient balance"), ErrorTypeBusiness},
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
for _, tc := range testCases {
|
|||
|
|
errorInfo := suite.errorHandler.ClassifyError(tc.error, "test")
|
|||
|
|
assert.Equal(suite.T(), tc.expected, errorInfo.Type)
|
|||
|
|
}
|
|||
|
|
})
|
|||
|
|
|
|||
|
|
// 测试重试机制
|
|||
|
|
suite.Run("RetryMechanism", func() {
|
|||
|
|
attempts := 0
|
|||
|
|
config := RetryConfig{
|
|||
|
|
MaxRetries: 3,
|
|||
|
|
RetryDelay: 10 * time.Millisecond,
|
|||
|
|
MaxRetryDelay: 100 * time.Millisecond,
|
|||
|
|
BackoffFactor: 2.0,
|
|||
|
|
ApiRetryCount: 3,
|
|||
|
|
DbRetryCount: 3,
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 测试重试成功
|
|||
|
|
err := suite.errorHandler.RetryWithBackoff(ctx, func() error {
|
|||
|
|
attempts++
|
|||
|
|
if attempts < 3 {
|
|||
|
|
return fmt.Errorf("temporary error")
|
|||
|
|
}
|
|||
|
|
return nil
|
|||
|
|
}, config, "test_retry")
|
|||
|
|
|
|||
|
|
assert.NoError(suite.T(), err)
|
|||
|
|
assert.Equal(suite.T(), 3, attempts)
|
|||
|
|
|
|||
|
|
// 测试重试失败
|
|||
|
|
attempts = 0
|
|||
|
|
err = suite.errorHandler.RetryWithBackoff(ctx, func() error {
|
|||
|
|
attempts++
|
|||
|
|
return fmt.Errorf("persistent error")
|
|||
|
|
}, config, "test_retry_fail")
|
|||
|
|
|
|||
|
|
assert.Error(suite.T(), err)
|
|||
|
|
assert.Equal(suite.T(), 3, attempts)
|
|||
|
|
})
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// TestMetricsCollector 测试指标收集器
|
|||
|
|
func (suite *OptimizedSystemTestSuite) TestMetricsCollector() {
|
|||
|
|
// 测试订单指标
|
|||
|
|
suite.Run("OrderMetrics", func() {
|
|||
|
|
// 记录订单处理
|
|||
|
|
suite.metricsCollector.RecordOrderProcessed(true, 100*time.Millisecond)
|
|||
|
|
suite.metricsCollector.RecordOrderProcessed(false, 200*time.Millisecond)
|
|||
|
|
suite.metricsCollector.RecordOrderCanceled()
|
|||
|
|
suite.metricsCollector.RecordReverseOrder(true)
|
|||
|
|
|
|||
|
|
metrics := suite.metricsCollector.GetMetrics()
|
|||
|
|
orderMetrics := metrics["order_metrics"].(*OrderMetrics)
|
|||
|
|
|
|||
|
|
assert.Equal(suite.T(), int64(2), orderMetrics.TotalProcessed)
|
|||
|
|
assert.Equal(suite.T(), int64(1), orderMetrics.SuccessfulOrders)
|
|||
|
|
assert.Equal(suite.T(), int64(1), orderMetrics.FailedOrders)
|
|||
|
|
assert.Equal(suite.T(), int64(1), orderMetrics.CanceledOrders)
|
|||
|
|
assert.Equal(suite.T(), int64(1), orderMetrics.ReverseOrdersCreated)
|
|||
|
|
})
|
|||
|
|
|
|||
|
|
// 测试持仓指标
|
|||
|
|
suite.Run("PositionMetrics", func() {
|
|||
|
|
difference := decimal.NewFromFloat(0.1)
|
|||
|
|
suite.metricsCollector.RecordPositionSync(true, difference)
|
|||
|
|
suite.metricsCollector.RecordPositionSync(false, decimal.Zero)
|
|||
|
|
suite.metricsCollector.RecordClosePosition(false)
|
|||
|
|
suite.metricsCollector.RecordClosePosition(true)
|
|||
|
|
|
|||
|
|
metrics := suite.metricsCollector.GetMetrics()
|
|||
|
|
positionMetrics := metrics["position_metrics"].(*PositionMetrics)
|
|||
|
|
|
|||
|
|
assert.Equal(suite.T(), int64(2), positionMetrics.SyncAttempts)
|
|||
|
|
assert.Equal(suite.T(), int64(1), positionMetrics.SyncSuccesses)
|
|||
|
|
assert.Equal(suite.T(), int64(1), positionMetrics.SyncFailures)
|
|||
|
|
assert.Equal(suite.T(), int64(2), positionMetrics.ClosePositions)
|
|||
|
|
assert.Equal(suite.T(), int64(1), positionMetrics.ForceClosePositions)
|
|||
|
|
})
|
|||
|
|
|
|||
|
|
// 测试性能指标
|
|||
|
|
suite.Run("PerformanceMetrics", func() {
|
|||
|
|
suite.metricsCollector.RecordLockOperation(true, 50*time.Millisecond)
|
|||
|
|
suite.metricsCollector.RecordDbOperation(false, 30*time.Millisecond, nil)
|
|||
|
|
suite.metricsCollector.RecordApiCall(100*time.Millisecond, nil, 1)
|
|||
|
|
suite.metricsCollector.RecordConcurrency(5)
|
|||
|
|
|
|||
|
|
metrics := suite.metricsCollector.GetMetrics()
|
|||
|
|
perfMetrics := metrics["performance_metrics"].(*PerformanceMetrics)
|
|||
|
|
|
|||
|
|
assert.Equal(suite.T(), int64(1), perfMetrics.LockAcquisitions)
|
|||
|
|
assert.Equal(suite.T(), int64(1), perfMetrics.DbQueries)
|
|||
|
|
assert.Equal(suite.T(), int64(1), perfMetrics.ApiCalls)
|
|||
|
|
assert.Equal(suite.T(), int64(5), perfMetrics.ConcurrentRequests)
|
|||
|
|
})
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// TestConcurrentOperations 测试并发操作
|
|||
|
|
func (suite *OptimizedSystemTestSuite) TestConcurrentOperations() {
|
|||
|
|
ctx := context.Background()
|
|||
|
|
|
|||
|
|
suite.Run("ConcurrentOrderProcessing", func() {
|
|||
|
|
var wg sync.WaitGroup
|
|||
|
|
successCount := 0
|
|||
|
|
var mu sync.Mutex
|
|||
|
|
|
|||
|
|
// 并发处理订单
|
|||
|
|
for i := 0; i < 10; i++ {
|
|||
|
|
wg.Add(1)
|
|||
|
|
go func(id int) {
|
|||
|
|
defer wg.Done()
|
|||
|
|
|
|||
|
|
// 使用锁保护订单处理
|
|||
|
|
lockConfig := GetOrderLockConfig(fmt.Sprintf("order_%d", id))
|
|||
|
|
err := suite.lockManager.WithLock(ctx, lockConfig, func() error {
|
|||
|
|
// 模拟订单处理
|
|||
|
|
config := GetDefaultTransactionConfig()
|
|||
|
|
result := suite.txManager.WithTransaction(ctx, config, func(txCtx *TransactionContext) error {
|
|||
|
|
return txCtx.Tx.Exec(`
|
|||
|
|
INSERT INTO test_orders (order_id, user_id, symbol, side, quantity, status)
|
|||
|
|
VALUES (?, ?, ?, ?, ?, ?)
|
|||
|
|
`, fmt.Sprintf("concurrent_order_%d", id), fmt.Sprintf("user_%d", id),
|
|||
|
|
"BTCUSDT", "BUY", 1.0, "NEW").Error
|
|||
|
|
})
|
|||
|
|
|
|||
|
|
if result.Success {
|
|||
|
|
mu.Lock()
|
|||
|
|
successCount++
|
|||
|
|
mu.Unlock()
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return result.Error
|
|||
|
|
})
|
|||
|
|
|
|||
|
|
if err != nil {
|
|||
|
|
logger.Errorf("并发订单处理失败 [%d]: %v", id, err)
|
|||
|
|
}
|
|||
|
|
}(i)
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
wg.Wait()
|
|||
|
|
assert.Equal(suite.T(), 10, successCount, "所有并发订单都应该成功处理")
|
|||
|
|
|
|||
|
|
// 验证数据库中的记录数
|
|||
|
|
var count int64
|
|||
|
|
suite.db.Raw("SELECT COUNT(*) FROM test_orders WHERE order_id LIKE 'concurrent_order_%'").Scan(&count)
|
|||
|
|
assert.Equal(suite.T(), int64(10), count)
|
|||
|
|
})
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// TestSystemIntegration 测试系统集成
|
|||
|
|
func (suite *OptimizedSystemTestSuite) TestSystemIntegration() {
|
|||
|
|
ctx := context.Background()
|
|||
|
|
|
|||
|
|
suite.Run("CompleteOrderFlow", func() {
|
|||
|
|
userID := "integration_user"
|
|||
|
|
symbol := "BTCUSDT"
|
|||
|
|
orderID := "integration_order_001"
|
|||
|
|
|
|||
|
|
// 步骤1:创建订单(使用锁和事务)
|
|||
|
|
err := WithOrderLock(ctx, orderID, func() error {
|
|||
|
|
return WithDefaultTransaction(ctx, func(txCtx *TransactionContext) error {
|
|||
|
|
return txCtx.Tx.Exec(`
|
|||
|
|
INSERT INTO test_orders (order_id, user_id, symbol, side, quantity, price, status)
|
|||
|
|
VALUES (?, ?, ?, ?, ?, ?, ?)
|
|||
|
|
`, orderID, userID, symbol, "BUY", 1.0, 50000.0, "NEW").Error
|
|||
|
|
})
|
|||
|
|
})
|
|||
|
|
assert.NoError(suite.T(), err)
|
|||
|
|
|
|||
|
|
// 步骤2:更新订单状态(使用乐观锁)
|
|||
|
|
updates := map[string]interface{}{
|
|||
|
|
"status": "FILLED",
|
|||
|
|
}
|
|||
|
|
err = UpdateWithOptimisticLockGlobal(ctx, nil, updates,
|
|||
|
|
"order_id = ? AND version = ?", orderID, 0)
|
|||
|
|
assert.NoError(suite.T(), err)
|
|||
|
|
|
|||
|
|
// 步骤3:创建持仓(使用锁和事务)
|
|||
|
|
err = WithPositionLock(ctx, userID, symbol, func() error {
|
|||
|
|
return WithDefaultTransaction(ctx, func(txCtx *TransactionContext) error {
|
|||
|
|
return txCtx.Tx.Exec(`
|
|||
|
|
INSERT INTO test_positions (user_id, symbol, side, quantity, avg_price, status)
|
|||
|
|
VALUES (?, ?, ?, ?, ?, ?)
|
|||
|
|
ON DUPLICATE KEY UPDATE
|
|||
|
|
quantity = quantity + VALUES(quantity),
|
|||
|
|
avg_price = (avg_price * quantity + VALUES(avg_price) * VALUES(quantity)) / (quantity + VALUES(quantity)),
|
|||
|
|
version = version + 1
|
|||
|
|
`, userID, symbol, "LONG", 1.0, 50000.0, "OPEN").Error
|
|||
|
|
})
|
|||
|
|
})
|
|||
|
|
assert.NoError(suite.T(), err)
|
|||
|
|
|
|||
|
|
// 验证最终状态
|
|||
|
|
var orderStatus string
|
|||
|
|
suite.db.Raw("SELECT status FROM test_orders WHERE order_id = ?", orderID).Scan(&orderStatus)
|
|||
|
|
assert.Equal(suite.T(), "FILLED", orderStatus)
|
|||
|
|
|
|||
|
|
var positionQuantity float64
|
|||
|
|
suite.db.Raw("SELECT quantity FROM test_positions WHERE user_id = ? AND symbol = ?",
|
|||
|
|
userID, symbol).Scan(&positionQuantity)
|
|||
|
|
assert.Equal(suite.T(), 1.0, positionQuantity)
|
|||
|
|
})
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// TestPerformance 测试性能
|
|||
|
|
func (suite *OptimizedSystemTestSuite) TestPerformance() {
|
|||
|
|
ctx := context.Background()
|
|||
|
|
|
|||
|
|
suite.Run("LockPerformance", func() {
|
|||
|
|
startTime := time.Now()
|
|||
|
|
iterations := 1000
|
|||
|
|
|
|||
|
|
for i := 0; i < iterations; i++ {
|
|||
|
|
lockConfig := GetOrderLockConfig(fmt.Sprintf("perf_order_%d", i))
|
|||
|
|
lock, err := suite.lockManager.AcquireLock(ctx, lockConfig)
|
|||
|
|
assert.NoError(suite.T(), err)
|
|||
|
|
suite.lockManager.ReleaseLock(ctx, lock)
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
duration := time.Since(startTime)
|
|||
|
|
avgDuration := duration / time.Duration(iterations)
|
|||
|
|
logger.Infof("锁性能测试: %d 次操作耗时 %v,平均每次 %v", iterations, duration, avgDuration)
|
|||
|
|
|
|||
|
|
// 平均每次操作应该在合理范围内
|
|||
|
|
assert.Less(suite.T(), avgDuration, 10*time.Millisecond)
|
|||
|
|
})
|
|||
|
|
|
|||
|
|
suite.Run("TransactionPerformance", func() {
|
|||
|
|
startTime := time.Now()
|
|||
|
|
iterations := 100
|
|||
|
|
|
|||
|
|
for i := 0; i < iterations; i++ {
|
|||
|
|
config := GetDefaultTransactionConfig()
|
|||
|
|
result := suite.txManager.WithTransaction(ctx, config, func(txCtx *TransactionContext) error {
|
|||
|
|
return txCtx.Tx.Exec(`
|
|||
|
|
INSERT INTO test_orders (order_id, user_id, symbol, side, quantity, status)
|
|||
|
|
VALUES (?, ?, ?, ?, ?, ?)
|
|||
|
|
`, fmt.Sprintf("perf_order_%d", i), "perf_user", "BTCUSDT", "BUY", 1.0, "NEW").Error
|
|||
|
|
})
|
|||
|
|
assert.True(suite.T(), result.Success)
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
duration := time.Since(startTime)
|
|||
|
|
avgDuration := duration / time.Duration(iterations)
|
|||
|
|
logger.Infof("事务性能测试: %d 次操作耗时 %v,平均每次 %v", iterations, duration, avgDuration)
|
|||
|
|
|
|||
|
|
// 平均每次事务应该在合理范围内
|
|||
|
|
assert.Less(suite.T(), avgDuration, 100*time.Millisecond)
|
|||
|
|
})
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// TestSuite 运行测试套件
|
|||
|
|
func TestOptimizedSystemSuite(t *testing.T) {
|
|||
|
|
suite.Run(t, new(OptimizedSystemTestSuite))
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// BenchmarkLockManager 锁管理器基准测试
|
|||
|
|
func BenchmarkLockManager(b *testing.B) {
|
|||
|
|
// 这里可以添加基准测试
|
|||
|
|
b.Skip("基准测试需要Redis连接")
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// BenchmarkTransactionManager 事务管理器基准测试
|
|||
|
|
func BenchmarkTransactionManager(b *testing.B) {
|
|||
|
|
// 这里可以添加基准测试
|
|||
|
|
b.Skip("基准测试需要数据库连接")
|
|||
|
|
}
|