[#1213] morph/client: Remember last succesfully used client

Prevent attemts to connect to the failing endpoint multiple times.
This speeds up all invocations a bit and removes excessive error logs.

Signed-off-by: Evgenii Stratonikov <evgeniy@nspcc.ru>
This commit is contained in:
Evgenii Stratonikov 2022-04-01 16:09:37 +03:00 committed by Alex Vanin
parent 9bbb136e4a
commit 923db59149

View file

@ -16,8 +16,11 @@ type multiClient struct {
sharedNotary *notary // notary config needed for single client construction sharedNotary *notary // notary config needed for single client construction
endpoints []string endpoints []string
clientsMtx sync.Mutex clientsMtx sync.RWMutex
clients map[string]*Client // lastSuccess is an index in endpoints array relating to a last
// used endpoint.
lastSuccess int
clients map[string]*Client
} }
// createForAddress creates single Client instance using provided endpoint. // createForAddress creates single Client instance using provided endpoint.
@ -58,29 +61,32 @@ func (x *multiClient) createForAddress(addr string) (*Client, error) {
return c, nil return c, nil
} }
// iterateClients executes f on each client until nil error is returned.
// When nil error is returned, lastSuccess field is updated.
// The iteration order is non-deterministic and shouldn't be relied upon.
func (x *multiClient) iterateClients(f func(*Client) error) error { func (x *multiClient) iterateClients(f func(*Client) error) error {
var ( var (
firstErr error firstErr error
err error err error
) )
for i := range x.endpoints { x.clientsMtx.RLock()
select { start := x.lastSuccess
case <-x.cfg.ctx.Done(): x.clientsMtx.RUnlock()
return x.cfg.ctx.Err()
default:
}
x.clientsMtx.Lock() for i := 0; i < len(x.endpoints); i++ {
c, cached := x.clients[x.endpoints[i]] index := (start + i) % len(x.endpoints)
x.clientsMtx.Unlock()
x.clientsMtx.RLock()
c, cached := x.clients[x.endpoints[index]]
x.clientsMtx.RUnlock()
if !cached { if !cached {
c, err = x.createForAddress(x.endpoints[i]) c, err = x.createForAddress(x.endpoints[index])
} }
if !cached && err != nil { if !cached && err != nil {
x.cfg.logger.Error("could not open morph client connection", x.cfg.logger.Error("could not open morph client connection",
zap.String("endpoint", x.endpoints[i]), zap.String("endpoint", x.endpoints[index]),
zap.String("err", err.Error()), zap.String("err", err.Error()),
) )
} else { } else {
@ -88,6 +94,11 @@ func (x *multiClient) iterateClients(f func(*Client) error) error {
} }
if err == nil { if err == nil {
if i != 0 {
x.clientsMtx.Lock()
x.lastSuccess = index
x.clientsMtx.Unlock()
}
return nil return nil
} }