chore: refresh gramsrv public release
This commit is contained in:
parent
75cebe8dbf
commit
70b6820474
1274 changed files with 378751 additions and 59919 deletions
|
|
@ -10,21 +10,11 @@ import (
|
|||
"telesrv/internal/store"
|
||||
)
|
||||
|
||||
// PtsAllocator 用 Redis INCR 分配账号级 pts,Redis 丢失时从 PG durable log 恢复当前最大 pts。
|
||||
type PtsAllocator struct {
|
||||
counter counterAllocator
|
||||
}
|
||||
|
||||
// BoxIDAllocator 用 Redis INCR 分配 owner 视角的 message box id。
|
||||
type BoxIDAllocator struct {
|
||||
counter counterAllocator
|
||||
}
|
||||
|
||||
// ChannelPtsAllocator 用 Redis INCR 分配 channel 维度 pts。
|
||||
type ChannelPtsAllocator struct {
|
||||
counter counterAllocator
|
||||
}
|
||||
|
||||
// ChannelIDAllocator 用 Redis INCR 分配全局 channel/supergroup id。
|
||||
type ChannelIDAllocator struct {
|
||||
counter counterAllocator
|
||||
|
|
@ -35,6 +25,11 @@ type ChannelMessageIDAllocator struct {
|
|||
counter counterAllocator
|
||||
}
|
||||
|
||||
// SecretChatIDAllocator 用 Redis INCR 分配全局 secret chat id(int32 量级)。
|
||||
type SecretChatIDAllocator struct {
|
||||
counter counterAllocator
|
||||
}
|
||||
|
||||
type counterAllocator struct {
|
||||
c *redis.Client
|
||||
source store.CounterSource
|
||||
|
|
@ -51,14 +46,6 @@ if current then
|
|||
return redis.call("INCR", KEYS[1])
|
||||
end
|
||||
return -1
|
||||
`)
|
||||
|
||||
counterNextByScript = redis.NewScript(`
|
||||
local current = redis.call("GET", KEYS[1])
|
||||
if current then
|
||||
return redis.call("INCRBY", KEYS[1], ARGV[1])
|
||||
end
|
||||
return -1
|
||||
`)
|
||||
|
||||
counterRecoverCurrentScript = redis.NewScript(`
|
||||
|
|
@ -78,25 +65,24 @@ end
|
|||
return redis.call("INCR", KEYS[1])
|
||||
`)
|
||||
|
||||
counterRecoverNextByScript = redis.NewScript(`
|
||||
counterNextAtLeastScript = redis.NewScript(`
|
||||
local current = redis.call("GET", KEYS[1])
|
||||
if not current then
|
||||
if (not current) or tonumber(current) < tonumber(ARGV[1]) then
|
||||
redis.call("SET", KEYS[1], ARGV[1])
|
||||
end
|
||||
return redis.call("INCRBY", KEYS[1], ARGV[2])
|
||||
return redis.call("INCR", KEYS[1])
|
||||
`)
|
||||
|
||||
counterSetAtLeastScript = redis.NewScript(`
|
||||
local current = redis.call("GET", KEYS[1])
|
||||
if (not current) or tonumber(current) < tonumber(ARGV[1]) then
|
||||
redis.call("SET", KEYS[1], ARGV[1])
|
||||
return tonumber(ARGV[1])
|
||||
end
|
||||
return tonumber(current)
|
||||
`)
|
||||
)
|
||||
|
||||
// NewPtsAllocator 创建 Redis-backed pts allocator。
|
||||
func NewPtsAllocator(c *redis.Client, source store.CounterSource) *PtsAllocator {
|
||||
return &PtsAllocator{counter: counterAllocator{
|
||||
c: c,
|
||||
source: source,
|
||||
key: ptsKey,
|
||||
name: "pts",
|
||||
}}
|
||||
}
|
||||
|
||||
// NewBoxIDAllocator 创建 Redis-backed message box id allocator。
|
||||
func NewBoxIDAllocator(c *redis.Client, source store.CounterSource) *BoxIDAllocator {
|
||||
return &BoxIDAllocator{counter: counterAllocator{
|
||||
|
|
@ -107,16 +93,6 @@ func NewBoxIDAllocator(c *redis.Client, source store.CounterSource) *BoxIDAlloca
|
|||
}}
|
||||
}
|
||||
|
||||
// NewChannelPtsAllocator 创建 Redis-backed channel pts allocator。
|
||||
func NewChannelPtsAllocator(c *redis.Client, source store.CounterSource) *ChannelPtsAllocator {
|
||||
return &ChannelPtsAllocator{counter: counterAllocator{
|
||||
c: c,
|
||||
source: source,
|
||||
key: channelPtsKey,
|
||||
name: "channel_pts",
|
||||
}}
|
||||
}
|
||||
|
||||
// NewChannelIDAllocator 创建 Redis-backed channel id allocator。
|
||||
func NewChannelIDAllocator(c *redis.Client, source store.CounterSource) *ChannelIDAllocator {
|
||||
return &ChannelIDAllocator{counter: counterAllocator{
|
||||
|
|
@ -137,41 +113,32 @@ func NewChannelMessageIDAllocator(c *redis.Client, source store.CounterSource) *
|
|||
}}
|
||||
}
|
||||
|
||||
func ptsKey(userID int64) string {
|
||||
return fmt.Sprintf("counter:pts:{%d}", userID)
|
||||
// NewSecretChatIDAllocator 创建 Redis-backed secret chat id allocator。
|
||||
func NewSecretChatIDAllocator(c *redis.Client, source store.CounterSource) *SecretChatIDAllocator {
|
||||
return &SecretChatIDAllocator{counter: counterAllocator{
|
||||
c: c,
|
||||
source: source,
|
||||
key: secretChatIDKey,
|
||||
name: "secret_chat_id",
|
||||
}}
|
||||
}
|
||||
|
||||
func boxIDKey(userID int64) string {
|
||||
return fmt.Sprintf("counter:box_id:{%d}", userID)
|
||||
}
|
||||
|
||||
func channelPtsKey(channelID int64) string {
|
||||
return fmt.Sprintf("counter:channel_pts:{%d}", channelID)
|
||||
}
|
||||
|
||||
func channelIDKey(_ int64) string {
|
||||
return "counter:channel_id"
|
||||
}
|
||||
|
||||
func secretChatIDKey(_ int64) string {
|
||||
return "counter:secret_chat_id"
|
||||
}
|
||||
|
||||
func channelMessageIDKey(channelID int64) string {
|
||||
return fmt.Sprintf("counter:channel_msg_id:{%d}", channelID)
|
||||
}
|
||||
|
||||
func (a *PtsAllocator) NextPts(ctx context.Context, userID int64) (int, error) {
|
||||
v, err := a.counter.next(ctx, userID)
|
||||
return int(v), err
|
||||
}
|
||||
|
||||
func (a *PtsAllocator) NextPtsN(ctx context.Context, userID int64, count int) (int, error) {
|
||||
v, err := a.counter.nextBy(ctx, userID, count)
|
||||
return int(v), err
|
||||
}
|
||||
|
||||
func (a *PtsAllocator) CurrentPts(ctx context.Context, userID int64) (int, error) {
|
||||
v, err := a.counter.current(ctx, userID)
|
||||
return int(v), err
|
||||
}
|
||||
|
||||
func (a *BoxIDAllocator) NextBoxID(ctx context.Context, userID int64) (int, error) {
|
||||
v, err := a.counter.next(ctx, userID)
|
||||
return int(v), err
|
||||
|
|
@ -182,29 +149,64 @@ func (a *BoxIDAllocator) CurrentBoxID(ctx context.Context, userID int64) (int, e
|
|||
return int(v), err
|
||||
}
|
||||
|
||||
func (a *ChannelPtsAllocator) NextChannelPts(ctx context.Context, channelID int64) (int, error) {
|
||||
v, err := a.counter.next(ctx, channelID)
|
||||
return int(v), err
|
||||
}
|
||||
|
||||
func (a *ChannelPtsAllocator) NextChannelPtsN(ctx context.Context, channelID int64, count int) (int, error) {
|
||||
v, err := a.counter.nextBy(ctx, channelID, count)
|
||||
return int(v), err
|
||||
}
|
||||
|
||||
func (a *ChannelPtsAllocator) CurrentChannelPts(ctx context.Context, channelID int64) (int, error) {
|
||||
v, err := a.counter.current(ctx, channelID)
|
||||
return int(v), err
|
||||
// BumpBoxIDAtLeast advances the Redis box id counter to at least floor without
|
||||
// allocating a visible id. It is a cold-path self-heal for Redis counters that
|
||||
// lag behind message_boxes after external/dev writes.
|
||||
func (a *BoxIDAllocator) BumpBoxIDAtLeast(ctx context.Context, userID int64, floor int) error {
|
||||
if a.counter.c == nil {
|
||||
return fmt.Errorf("redis box_id counter: nil client")
|
||||
}
|
||||
if _, err := counterSetAtLeastScript.Run(ctx, a.counter.c, []string{boxIDKey(userID)}, floor).Int64(); err != nil {
|
||||
return fmt.Errorf("redis set-at-least box_id counter: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a *ChannelIDAllocator) NextChannelID(ctx context.Context) (int64, error) {
|
||||
return a.counter.next(ctx, 1)
|
||||
}
|
||||
|
||||
// NextChannelIDAtLeast 把计数器至少顶到 floor 后再分配下一个 id。
|
||||
// 用于撞主键自愈:Redis 快照回退或测试 fallback 分配器绕过 Redis 写库
|
||||
// 后,计数器可能落后于 channels 表真实最大 id。
|
||||
func (a *ChannelIDAllocator) NextChannelIDAtLeast(ctx context.Context, floor int64) (int64, error) {
|
||||
if a.counter.c == nil {
|
||||
return 0, fmt.Errorf("redis channel_id counter: nil client")
|
||||
}
|
||||
v, err := counterNextAtLeastScript.Run(ctx, a.counter.c, []string{channelIDKey(1)}, floor).Int64()
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("redis next-at-least channel_id counter: %w", err)
|
||||
}
|
||||
return v, nil
|
||||
}
|
||||
|
||||
func (a *ChannelIDAllocator) CurrentChannelID(ctx context.Context) (int64, error) {
|
||||
return a.counter.current(ctx, 1)
|
||||
}
|
||||
|
||||
func (a *SecretChatIDAllocator) NextSecretChatID(ctx context.Context) (int, error) {
|
||||
v, err := a.counter.next(ctx, 1)
|
||||
return int(v), err
|
||||
}
|
||||
|
||||
// NextSecretChatIDAtLeast 把计数器至少顶到 floor 后再分配下一个 id(撞 chat_id
|
||||
// 主键自愈:Redis 快照回退或外部写库后计数器落后于 secret_chats 表最大 id)。
|
||||
func (a *SecretChatIDAllocator) NextSecretChatIDAtLeast(ctx context.Context, floor int) (int, error) {
|
||||
if a.counter.c == nil {
|
||||
return 0, fmt.Errorf("redis secret_chat_id counter: nil client")
|
||||
}
|
||||
v, err := counterNextAtLeastScript.Run(ctx, a.counter.c, []string{secretChatIDKey(1)}, floor).Int64()
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("redis next-at-least secret_chat_id counter: %w", err)
|
||||
}
|
||||
return int(v), nil
|
||||
}
|
||||
|
||||
func (a *SecretChatIDAllocator) CurrentSecretChatID(ctx context.Context) (int, error) {
|
||||
v, err := a.counter.current(ctx, 1)
|
||||
return int(v), err
|
||||
}
|
||||
|
||||
func (a *ChannelMessageIDAllocator) NextChannelMessageID(ctx context.Context, channelID int64) (int, error) {
|
||||
v, err := a.counter.next(ctx, channelID)
|
||||
return int(v), err
|
||||
|
|
@ -216,24 +218,11 @@ func (a *ChannelMessageIDAllocator) CurrentChannelMessageID(ctx context.Context,
|
|||
}
|
||||
|
||||
func (a counterAllocator) next(ctx context.Context, userID int64) (int64, error) {
|
||||
return a.nextBy(ctx, userID, 1)
|
||||
}
|
||||
|
||||
func (a counterAllocator) nextBy(ctx context.Context, userID int64, count int) (int64, error) {
|
||||
if count <= 0 {
|
||||
return 0, fmt.Errorf("redis next %s counter: invalid count %d", a.name, count)
|
||||
}
|
||||
key, err := a.validatedKey(userID)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
script := counterNextScript
|
||||
args := []any{}
|
||||
if count > 1 {
|
||||
script = counterNextByScript
|
||||
args = append(args, count)
|
||||
}
|
||||
v, err := script.Run(ctx, a.c, []string{key}, args...).Int64()
|
||||
v, err := counterNextScript.Run(ctx, a.c, []string{key}).Int64()
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("redis next %s counter: %w", a.name, err)
|
||||
}
|
||||
|
|
@ -244,13 +233,7 @@ func (a counterAllocator) nextBy(ctx context.Context, userID int64, count int) (
|
|||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
recoverScript := counterRecoverNextScript
|
||||
recoverArgs := []any{recovered}
|
||||
if count > 1 {
|
||||
recoverScript = counterRecoverNextByScript
|
||||
recoverArgs = append(recoverArgs, count)
|
||||
}
|
||||
v, err = recoverScript.Run(ctx, a.c, []string{key}, recoverArgs...).Int64()
|
||||
v, err = counterRecoverNextScript.Run(ctx, a.c, []string{key}, recovered).Int64()
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("redis recover-next %s counter: %w", a.name, err)
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue