Skip to content

Commit

Permalink
MultiNode Integration: TOML Configurations (#844)
Browse files Browse the repository at this point in the history
* MultiNode integration setup

* Update MultiNode files

* Add MultiNode flag

* Remove internal dependency

* Fix build

* Fix import cycle

* tidy

* Update client_test.go

* lint

* Fix duplicate metrics

* Add chain multinode flag

* Extend client

* Add defaults

* Address comments

* lint

* Fix lint overflow issues

* Update transaction_sender.go

* Fix lint

* Validate node config

* Update toml.go

* Add SendOnly nodes

* Use pointers on config

* Use test context

* Use configured selection mode

* Set defaults

* lint

* Add nil check

* Update multinode.go

* Add comments

* Update multinode.go

* Wrap multinode config

* Fix imports

* Update .golangci.yml

* Use MultiNode config
  • Loading branch information
DylanTinianov authored Oct 4, 2024
1 parent c8e4c1f commit 3f52bbc
Show file tree
Hide file tree
Showing 3 changed files with 181 additions and 65 deletions.
14 changes: 7 additions & 7 deletions pkg/solana/chain.go
Original file line number Diff line number Diff line change
Expand Up @@ -230,10 +230,10 @@ func newChain(id string, cfg *config.TOMLConfig, ks loop.Keystore, lggr logger.L
clientCache: map[string]*verifiedCachedClient{},
}

if cfg.MultiNodeEnabled() {
if cfg.MultiNode.Enabled() {
chainFamily := "solana"

mnCfg := cfg.MultiNodeConfig()
mnCfg := &cfg.MultiNode

var nodes []mn.Node[mn.StringID, *client.Client]
var sendOnlyNodes []mn.SendOnlyNode[mn.StringID, *client.Client]
Expand All @@ -258,8 +258,8 @@ func newChain(id string, cfg *config.TOMLConfig, ks loop.Keystore, lggr logger.L

multiNode := mn.NewMultiNode[mn.StringID, *client.Client](
lggr,
mn.NodeSelectionModeRoundRobin,
0,
mnCfg.SelectionMode(),
mnCfg.LeaseDuration(),
nodes,
sendOnlyNodes,
mn.StringID(id),
Expand Down Expand Up @@ -398,7 +398,7 @@ func (c *chain) ChainID() string {

// getClient returns a client, randomly selecting one from available and valid nodes
func (c *chain) getClient() (client.ReaderWriter, error) {
if c.cfg.MultiNodeEnabled() {
if c.cfg.MultiNode.Enabled() {
return c.multiNode.SelectRPC()
}

Expand Down Expand Up @@ -482,7 +482,7 @@ func (c *chain) Start(ctx context.Context) error {
c.lggr.Debug("Starting balance monitor")
var ms services.MultiStart
startAll := []services.StartClose{c.txm, c.balanceMonitor}
if c.cfg.MultiNodeEnabled() {
if c.cfg.MultiNode.Enabled() {
c.lggr.Debug("Starting multinode")
startAll = append(startAll, c.multiNode, c.txSender)
}
Expand All @@ -496,7 +496,7 @@ func (c *chain) Close() error {
c.lggr.Debug("Stopping txm")
c.lggr.Debug("Stopping balance monitor")
closeAll := []io.Closer{c.txm, c.balanceMonitor}
if c.cfg.MultiNodeEnabled() {
if c.cfg.MultiNode.Enabled() {
c.lggr.Debug("Stopping multinode")
closeAll = append(closeAll, c.multiNode, c.txSender)
}
Expand Down
223 changes: 171 additions & 52 deletions pkg/solana/config/multinode.go
Original file line number Diff line number Diff line change
@@ -1,87 +1,206 @@
package config

import "time"
import (
"time"

"github.com/smartcontractkit/chainlink-common/pkg/config"

mn "github.com/smartcontractkit/chainlink-solana/pkg/solana/client/multinode"
)

// MultiNodeConfig is a wrapper to provide required functions while keeping configs Public
type MultiNodeConfig struct {
MultiNode
}

type MultiNode struct {
// TODO: Determine current config overlap https://smartcontract-it.atlassian.net/browse/BCI-4065
// Feature flag
multiNodeEnabled bool
Enabled *bool

// Node Configs
pollFailureThreshold uint32
pollInterval time.Duration
selectionMode string
syncThreshold uint32
nodeIsSyncingEnabled bool
finalizedBlockPollInterval time.Duration
enforceRepeatableRead bool
deathDeclarationDelay time.Duration
PollFailureThreshold *uint32
PollInterval *config.Duration
SelectionMode *string
SyncThreshold *uint32
NodeIsSyncingEnabled *bool
LeaseDuration *config.Duration
FinalizedBlockPollInterval *config.Duration
EnforceRepeatableRead *bool
DeathDeclarationDelay *config.Duration

// Chain Configs
nodeNoNewHeadsThreshold time.Duration
noNewFinalizedHeadsThreshold time.Duration
finalityDepth uint32
finalityTagEnabled bool
finalizedBlockOffset uint32
NodeNoNewHeadsThreshold *config.Duration
NoNewFinalizedHeadsThreshold *config.Duration
FinalityDepth *uint32
FinalityTagEnabled *bool
FinalizedBlockOffset *uint32
}

func (c *MultiNode) MultiNodeEnabled() bool {
return c.multiNodeEnabled
func (c *MultiNodeConfig) Enabled() bool {
return c.MultiNode.Enabled != nil && *c.MultiNode.Enabled
}

func (c *MultiNode) PollFailureThreshold() uint32 {
return c.pollFailureThreshold
func (c *MultiNodeConfig) PollFailureThreshold() uint32 {
return *c.MultiNode.PollFailureThreshold
}

func (c *MultiNode) PollInterval() time.Duration {
return c.pollInterval
func (c *MultiNodeConfig) PollInterval() time.Duration {
return c.MultiNode.PollInterval.Duration()
}

func (c *MultiNode) SelectionMode() string {
return c.selectionMode
}
func (c *MultiNodeConfig) SelectionMode() string {
return *c.MultiNode.SelectionMode
}

func (c *MultiNode) SyncThreshold() uint32 {
return c.syncThreshold
func (c *MultiNodeConfig) SyncThreshold() uint32 {
return *c.MultiNode.SyncThreshold
}

func (c *MultiNode) NodeIsSyncingEnabled() bool {
return c.nodeIsSyncingEnabled
func (c *MultiNodeConfig) NodeIsSyncingEnabled() bool {
return *c.MultiNode.NodeIsSyncingEnabled
}

func (c *MultiNode) FinalizedBlockPollInterval() time.Duration {
return c.finalizedBlockPollInterval
}
func (c *MultiNodeConfig) LeaseDuration() time.Duration { return c.MultiNode.LeaseDuration.Duration() }

func (c *MultiNode) EnforceRepeatableRead() bool {
return c.enforceRepeatableRead
func (c *MultiNodeConfig) FinalizedBlockPollInterval() time.Duration {
return c.MultiNode.FinalizedBlockPollInterval.Duration()
}

func (c *MultiNode) DeathDeclarationDelay() time.Duration {
return c.deathDeclarationDelay
}
func (c *MultiNodeConfig) EnforceRepeatableRead() bool { return *c.MultiNode.EnforceRepeatableRead }

func (c *MultiNode) NodeNoNewHeadsThreshold() time.Duration {
return c.nodeNoNewHeadsThreshold
func (c *MultiNodeConfig) DeathDeclarationDelay() time.Duration {
return c.MultiNode.DeathDeclarationDelay.Duration()
}

func (c *MultiNode) NoNewFinalizedHeadsThreshold() time.Duration {
return c.noNewFinalizedHeadsThreshold
func (c *MultiNodeConfig) NodeNoNewHeadsThreshold() time.Duration {
return c.MultiNode.NodeNoNewHeadsThreshold.Duration()
}

func (c *MultiNode) FinalityDepth() uint32 {
return c.finalityDepth
func (c *MultiNodeConfig) NoNewFinalizedHeadsThreshold() time.Duration {
return c.MultiNode.NoNewFinalizedHeadsThreshold.Duration()
}

func (c *MultiNode) FinalityTagEnabled() bool {
return c.finalityTagEnabled
}
func (c *MultiNodeConfig) FinalityDepth() uint32 { return *c.MultiNode.FinalityDepth }

func (c *MultiNode) FinalizedBlockOffset() uint32 {
return c.finalizedBlockOffset
}
func (c *MultiNodeConfig) FinalityTagEnabled() bool { return *c.MultiNode.FinalityTagEnabled }

func (c *MultiNodeConfig) FinalizedBlockOffset() uint32 { return *c.MultiNode.FinalizedBlockOffset }

func (c *MultiNodeConfig) SetDefaults() {
// MultiNode is disabled as it's not fully implemented yet: BCFR-122
if c.MultiNode.Enabled == nil {
c.MultiNode.Enabled = ptr(false)
}

/* Node Configs */
// Failure threshold for polling set to 5 to tolerate some polling failures before taking action.
if c.MultiNode.PollFailureThreshold == nil {
c.MultiNode.PollFailureThreshold = ptr(uint32(5))
}
// Poll interval is set to 10 seconds to ensure timely updates while minimizing resource usage.
if c.MultiNode.PollInterval == nil {
c.MultiNode.PollInterval = config.MustNewDuration(10 * time.Second)
}
// Selection mode defaults to priority level to enable using node priorities
if c.MultiNode.SelectionMode == nil {
c.MultiNode.SelectionMode = ptr(mn.NodeSelectionModePriorityLevel)
}
// The sync threshold is set to 5 to allow for some flexibility in node synchronization before considering it out of sync.
if c.MultiNode.SyncThreshold == nil {
c.MultiNode.SyncThreshold = ptr(uint32(5))
}
// Lease duration is set to 1 minute by default to allow node locks for a reasonable amount of time.
if c.MultiNode.LeaseDuration == nil {
c.MultiNode.LeaseDuration = config.MustNewDuration(time.Minute)
}
// Node syncing is not relevant for Solana and is disabled by default.
if c.MultiNode.NodeIsSyncingEnabled == nil {
c.MultiNode.NodeIsSyncingEnabled = ptr(false)
}
// The finalized block polling interval is set to 5 seconds to ensure timely updates while minimizing resource usage.
if c.MultiNode.FinalizedBlockPollInterval == nil {
c.MultiNode.FinalizedBlockPollInterval = config.MustNewDuration(5 * time.Second)
}
// Repeatable read guarantee should be enforced by default.
if c.MultiNode.EnforceRepeatableRead == nil {
c.MultiNode.EnforceRepeatableRead = ptr(true)
}
// The delay before declaring a node dead is set to 10 seconds to give nodes time to recover from temporary issues.
if c.MultiNode.DeathDeclarationDelay == nil {
c.MultiNode.DeathDeclarationDelay = config.MustNewDuration(10 * time.Second)
}

func (c *MultiNode) SetDefaults() {
// TODO: Set defaults for MultiNode config https://smartcontract-it.atlassian.net/browse/BCI-4065
c.multiNodeEnabled = false
/* Chain Configs */
// Threshold for no new heads is set to 10 seconds, assuming that heads should update at a reasonable pace.
if c.MultiNode.NodeNoNewHeadsThreshold == nil {
c.MultiNode.NodeNoNewHeadsThreshold = config.MustNewDuration(10 * time.Second)
}
// Similar to heads, finalized heads should be updated within 10 seconds.
if c.MultiNode.NoNewFinalizedHeadsThreshold == nil {
c.MultiNode.NoNewFinalizedHeadsThreshold = config.MustNewDuration(10 * time.Second)
}
// Finality tags are used in Solana and enabled by default.
if c.MultiNode.FinalityTagEnabled == nil {
c.MultiNode.FinalityTagEnabled = ptr(true)
}
// Finality depth will not be used since finality tags are enabled.
if c.MultiNode.FinalityDepth == nil {
c.MultiNode.FinalityDepth = ptr(uint32(0))
}
// Finalized block offset will not be used since finality tags are enabled.
if c.MultiNode.FinalizedBlockOffset == nil {
c.MultiNode.FinalizedBlockOffset = ptr(uint32(0))
}
}

func (c *MultiNodeConfig) SetFrom(f *MultiNodeConfig) {
if f.MultiNode.Enabled != nil {
c.MultiNode.Enabled = f.MultiNode.Enabled
}

// Node Configs
if f.MultiNode.PollFailureThreshold != nil {
c.MultiNode.PollFailureThreshold = f.MultiNode.PollFailureThreshold
}
if f.MultiNode.PollInterval != nil {
c.MultiNode.PollInterval = f.MultiNode.PollInterval
}
if f.MultiNode.SelectionMode != nil {
c.MultiNode.SelectionMode = f.MultiNode.SelectionMode
}
if f.MultiNode.SyncThreshold != nil {
c.MultiNode.SyncThreshold = f.MultiNode.SyncThreshold
}
if f.MultiNode.NodeIsSyncingEnabled != nil {
c.MultiNode.NodeIsSyncingEnabled = f.MultiNode.NodeIsSyncingEnabled
}
if f.MultiNode.LeaseDuration != nil {
c.MultiNode.LeaseDuration = f.MultiNode.LeaseDuration
}
if f.MultiNode.FinalizedBlockPollInterval != nil {
c.MultiNode.FinalizedBlockPollInterval = f.MultiNode.FinalizedBlockPollInterval
}
if f.MultiNode.EnforceRepeatableRead != nil {
c.MultiNode.EnforceRepeatableRead = f.MultiNode.EnforceRepeatableRead
}
if f.MultiNode.DeathDeclarationDelay != nil {
c.MultiNode.DeathDeclarationDelay = f.MultiNode.DeathDeclarationDelay
}

// Chain Configs
if f.MultiNode.NodeNoNewHeadsThreshold != nil {
c.MultiNode.NodeNoNewHeadsThreshold = f.MultiNode.NodeNoNewHeadsThreshold
}
if f.MultiNode.NoNewFinalizedHeadsThreshold != nil {
c.MultiNode.NoNewFinalizedHeadsThreshold = f.MultiNode.NoNewFinalizedHeadsThreshold
}
if f.MultiNode.FinalityDepth != nil {
c.MultiNode.FinalityDepth = f.MultiNode.FinalityDepth
}
if f.MultiNode.FinalityTagEnabled != nil {
c.MultiNode.FinalityTagEnabled = f.MultiNode.FinalityTagEnabled
}
if f.MultiNode.FinalizedBlockOffset != nil {
c.MultiNode.FinalizedBlockOffset = f.MultiNode.FinalizedBlockOffset
}
}
9 changes: 3 additions & 6 deletions pkg/solana/config/toml.go
Original file line number Diff line number Diff line change
Expand Up @@ -113,8 +113,8 @@ type TOMLConfig struct {
// Do not access directly, use [IsEnabled]
Enabled *bool
Chain
MultiNode
Nodes Nodes
MultiNode MultiNodeConfig
Nodes Nodes
}

func (c *TOMLConfig) IsEnabled() bool {
Expand All @@ -130,6 +130,7 @@ func (c *TOMLConfig) SetFrom(f *TOMLConfig) {
}
setFromChain(&c.Chain, &f.Chain)
c.Nodes.SetFrom(&f.Nodes)
c.MultiNode.SetFrom(&f.MultiNode)
}

func setFromChain(c, f *Chain) {
Expand Down Expand Up @@ -285,10 +286,6 @@ func (c *TOMLConfig) ListNodes() Nodes {
return c.Nodes
}

func (c *TOMLConfig) MultiNodeConfig() *MultiNode {
return &c.MultiNode
}

func (c *TOMLConfig) SetDefaults() {
c.Chain.SetDefaults()
c.MultiNode.SetDefaults()
Expand Down

0 comments on commit 3f52bbc

Please sign in to comment.