perf(wallet): 避免同钱包预留占满数据库池
千并发验收的 pg_stat_activity 显示大量事务锁等待集中在同一验收钱包,等待行锁的请求持续占用 API Pool,导致 canceled acquire 和排队增长。 在每个 Store 内增加 256 槽钱包预留分片锁,同一钱包先在进程内串行再申请数据库连接;跨 API 节点仍由 PostgreSQL 行锁保证余额与幂等正确性。 验证:Go 全量测试、临时 PostgreSQL 并发账务集成测试、gofmt 和 git diff --check 通过。
This commit is contained in:
@@ -5,14 +5,30 @@ import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"hash/fnv"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/easyai/easyai-ai-gateway/apps/api/internal/auth"
|
||||
"github.com/jackc/pgx/v5"
|
||||
)
|
||||
|
||||
const walletReservationLockStripes = 256
|
||||
|
||||
type walletReservationLockSet struct {
|
||||
stripes [walletReservationLockStripes]sync.Mutex
|
||||
}
|
||||
|
||||
func (locks *walletReservationLockSet) lock(gatewayUserID string) func() {
|
||||
hasher := fnv.New32a()
|
||||
_, _ = hasher.Write([]byte(strings.TrimSpace(gatewayUserID)))
|
||||
stripe := &locks.stripes[hasher.Sum32()%walletReservationLockStripes]
|
||||
stripe.Lock()
|
||||
return stripe.Unlock
|
||||
}
|
||||
|
||||
type GatewayWalletAccount struct {
|
||||
ID string `json:"id"`
|
||||
GatewayTenantID string `json:"gatewayTenantId,omitempty"`
|
||||
@@ -153,12 +169,16 @@ func (s *Store) ReserveTaskBilling(ctx context.Context, task GatewayTask, user *
|
||||
pricingSnapshot = emptyObjectIfNil(pricingSnapshots[0])
|
||||
}
|
||||
if exactAmount := walletString(pricingSnapshot["reservationAmount"]); exactAmount != "" {
|
||||
unlock := s.walletReservationLocks.lock(gatewayUserID)
|
||||
defer unlock()
|
||||
return s.reserveTaskBillingExact(ctx, task, gatewayUserID, exactAmount, pricingSnapshot)
|
||||
}
|
||||
amounts := walletBillingAmounts(billings)
|
||||
if len(amounts) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
unlock := s.walletReservationLocks.lock(gatewayUserID)
|
||||
defer unlock()
|
||||
|
||||
reservations := make([]WalletBillingReservation, 0, len(amounts))
|
||||
pricingSnapshotJSON, _ := json.Marshal(sanitizeJSONForStorage(pricingSnapshot))
|
||||
|
||||
Reference in New Issue
Block a user