Skip to content

Commit 1c2bac5

Browse files
authored
Merge pull request #121 from blinklabs-io/feat/chainsync-input-auto-reconnect
feat: auto-reconnect on connection failure in chainsync input
2 parents d879c4a + 67dc321 commit 1c2bac5

File tree

6 files changed

+57
-11
lines changed

6 files changed

+57
-11
lines changed

cmd/snek/main.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -149,15 +149,15 @@ func main() {
149149

150150
// Start API after plugins are configured
151151
if err := apiInstance.Start(); err != nil {
152-
logger.Fatalf("failed to start API: %s\n", err)
152+
logger.Fatalf("failed to start API: %s", err)
153153
}
154154

155155
// Start pipeline and wait for error
156156
if err := pipe.Start(); err != nil {
157-
logger.Fatalf("failed to start pipeline: %s\n", err)
157+
logger.Fatalf("failed to start pipeline: %s", err)
158158
}
159159
err, ok := <-pipe.ErrorChan()
160160
if ok {
161-
logger.Fatalf("pipeline failed: %s\n", err)
161+
logger.Fatalf("pipeline failed: %s", err)
162162
}
163163
}

go.mod

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ module github.com/blinklabs-io/snek
33
go 1.20
44

55
require (
6-
github.com/blinklabs-io/gouroboros v0.60.0
6+
github.com/blinklabs-io/gouroboros v0.61.0
77
github.com/gen2brain/beeep v0.0.0-20230602101333-f384c29b62dd
88
github.com/gin-gonic/gin v1.9.1
99
github.com/kelseyhightower/envconfig v1.4.0
@@ -17,7 +17,7 @@ require (
1717
)
1818

1919
// XXX: uncomment when testing local changes to gouroboros
20-
// replace github.com/blinklabs-io/gouroboros v0.52.0 => ../gouroboros
20+
// replace github.com/blinklabs-io/gouroboros => ../gouroboros
2121

2222
require (
2323
cloud.google.com/go/compute v1.20.1 // indirect

go.sum

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,8 @@ cloud.google.com/go/compute/metadata v0.2.3 h1:mg4jlk7mCAj6xXp9UJ4fjI9VUI5rubuGB
44
cloud.google.com/go/compute/metadata v0.2.3/go.mod h1:VAV5nSsACxMJvgaAuX6Pk2AawlZn8kiOGuCv6gTkwuA=
55
github.com/KyleBanks/depth v1.2.1 h1:5h8fQADFrWtarTdtDudMmGsC7GPbOAu6RVB3ffsVFHc=
66
github.com/KyleBanks/depth v1.2.1/go.mod h1:jzSb9d0L43HxTQfT+oSA1EEp2q+ne2uh6XgeJcm8brE=
7-
github.com/blinklabs-io/gouroboros v0.60.0 h1:G08+r15QwTlak8UfCzZmJVlzIBMIy2iyYI7CL2+Jcxs=
8-
github.com/blinklabs-io/gouroboros v0.60.0/go.mod h1:D5YJka8EyVmiXNMbRvjH23H9lNMLA4+qSlNNC/j7R0k=
7+
github.com/blinklabs-io/gouroboros v0.61.0 h1:HSAo2thM/4JM6tVF4e/o9f20aVElSckJVX6LdkvuNyE=
8+
github.com/blinklabs-io/gouroboros v0.61.0/go.mod h1:D5YJka8EyVmiXNMbRvjH23H9lNMLA4+qSlNNC/j7R0k=
99
github.com/bytedance/sonic v1.5.0/go.mod h1:ED5hyg4y6t3/9Ku1R6dU/4KyJ48DZ4jPhfY1O2AihPM=
1010
github.com/bytedance/sonic v1.10.0-rc/go.mod h1:ElCzW+ufi8qKqNW0FY314xriJhyJhuoJ3gFZdAHF7NM=
1111
github.com/bytedance/sonic v1.10.2 h1:GQebETVBxYB7JGWJtLBi07OVzWwt+8dWA00gEVW2ZFE=

input/chainsync/chainsync.go

Lines changed: 34 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,11 @@ import (
2929
ocommon "github.com/blinklabs-io/gouroboros/protocol/common"
3030
)
3131

32+
const (
33+
// Size of cache for recent chainsync cursors
34+
cursorCacheSize = 20
35+
)
36+
3237
type ChainSync struct {
3338
oConn *ouroboros.Connection
3439
logger plugin.Logger
@@ -41,12 +46,14 @@ type ChainSync struct {
4146
intersectTip bool
4247
intersectPoints []ocommon.Point
4348
includeCbor bool
49+
autoReconnect bool
4450
statusUpdateFunc StatusUpdateFunc
4551
status *ChainSyncStatus
4652
errorChan chan error
4753
eventChan chan event.Event
4854
bulkRangeStart ocommon.Point
4955
bulkRangeEnd ocommon.Point
56+
cursorCache []ocommon.Point
5057
}
5158

5259
type ChainSyncStatus struct {
@@ -79,6 +86,7 @@ func (c *ChainSync) Start() error {
7986
if err := c.setupConnection(); err != nil {
8087
return err
8188
}
89+
// Start chainsync client
8290
c.oConn.ChainSync().Client.Start()
8391
if c.oConn.BlockFetch() != nil {
8492
c.oConn.BlockFetch().Client.Start()
@@ -201,11 +209,27 @@ func (c *ChainSync) setupConnection() error {
201209
go func() {
202210
err, ok := <-c.oConn.ErrorChan()
203211
if ok {
204-
// Pass error through our own error channel
205-
c.errorChan <- err
206-
return
212+
if c.autoReconnect {
213+
if c.logger != nil {
214+
c.logger.Infof("reconnecting to %s due to error: %s", dialAddress, err)
215+
}
216+
// Shutdown current connection
217+
if err := c.oConn.Close(); err != nil {
218+
c.errorChan <- err
219+
return
220+
}
221+
// Set the intersect points from the cursor cache
222+
c.intersectPoints = c.cursorCache[:]
223+
// Restart the connection
224+
if err := c.Start(); err != nil {
225+
c.errorChan <- err
226+
return
227+
}
228+
} else {
229+
// Pass error through our own error channel
230+
c.errorChan <- err
231+
}
207232
}
208-
close(c.errorChan)
209233
}()
210234
return nil
211235
}
@@ -297,6 +321,12 @@ func (c *ChainSync) updateStatus(
297321
tipSlotNumber uint64,
298322
tipBlockHash string,
299323
) {
324+
// Update cursor cache
325+
blockHashBytes, _ := hex.DecodeString(blockHash)
326+
c.cursorCache = append(c.cursorCache, ocommon.Point{Slot: slotNumber, Hash: blockHashBytes})
327+
if len(c.cursorCache) > cursorCacheSize {
328+
c.cursorCache = c.cursorCache[len(c.cursorCache)-cursorCacheSize:]
329+
}
300330
// Determine if we've reached the chain tip
301331
if !c.status.TipReached {
302332
// Make sure we're past the end slot in any bulk range, since we don't update the tip during bulk sync

input/chainsync/options.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,13 @@ func WithIncludeCbor(includeCbor bool) ChainSyncOptionFunc {
8484
}
8585
}
8686

87+
// WithAutoReconnect specified whether to automatically reconnect if the connection is broken
88+
func WithAutoReconnect(autoReconnect bool) ChainSyncOptionFunc {
89+
return func(c *ChainSync) {
90+
c.autoReconnect = autoReconnect
91+
}
92+
}
93+
8794
// WithStatusUpdateFunc specifies a callback function for status updates. This is useful for tracking the chain-sync status
8895
// to be able to resume a sync at a later time, especially when any filtering could prevent you from getting all block update events
8996
func WithStatusUpdateFunc(

input/chainsync/plugin.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ var cmdlineOptions struct {
3535
intersectTip bool
3636
intersectPoint string
3737
includeCbor bool
38+
autoReconnect bool
3839
}
3940

4041
func init() {
@@ -110,6 +111,13 @@ func init() {
110111
DefaultValue: false,
111112
Dest: &(cmdlineOptions.includeCbor),
112113
},
114+
{
115+
Name: "auto-reconnect",
116+
Type: plugin.PluginOptionTypeBool,
117+
Description: "auto-reconnect if the connection is broken",
118+
DefaultValue: true,
119+
Dest: &(cmdlineOptions.autoReconnect),
120+
},
113121
},
114122
},
115123
)
@@ -127,6 +135,7 @@ func NewFromCmdlineOptions() plugin.Plugin {
127135
WithNtcTcp(cmdlineOptions.ntcTcp),
128136
WithBulkMode(cmdlineOptions.bulkMode),
129137
WithIncludeCbor(cmdlineOptions.includeCbor),
138+
WithAutoReconnect(cmdlineOptions.autoReconnect),
130139
}
131140
if cmdlineOptions.intersectPoint != "" {
132141
intersectPoints := []ocommon.Point{}

0 commit comments

Comments
 (0)