From 8d240e9b4c7177d5b19fc68c406b8fec127a7ef5 Mon Sep 17 00:00:00 2001 From: c9s Date: Tue, 11 Apr 2023 18:21:40 +0800 Subject: [PATCH] maxapi: improve nonce update with retry --- pkg/exchange/max/maxapi/restapi.go | 47 +++++++++++++++++++++++------- pkg/util/backoff/general.go | 1 + 2 files changed, 38 insertions(+), 10 deletions(-) diff --git a/pkg/exchange/max/maxapi/restapi.go b/pkg/exchange/max/maxapi/restapi.go index 5e0deeead..1c2b1773a 100644 --- a/pkg/exchange/max/maxapi/restapi.go +++ b/pkg/exchange/max/maxapi/restapi.go @@ -23,6 +23,7 @@ import ( log "github.com/sirupsen/logrus" "github.com/c9s/bbgo/pkg/util" + "github.com/c9s/bbgo/pkg/util/backoff" "github.com/c9s/bbgo/pkg/version" ) @@ -147,16 +148,41 @@ func (c *RestClient) Auth(key string, secret string) *RestClient { return c } -func (c *RestClient) initNonce() { - var clientTime = time.Now() - var err error - serverTimestamp, err = c.PublicService.Timestamp() - if err != nil { - logger.WithError(err).Panic("failed to sync timestamp with max") - } +func (c *RestClient) queryAndUpdateServerTimestamp(ctx context.Context) { + for { + select { + case <-ctx.Done(): + return - timeOffset = serverTimestamp - clientTime.Unix() - logger.Infof("loaded max server timestamp: %d offset=%d", serverTimestamp, timeOffset) + default: + op := func() error { + serverTs, err := c.PublicService.Timestamp() + if err != nil { + return err + } + if serverTs == 0 { + return errors.New("unexpected zero server timestamp") + } + + clientTime := time.Now() + offset := serverTs - clientTime.Unix() + + atomic.StoreInt64(&serverTimestamp, serverTs) + atomic.StoreInt64(&timeOffset, offset) + + logger.Infof("loaded max server timestamp: %d offset=%d", serverTimestamp, offset) + return nil + } + + if err := backoff.RetryGeneral(ctx, op); err != nil { + logger.WithError(err).Error("unable to sync timestamp with max") + } + } + } +} + +func (c *RestClient) initNonce() { + go c.queryAndUpdateServerTimestamp(context.Background()) } func (c *RestClient) getNonce() int64 { @@ -164,7 +190,8 @@ func (c *RestClient) getNonce() int64 { // nonce 與伺服器的時間差不得超過正負30秒,每個 nonce 只能使用一次。 var seconds = time.Now().Unix() var rc = atomic.AddInt64(&reqCount, 1) - return (seconds+timeOffset)*1000 - 1 + int64(math.Mod(float64(rc), 1000.0)) + var offset = atomic.LoadInt64(&timeOffset) + return (seconds+offset)*1000 - 1 + int64(math.Mod(float64(rc), 1000.0)) } func (c *RestClient) NewAuthenticatedRequest(ctx context.Context, m string, refURL string, params url.Values, payload interface{}) (*http.Request, error) { diff --git a/pkg/util/backoff/general.go b/pkg/util/backoff/general.go index 968acd8ef..e91da06b3 100644 --- a/pkg/util/backoff/general.go +++ b/pkg/util/backoff/general.go @@ -8,6 +8,7 @@ import ( var MaxRetries uint64 = 101 +// RetryGeneral retries operation with max retry times 101 and with the exponential backoff func RetryGeneral(ctx context.Context, op backoff.Operation) (err error) { err = backoff.Retry(op, backoff.WithContext( backoff.WithMaxRetries(