Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

MultiNode Integration: TOML Configurations #844

Merged
merged 49 commits into from
Oct 4, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
49 commits
Select commit Hold shift + click to select a range
60673aa
MultiNode integration setup
DylanTinianov Aug 20, 2024
dc85772
Merge branch 'develop' into BCI-2835-integrate-multinode
DylanTinianov Aug 27, 2024
e6683e6
Merge branch 'develop' into BCI-2835-integrate-multinode
DylanTinianov Sep 3, 2024
1c68485
Update MultiNode files
DylanTinianov Sep 3, 2024
c44a6ce
Add MultiNode flag
DylanTinianov Sep 3, 2024
64db86a
Remove internal dependency
DylanTinianov Sep 3, 2024
f7c1bc9
Fix build
DylanTinianov Sep 3, 2024
9e91b47
Fix import cycle
DylanTinianov Sep 4, 2024
354dc50
tidy
DylanTinianov Sep 4, 2024
60c3352
Update client_test.go
DylanTinianov Sep 4, 2024
dcec343
Merge branch 'develop' into BCI-2835-integrate-multinode
DylanTinianov Sep 4, 2024
8e2306b
lint
DylanTinianov Sep 4, 2024
b8d6755
Fix duplicate metrics
DylanTinianov Sep 4, 2024
2cb4d77
Add chain multinode flag
DylanTinianov Sep 5, 2024
0b33b1f
Extend client
DylanTinianov Sep 6, 2024
95e1c9a
Add defaults
DylanTinianov Sep 6, 2024
6641bc9
Merge branch 'develop' into BCI-2835-integrate-multinode
DylanTinianov Sep 6, 2024
d8d312c
Address comments
DylanTinianov Sep 10, 2024
aa3c068
Merge branch 'develop' into BCI-2835-integrate-multinode
DylanTinianov Sep 10, 2024
3c3756e
lint
DylanTinianov Sep 12, 2024
7c8b55d
Merge branch 'develop' into BCI-2835-integrate-multinode
DylanTinianov Sep 12, 2024
2521670
Fix lint overflow issues
DylanTinianov Sep 12, 2024
5b5cfd6
Update transaction_sender.go
DylanTinianov Sep 12, 2024
690f812
Fix lint
DylanTinianov Sep 12, 2024
fd3823b
Validate node config
DylanTinianov Sep 12, 2024
4bf96b7
Update toml.go
DylanTinianov Sep 12, 2024
c1b83a5
Add SendOnly nodes
DylanTinianov Sep 18, 2024
c58dbc8
Merge branch 'BCI-2835-integrate-multinode' into BCFR-302-multinode-toml
DylanTinianov Sep 18, 2024
e0231b0
Use pointers on config
DylanTinianov Sep 18, 2024
0122a5f
Fix conflicts
DylanTinianov Sep 18, 2024
8aa39f6
Use test context
DylanTinianov Sep 19, 2024
0e5a35e
Merge branch 'BCI-2835-integrate-multinode' into BCFR-302-multinode-toml
DylanTinianov Sep 19, 2024
b5ff16d
Use configured selection mode
DylanTinianov Sep 19, 2024
1b3a101
Set defaults
DylanTinianov Sep 24, 2024
b7cc350
Merge branch 'develop' into BCFR-302-multinode-toml
DylanTinianov Sep 24, 2024
0afd8da
lint
DylanTinianov Sep 24, 2024
b99b90c
Add nil check
DylanTinianov Sep 24, 2024
d7203f8
Merge branch 'develop' into BCFR-302-multinode-toml
DylanTinianov Sep 27, 2024
98b0e9d
Update multinode.go
DylanTinianov Sep 27, 2024
0621cf4
Merge branch 'develop' into BCFR-302-multinode-toml
DylanTinianov Oct 1, 2024
fd0cc16
Add comments
DylanTinianov Oct 1, 2024
75b8f02
Merge branch 'BCFR-302-multinode-toml' of https://github.com/smartcon…
DylanTinianov Oct 1, 2024
456be6c
Update multinode.go
DylanTinianov Oct 1, 2024
ad70b8a
Wrap multinode config
DylanTinianov Oct 1, 2024
99149a4
Merge branch 'develop' into BCFR-302-multinode-toml
DylanTinianov Oct 1, 2024
e628945
Fix imports
DylanTinianov Oct 1, 2024
3b497ef
Update .golangci.yml
DylanTinianov Oct 1, 2024
fd157da
Use MultiNode config
DylanTinianov Oct 2, 2024
44d64b1
Merge branch 'develop' into BCFR-302-multinode-toml
DylanTinianov Oct 4, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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(),
DylanTinianov marked this conversation as resolved.
Show resolved Hide resolved
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
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what was the result of checking for shareable config?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Did not end up using any shared configs.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would be nice to reuse the config across different chains. The EVM case might be a bit complex, but for new integrations like Aptos/Tron, it does not make sense to duplicate this code.
Could you create a follow up ticket to address that?

Copy link
Contributor Author

@DylanTinianov DylanTinianov Oct 4, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah sounds good, we can extract this to the framework repo when doing the extraction.

// 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
Loading