mirror of
https://github.com/OffchainLabs/prysm.git
synced 2026-02-12 05:54:57 -05:00
**What type of PR is this?** Other **What does this PR do? Why is it needed?** Follow up to https://github.com/OffchainLabs/prysm/pull/16215 this pr improves logging, fixes stuttering in package naming, adds additional unit tests, and deduplicates fallback node code. **Which issues(s) does this PR fix?** fixes a potential race if reconnecting to the same host very quickly which has a stale connection still. **Other notes for review** **Acknowledgements** - [x] I have read [CONTRIBUTING.md](https://github.com/prysmaticlabs/prysm/blob/develop/CONTRIBUTING.md). - [x] I have included a uniquely named [changelog fragment file](https://github.com/prysmaticlabs/prysm/blob/develop/CONTRIBUTING.md#maintaining-changelogmd). - [x] I have added a description with sufficient context for reviewers to understand this PR. - [x] I have tested that my changes work as expected and I added a testing plan to the PR description (if applicable).
67 lines
1.8 KiB
Go
67 lines
1.8 KiB
Go
package fallback
|
|
|
|
import (
|
|
"context"
|
|
|
|
"github.com/sirupsen/logrus"
|
|
)
|
|
|
|
// HostProvider is the subset of connection-provider methods that EnsureReady
|
|
// needs. Both grpc.GrpcConnectionProvider and rest.RestConnectionProvider
|
|
// satisfy this interface.
|
|
type HostProvider interface {
|
|
Hosts() []string
|
|
CurrentHost() string
|
|
SwitchHost(index int) error
|
|
}
|
|
|
|
// ReadyChecker can report whether the current endpoint is ready.
|
|
// iface.NodeClient satisfies this implicitly.
|
|
type ReadyChecker interface {
|
|
IsReady(ctx context.Context) bool
|
|
}
|
|
|
|
// EnsureReady iterates through the configured hosts and returns true as soon as
|
|
// one responds as ready. It starts from the provider's current host and wraps
|
|
// around using modular arithmetic, performing failover when a host is not ready.
|
|
func EnsureReady(ctx context.Context, provider HostProvider, checker ReadyChecker) bool {
|
|
hosts := provider.Hosts()
|
|
numHosts := len(hosts)
|
|
startingHost := provider.CurrentHost()
|
|
var attemptedHosts []string
|
|
|
|
// Find current index
|
|
currentIdx := 0
|
|
for i, h := range hosts {
|
|
if h == startingHost {
|
|
currentIdx = i
|
|
break
|
|
}
|
|
}
|
|
|
|
for i := range numHosts {
|
|
if checker.IsReady(ctx) {
|
|
if len(attemptedHosts) > 0 {
|
|
log.WithFields(logrus.Fields{
|
|
"previous": startingHost,
|
|
"current": provider.CurrentHost(),
|
|
"tried": attemptedHosts,
|
|
}).Info("Switched to responsive beacon node")
|
|
}
|
|
return true
|
|
}
|
|
attemptedHosts = append(attemptedHosts, provider.CurrentHost())
|
|
|
|
// Try next host if not the last iteration
|
|
if i < numHosts-1 {
|
|
nextIdx := (currentIdx + i + 1) % numHosts
|
|
if err := provider.SwitchHost(nextIdx); err != nil {
|
|
log.WithError(err).Error("Failed to switch host")
|
|
}
|
|
}
|
|
}
|
|
|
|
log.WithField("tried", attemptedHosts).Warn("No responsive beacon node found")
|
|
return false
|
|
}
|