forked from TrueCloudLab/frostfs-node
[#1609] config: Allow to prioritize N3 endpoints
Signed-off-by: Evgenii Stratonikov <evgeniy@nspcc.ru>
This commit is contained in:
parent
aed83d1660
commit
7410827db8
15 changed files with 123 additions and 85 deletions
|
@ -26,7 +26,7 @@ func TestAuditResults(t *testing.T) {
|
|||
auditHash, err := util.Uint160DecodeStringLE(sAuditHash)
|
||||
require.NoError(t, err)
|
||||
|
||||
morphClient, err := client.New(key, endpoint)
|
||||
morphClient, err := client.New(key, client.WithEndpoints(client.Endpoint{Address: endpoint}))
|
||||
require.NoError(t, err)
|
||||
|
||||
auditClientWrapper, err := NewFromMorph(morphClient, auditHash, 0)
|
||||
|
|
|
@ -2,6 +2,7 @@ package client
|
|||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"sync"
|
||||
"time"
|
||||
|
@ -35,7 +36,7 @@ type cfg struct {
|
|||
|
||||
signer *transaction.Signer
|
||||
|
||||
extraEndpoints []string
|
||||
endpoints []Endpoint
|
||||
|
||||
singleCli *client.WSClient // neo-go client for single client mode
|
||||
|
||||
|
@ -76,7 +77,7 @@ func defaultConfig() *cfg {
|
|||
// If desired option satisfies the default value, it can be omitted.
|
||||
// If multiple options of the same config value are supplied,
|
||||
// the option with the highest index in the arguments will be used.
|
||||
func New(key *keys.PrivateKey, endpoint string, opts ...Option) (*Client, error) {
|
||||
func New(key *keys.PrivateKey, opts ...Option) (*Client, error) {
|
||||
if key == nil {
|
||||
panic("empty private key")
|
||||
}
|
||||
|
@ -89,6 +90,10 @@ func New(key *keys.PrivateKey, endpoint string, opts ...Option) (*Client, error)
|
|||
opt(cfg)
|
||||
}
|
||||
|
||||
if len(cfg.endpoints) == 0 {
|
||||
return nil, errors.New("no endpoints were provided")
|
||||
}
|
||||
|
||||
cli := &Client{
|
||||
cache: newClientCache(),
|
||||
logger: cfg.logger,
|
||||
|
@ -111,9 +116,8 @@ func New(key *keys.PrivateKey, endpoint string, opts ...Option) (*Client, error)
|
|||
// they will be used in switch process, otherwise
|
||||
// inactive mode will be enabled
|
||||
cli.client = cfg.singleCli
|
||||
cli.endpoints.init(cfg.extraEndpoints)
|
||||
} else {
|
||||
ws, err := newWSClient(*cfg, endpoint)
|
||||
ws, err := newWSClient(*cfg, cfg.endpoints[0].Address)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not create morph client: %w", err)
|
||||
}
|
||||
|
@ -124,8 +128,8 @@ func New(key *keys.PrivateKey, endpoint string, opts ...Option) (*Client, error)
|
|||
}
|
||||
|
||||
cli.client = ws
|
||||
cli.endpoints.init(append([]string{endpoint}, cfg.extraEndpoints...))
|
||||
}
|
||||
cli.endpoints.init(cfg.endpoints)
|
||||
|
||||
go cli.notificationLoop()
|
||||
|
||||
|
@ -206,11 +210,11 @@ func WithSigner(signer *transaction.Signer) Option {
|
|||
}
|
||||
}
|
||||
|
||||
// WithExtraEndpoints returns a client constructor option
|
||||
// WithEndpoints returns a client constructor option
|
||||
// that specifies additional Neo rpc endpoints.
|
||||
func WithExtraEndpoints(endpoints []string) Option {
|
||||
func WithEndpoints(endpoints ...Endpoint) Option {
|
||||
return func(c *cfg) {
|
||||
c.extraEndpoints = append(c.extraEndpoints, endpoints...)
|
||||
c.endpoints = append(c.endpoints, endpoints...)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,69 +1,46 @@
|
|||
package client
|
||||
|
||||
import (
|
||||
"sort"
|
||||
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
// Endpoint represents morph endpoint together with its priority.
|
||||
type Endpoint struct {
|
||||
Address string
|
||||
Priority int
|
||||
}
|
||||
|
||||
type endpoints struct {
|
||||
curr int
|
||||
list []string
|
||||
list []Endpoint
|
||||
}
|
||||
|
||||
func (e *endpoints) init(ee []string) {
|
||||
func (e *endpoints) init(ee []Endpoint) {
|
||||
sort.SliceStable(ee, func(i, j int) bool {
|
||||
return ee[i].Priority > ee[j].Priority
|
||||
})
|
||||
|
||||
e.curr = 0
|
||||
e.list = ee
|
||||
}
|
||||
|
||||
// next returns the next endpoint and its index
|
||||
// to try to connect to.
|
||||
// Returns -1 index if there is no known RPC endpoints.
|
||||
func (e *endpoints) next() (string, int) {
|
||||
if len(e.list) == 0 {
|
||||
return "", -1
|
||||
}
|
||||
|
||||
next := e.curr + 1
|
||||
if next == len(e.list) {
|
||||
next = 0
|
||||
}
|
||||
|
||||
e.curr = next
|
||||
|
||||
return e.list[next], next
|
||||
}
|
||||
|
||||
// current returns an endpoint and its index the Client
|
||||
// is connected to.
|
||||
// Returns -1 index if there is no known RPC endpoints
|
||||
func (e *endpoints) current() (string, int) {
|
||||
if len(e.list) == 0 {
|
||||
return "", -1
|
||||
}
|
||||
|
||||
return e.list[e.curr], e.curr
|
||||
}
|
||||
|
||||
func (c *Client) switchRPC() bool {
|
||||
c.switchLock.Lock()
|
||||
defer c.switchLock.Unlock()
|
||||
|
||||
c.client.Close()
|
||||
|
||||
_, currEndpointIndex := c.endpoints.current()
|
||||
if currEndpointIndex == -1 {
|
||||
// there are no known RPC endpoints to try
|
||||
// to connect to => do not switch
|
||||
return false
|
||||
}
|
||||
|
||||
for {
|
||||
newEndpoint, index := c.endpoints.next()
|
||||
if index == currEndpointIndex {
|
||||
// all the endpoint have been tried
|
||||
// for connection unsuccessfully
|
||||
return false
|
||||
// Iterate endpoints in the order of decreasing priority.
|
||||
// Skip the current endpoint.
|
||||
last := c.endpoints.curr
|
||||
for c.endpoints.curr = range c.endpoints.list {
|
||||
if c.endpoints.curr == last {
|
||||
continue
|
||||
}
|
||||
|
||||
newEndpoint := c.endpoints.list[c.endpoints.curr].Address
|
||||
cli, err := newWSClient(c.cfg, newEndpoint)
|
||||
if err != nil {
|
||||
c.logger.Warn("could not establish connection to the switched RPC node",
|
||||
|
@ -103,6 +80,8 @@ func (c *Client) switchRPC() bool {
|
|||
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func (c *Client) notificationLoop() {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue