fix: update backoff handling to use a factory function for concurrency safety

This commit is contained in:
krau
2026-06-25 10:22:31 +08:00
parent 8059e27978
commit 88b170acaa
2 changed files with 16 additions and 7 deletions
+1 -1
View File
@@ -15,7 +15,7 @@ import (
// https://github.com/iyear/tdl/blob/master/core/tclient/tclient.go // https://github.com/iyear/tdl/blob/master/core/tclient/tclient.go
func NewDefaultMiddlewares(ctx context.Context, timeout time.Duration) []telegram.Middleware { func NewDefaultMiddlewares(ctx context.Context, timeout time.Duration) []telegram.Middleware {
return []telegram.Middleware{ return []telegram.Middleware{
recovery.New(ctx, newBackoff(timeout)), recovery.New(ctx, func() backoff.BackOff { return newBackoff(timeout) }),
retry.New(config.C().Telegram.RpcRetry), retry.New(config.C().Telegram.RpcRetry),
floodwait.NewSimpleWaiter(), floodwait.NewSimpleWaiter(),
} }
+15 -6
View File
@@ -14,19 +14,28 @@ import (
) )
type recovery struct { type recovery struct {
ctx context.Context ctx context.Context
backoff backoff.BackOff newBackoff func() backoff.BackOff
} }
func New(ctx context.Context, backoff backoff.BackOff) telegram.Middleware { // New returns a recovery middleware.
//
// newBackoff is a factory that must return a fresh backoff.BackOff on every call: backoff implementations in
// cenkalti/backoff/v4 (notably ExponentialBackOff) are not safe for concurrent
// use, and the Telegram client invokes RPCs from many goroutines in parallel.
//
// Sharing a single instance corrupts its internal counters, breaks the
// exponential interval, and defeats MaxElapsedTime - see issue #218.
func New(ctx context.Context, newBackoff func() backoff.BackOff) telegram.Middleware {
return &recovery{ return &recovery{
ctx: ctx, ctx: ctx,
backoff: backoff, newBackoff: newBackoff,
} }
} }
func (r *recovery) Handle(next tg.Invoker) telegram.InvokeFunc { func (r *recovery) Handle(next tg.Invoker) telegram.InvokeFunc {
return func(ctx context.Context, input bin.Encoder, output bin.Decoder) error { return func(ctx context.Context, input bin.Encoder, output bin.Decoder) error {
b := r.newBackoff()
return backoff.RetryNotify(func() error { return backoff.RetryNotify(func() error {
if err := next.Invoke(ctx, input, output); err != nil { if err := next.Invoke(ctx, input, output); err != nil {
@@ -38,7 +47,7 @@ func (r *recovery) Handle(next tg.Invoker) telegram.InvokeFunc {
} }
return nil return nil
}, r.backoff, func(err error, duration time.Duration) { }, b, func(err error, duration time.Duration) {
log.FromContext(ctx).Debug("Wait for connection recovery", "error", err, "duration", duration) log.FromContext(ctx).Debug("Wait for connection recovery", "error", err, "duration", duration)
}) })
} }