chore: refresh gramsrv public release
This commit is contained in:
parent
75cebe8dbf
commit
70b6820474
1274 changed files with 378751 additions and 59919 deletions
|
|
@ -0,0 +1,88 @@
|
|||
package postgres
|
||||
|
||||
import (
|
||||
"context"
|
||||
"sync/atomic"
|
||||
"testing"
|
||||
|
||||
"telesrv/internal/domain"
|
||||
)
|
||||
|
||||
// staleChannelIDAllocator 模拟落后的 channel id 计数器:从 start 起按 1
|
||||
// 步进,必然撞上已存在的 channel 主键。
|
||||
type staleChannelIDAllocator struct {
|
||||
next atomic.Int64
|
||||
atLeast atomic.Int64 // 记录 NextChannelIDAtLeast 被调用时的 floor
|
||||
}
|
||||
|
||||
func (a *staleChannelIDAllocator) NextChannelID(_ context.Context) (int64, error) {
|
||||
return a.next.Add(1), nil
|
||||
}
|
||||
|
||||
func (a *staleChannelIDAllocator) CurrentChannelID(_ context.Context) (int64, error) {
|
||||
return a.next.Load(), nil
|
||||
}
|
||||
|
||||
func (a *staleChannelIDAllocator) NextChannelIDAtLeast(_ context.Context, floor int64) (int64, error) {
|
||||
a.atLeast.Store(floor)
|
||||
for {
|
||||
cur := a.next.Load()
|
||||
if cur < floor {
|
||||
if !a.next.CompareAndSwap(cur, floor) {
|
||||
continue
|
||||
}
|
||||
}
|
||||
return a.next.Add(1), nil
|
||||
}
|
||||
}
|
||||
|
||||
// TestChannelStoreCreateChannelRecoversFromStaleIDCounter 验证计数器落后
|
||||
// (Redis 快照回退 / 测试 fallback 分配器绕过 Redis 直写同一库)时,
|
||||
// CreateChannel 经预检自愈拿到空闲 id,而非撞主键 500。
|
||||
func TestChannelStoreCreateChannelRecoversFromStaleIDCounter(t *testing.T) {
|
||||
pool := testPool(t)
|
||||
ctx := context.Background()
|
||||
suffix := randomSuffix(t)
|
||||
|
||||
users := NewUserStore(pool)
|
||||
creator, err := users.Create(ctx, domain.User{AccessHash: 91, Phone: "+1673" + suffix + "01", FirstName: "StaleIDCreator"})
|
||||
if err != nil {
|
||||
t.Fatalf("create user: %v", err)
|
||||
}
|
||||
t.Cleanup(func() {
|
||||
_, _ = pool.Exec(ctx, "DELETE FROM users WHERE id = $1", creator.ID)
|
||||
})
|
||||
|
||||
// 先用默认 PG fallback 分配器造一个真实 channel(占住 max id)。
|
||||
seedStore := NewChannelStore(pool)
|
||||
seed, err := seedStore.CreateChannel(ctx, domain.CreateChannelRequest{
|
||||
CreatorUserID: creator.ID,
|
||||
Title: "StaleSeed " + suffix,
|
||||
Megagroup: true,
|
||||
Date: 1700000700,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("seed channel: %v", err)
|
||||
}
|
||||
|
||||
// 落后计数器从 seed id 之前很远处开始(模拟回退),首次分配必撞。
|
||||
stale := &staleChannelIDAllocator{}
|
||||
stale.next.Store(seed.Channel.ID - 3)
|
||||
staleStore := NewChannelStore(pool, WithChannelAllocators(stale, nil))
|
||||
// msgIDs 传 nil 会被 fallback 覆盖;channel pts 由 PG 事务直接维护。
|
||||
created, err := staleStore.CreateChannel(ctx, domain.CreateChannelRequest{
|
||||
CreatorUserID: creator.ID,
|
||||
Title: "StaleRecovered " + suffix,
|
||||
Megagroup: true,
|
||||
Date: 1700000701,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("create channel with stale counter: %v", err)
|
||||
}
|
||||
if created.Channel.ID <= seed.Channel.ID {
|
||||
t.Fatalf("recovered channel id = %d, want > seed id %d", created.Channel.ID, seed.Channel.ID)
|
||||
}
|
||||
if stale.atLeast.Load() < seed.Channel.ID {
|
||||
t.Fatalf("NextChannelIDAtLeast floor = %d, want >= seed id %d(应按表内最大 id 对账)", stale.atLeast.Load(), seed.Channel.ID)
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue