fix(cache): enhance sequence generation atomicity and concurrency safety
Implement Lua scripts for atomic sequence incrementing and cold-start initialization in Redis to prevent race conditions. Improve the sequence buffer manager with CAS-like logic to prevent interval leakage during concurrent allocations. Update the message repository to use `SELECT ... FOR UPDATE` and conditional updates for `last_seq` to ensure database consistency during high concurrency. - Add `nextSeqLua` and `initSeqLua` for atomic Redis operations - Implement thread-safe buffer management in `seq_buffer.go` - Add row-level locking in `message_repo.go` - Update service layers to support group-aware sequence retrieval
This commit is contained in:
@@ -414,14 +414,21 @@ func (r *messageRepository) UpdateConversationLastSeq(conversationID string, seq
|
||||
}).Error
|
||||
}
|
||||
|
||||
// GetNextSeq 获取会话的下一个seq值
|
||||
// GetNextSeq 获取会话的下一个seq值(使用 SELECT ... FOR UPDATE 保证原子性)
|
||||
func (r *messageRepository) GetNextSeq(conversationID string) (int64, error) {
|
||||
var conv model.Conversation
|
||||
err := r.db.Select("last_seq").Where("id = ?", conversationID).First(&conv).Error
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return conv.LastSeq + 1, nil
|
||||
var nextSeq int64
|
||||
err := r.db.Transaction(func(tx *gorm.DB) error {
|
||||
var conv model.Conversation
|
||||
if err := tx.Clauses(clause.Locking{Strength: "UPDATE"}).Where("id = ?", conversationID).First(&conv).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
nextSeq = conv.LastSeq + 1
|
||||
// 立即写回,防止并发读取同一个 last_seq 值
|
||||
return tx.Model(&model.Conversation{}).
|
||||
Where("id = ?", conversationID).
|
||||
Update("last_seq", nextSeq).Error
|
||||
})
|
||||
return nextSeq, err
|
||||
}
|
||||
|
||||
// CreateMessageWithSeq 创建消息并更新seq(事务操作)
|
||||
@@ -433,13 +440,20 @@ func (r *messageRepository) CreateMessageWithSeq(msg *model.Message) error {
|
||||
return err
|
||||
}
|
||||
|
||||
// 更新会话的last_seq
|
||||
if err := tx.Model(&model.Conversation{}).
|
||||
Where("id = ?", msg.ConversationID).
|
||||
// 更新会话last_seq:仅当新 seq 更大时才更新,防止并发写入导致 last_seq 回退
|
||||
result := tx.Model(&model.Conversation{}).
|
||||
Where("id = ? AND last_seq < ?", msg.ConversationID, msg.Seq).
|
||||
Updates(map[string]any{
|
||||
"last_seq": msg.Seq,
|
||||
"last_msg_time": gorm.Expr("CURRENT_TIMESTAMP"),
|
||||
}).Error; err != nil {
|
||||
})
|
||||
if result.Error != nil {
|
||||
return result.Error
|
||||
}
|
||||
// 当 last_seq >= msg.Seq 时(并发写入已更新),仍需刷新 last_msg_time
|
||||
if err := tx.Model(&model.Conversation{}).
|
||||
Where("id = ? AND last_seq >= ?", msg.ConversationID, msg.Seq).
|
||||
Update("last_msg_time", gorm.Expr("CURRENT_TIMESTAMP")).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user