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:
29
internal/cache/seq_buffer.go
vendored
29
internal/cache/seq_buffer.go
vendored
@@ -85,6 +85,8 @@ end
|
||||
-- 缓冲区耗尽,扩展 LAST
|
||||
local newLast = last + bufSize
|
||||
redis.call('HSET', bufKey, 'LAST', newLast)
|
||||
-- 推进 CURR 防止下一个调用者获得相同的 start seq
|
||||
redis.call('HSET', bufKey, 'CURR', curr + 1)
|
||||
-- 同步更新 msg_seq(兼容旧 INCR key,确保 READ 的值不回退)
|
||||
local oldSeq = tonumber(redis.call('GET', seqKey))
|
||||
if oldSeq and oldSeq > newLast then
|
||||
@@ -178,12 +180,29 @@ func (m *SeqBufferManager) GetNextSeq(ctx context.Context, convID string, isGrou
|
||||
return 0, fmt.Errorf("allocate from redis: %w", err)
|
||||
}
|
||||
|
||||
// 3. 更新本地缓冲区
|
||||
// 3. 更新本地缓冲区(CAS 防止并发分配时丢失区间)
|
||||
newBuf := &localSeqBuffer{curr: startSeq, last: endSeq}
|
||||
m.buffers.Store(convID, newBuf)
|
||||
|
||||
// startSeq 已经被 Redis 分配,返回它
|
||||
return startSeq, nil
|
||||
for {
|
||||
existing, loaded := m.buffers.LoadOrStore(convID, newBuf)
|
||||
if !loaded {
|
||||
// 首次插入,直接使用
|
||||
return startSeq, nil
|
||||
}
|
||||
// 已有缓冲区,检查是否被其他 goroutine 刷新
|
||||
oldBuf := existing.(*localSeqBuffer)
|
||||
oldBuf.mu.Lock()
|
||||
if oldBuf.curr < oldBuf.last {
|
||||
// 旧缓冲区仍有余量,直接使用(丢弃本次分配,避免区间泄漏)
|
||||
seq := oldBuf.curr + 1
|
||||
oldBuf.curr = seq
|
||||
oldBuf.mu.Unlock()
|
||||
return seq, nil
|
||||
}
|
||||
// 旧缓冲区耗尽,替换为新分配的区间
|
||||
m.buffers.Store(convID, newBuf)
|
||||
oldBuf.mu.Unlock()
|
||||
return startSeq, nil
|
||||
}
|
||||
}
|
||||
|
||||
// allocateFromRedis 向 Redis 预分配 seq 区间
|
||||
|
||||
Reference in New Issue
Block a user