chore: refresh gramsrv public release

This commit is contained in:
A 2026-06-30 14:37:43 +08:00
parent 75cebe8dbf
commit 70b6820474
1274 changed files with 378751 additions and 59919 deletions

View file

@ -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)
}
}