feat: implement concurrent top-up locking mechanism to prevent race conditions
This commit is contained in:
@@ -844,18 +844,64 @@ type topUpRequest struct {
|
||||
Key string `json:"key"`
|
||||
}
|
||||
|
||||
var topUpLock = sync.Mutex{}
|
||||
var topUpLocks sync.Map
|
||||
var topUpCreateLock sync.Mutex
|
||||
|
||||
type topUpTryLock struct {
|
||||
ch chan struct{}
|
||||
}
|
||||
|
||||
func newTopUpTryLock() *topUpTryLock {
|
||||
return &topUpTryLock{ch: make(chan struct{}, 1)}
|
||||
}
|
||||
|
||||
func (l *topUpTryLock) TryLock() bool {
|
||||
select {
|
||||
case l.ch <- struct{}{}:
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
func (l *topUpTryLock) Unlock() {
|
||||
select {
|
||||
case <-l.ch:
|
||||
default:
|
||||
}
|
||||
}
|
||||
|
||||
func getTopUpLock(userID int) *topUpTryLock {
|
||||
if v, ok := topUpLocks.Load(userID); ok {
|
||||
return v.(*topUpTryLock)
|
||||
}
|
||||
topUpCreateLock.Lock()
|
||||
defer topUpCreateLock.Unlock()
|
||||
if v, ok := topUpLocks.Load(userID); ok {
|
||||
return v.(*topUpTryLock)
|
||||
}
|
||||
l := newTopUpTryLock()
|
||||
topUpLocks.Store(userID, l)
|
||||
return l
|
||||
}
|
||||
|
||||
func TopUp(c *gin.Context) {
|
||||
topUpLock.Lock()
|
||||
defer topUpLock.Unlock()
|
||||
id := c.GetInt("id")
|
||||
lock := getTopUpLock(id)
|
||||
if !lock.TryLock() {
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"success": false,
|
||||
"message": "充值处理中,请稍后重试",
|
||||
})
|
||||
return
|
||||
}
|
||||
defer lock.Unlock()
|
||||
req := topUpRequest{}
|
||||
err := c.ShouldBindJSON(&req)
|
||||
if err != nil {
|
||||
common.ApiError(c, err)
|
||||
return
|
||||
}
|
||||
id := c.GetInt("id")
|
||||
quota, err := model.Redeem(req.Key, id)
|
||||
if err != nil {
|
||||
common.ApiError(c, err)
|
||||
@@ -866,7 +912,6 @@ func TopUp(c *gin.Context) {
|
||||
"message": "",
|
||||
"data": quota,
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
type UpdateUserSettingRequest struct {
|
||||
|
||||
Reference in New Issue
Block a user