-
Notifications
You must be signed in to change notification settings - Fork 179
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
Backoff for websocket connection retry #338
Changes from 3 commits
f05b6af
61f334d
fd68e33
471f24b
4834f26
30d9293
350de2f
4537221
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -15,8 +15,10 @@ import ( | |
"sort" | ||
"time" | ||
|
||
"github.com/cenkalti/backoff" | ||
"github.com/ericxtang/m3u8" | ||
ethcommon "github.com/ethereum/go-ethereum/common" | ||
"github.com/ethereum/go-ethereum/core/types" | ||
"github.com/ethereum/go-ethereum/crypto" | ||
"github.com/golang/glog" | ||
"github.com/livepeer/go-livepeer/common" | ||
|
@@ -44,6 +46,7 @@ var DefaultMasterPlaylistWaitTime = 60 * time.Second | |
var DefaultJobLength = int64(5760) //Avg 1 day in 15 sec blocks | ||
var ConnFileWriteFreq = time.Duration(60) * time.Second | ||
var LivepeerVersion = "0.1.14-unstable" | ||
var SubscribeRetry = uint64(3) | ||
|
||
//NodeID can be converted from libp2p PeerID. | ||
type NodeID string | ||
|
@@ -119,8 +122,17 @@ func (n *LivepeerNode) CreateTranscodeJob(strmID StreamID, profiles []ffmpeg.Vid | |
return err | ||
} | ||
|
||
blk, err := b.BlockByNumber(context.Background(), nil) | ||
if err != nil { | ||
var blk *types.Block | ||
getBlock := func() error { | ||
blk, err = b.BlockByNumber(context.Background(), nil) | ||
if err != nil { | ||
return err | ||
} | ||
return nil | ||
} | ||
bo := backoff.NewExponentialBackOff() | ||
bo.MaxElapsedTime = time.Second * 15 | ||
if err := backoff.Retry(getBlock, backoff.WithMaxRetries(bo, SubscribeRetry)); err != nil { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. In the case of websocket connections to Infura, we really should retry indefinitely. We don't want to force user interaction if all they need to do is re-establish the connection. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Fair enough. My concern was around spamming the Infura network, but I guess they are there for a reason and we should always try to reconnect. |
||
glog.Errorf("Cannot get current block number: %v", err) | ||
return ErrNotFound | ||
} | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -169,6 +169,7 @@ func (c *BasicClaimManager) AddReceipt(seqNo int64, data []byte, tDataHash []byt | |
|
||
c.cost = new(big.Int).Add(c.cost, c.pricePerSegment) | ||
c.unclaimedSegs[seqNo] = true | ||
// glog.Infof("Added %v. unclaimSegs: %v", seqNo, c.unclaimedSegs) | ||
|
||
return nil | ||
} | ||
|
@@ -243,7 +244,12 @@ func (c *BasicClaimManager) markClaimedSegs(segRange [2]int64) { | |
|
||
//Claim creates the onchain claim for all the claims added through AddReceipt | ||
func (c *BasicClaimManager) ClaimVerifyAndDistributeFees() error { | ||
// segs := make([]int64, 0) | ||
// for k, _ := range c.unclaimedSegs { | ||
// segs = append(segs, k) | ||
// } | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. extraneous? |
||
ranges := c.makeRanges() | ||
// glog.Infof("Claiming for segs: , ranges: %v", segs, ranges) | ||
|
||
for _, segRange := range ranges { | ||
//create concat hashes for each seg | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -19,6 +19,7 @@ import ( | |
"sync" | ||
"time" | ||
|
||
"github.com/cenkalti/backoff" | ||
"github.com/ethereum/go-ethereum/accounts" | ||
"github.com/ethereum/go-ethereum/accounts/abi/bind" | ||
"github.com/ethereum/go-ethereum/common" | ||
|
@@ -713,16 +714,28 @@ func (c *client) RegisteredTranscoders() ([]*lpTypes.Transcoder, error) { | |
func (c *client) IsAssignedTranscoder(jobID *big.Int) (bool, error) { | ||
jInfo, err := c.JobsManagerSession.GetJob(jobID) | ||
if err != nil { | ||
glog.Errorf("Error getting job: %v", err) | ||
return false, err | ||
} | ||
|
||
blk, err := c.backend.BlockByNumber(context.Background(), jInfo.CreationBlock) | ||
if err != nil { | ||
var blk *types.Block | ||
getBlock := func() error { | ||
blk, err = c.backend.BlockByNumber(context.Background(), jInfo.CreationBlock) | ||
if err != nil { | ||
glog.Errorf("Error getting block by number %v: %v. retrying...", jInfo.CreationBlock.String(), err) | ||
return err | ||
} | ||
|
||
return nil | ||
} | ||
if err := backoff.Retry(getBlock, backoff.WithMaxRetries(backoff.NewConstantBackOff(time.Second), SubscribeRetry)); err != nil { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why use a constant backoff here, but an exponential backoff strategy in There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think I removed all the exponential backoffs. |
||
glog.Errorf("BlockByNumber failed: %v", err) | ||
return false, err | ||
} | ||
|
||
t, err := c.BondingManagerSession.ElectActiveTranscoder(jInfo.MaxPricePerSegment, blk.Hash(), jInfo.CreationRound) | ||
if err != nil { | ||
glog.Errorf("Error getting ElectActiveTranscoder: %v", err) | ||
return false, err | ||
} | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -3,8 +3,11 @@ package eth | |
import ( | ||
"context" | ||
"fmt" | ||
"math/big" | ||
"strings" | ||
"time" | ||
|
||
"github.com/cenkalti/backoff" | ||
"github.com/ethereum/go-ethereum" | ||
"github.com/ethereum/go-ethereum/accounts/abi" | ||
"github.com/ethereum/go-ethereum/common" | ||
|
@@ -14,6 +17,8 @@ import ( | |
"github.com/livepeer/go-livepeer/eth/contracts" | ||
) | ||
|
||
var SubscribeRetry = uint64(3) | ||
|
||
type logCallback func(types.Log) (bool, error) | ||
type headerCallback func(*types.Header) (bool, error) | ||
|
||
|
@@ -35,6 +40,7 @@ type eventMonitor struct { | |
backend *ethclient.Client | ||
contractAddrMap map[string]common.Address | ||
eventSubMap map[string]*EventSubscription | ||
latestBlock *big.Int | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is this used anywhere? |
||
} | ||
|
||
func NewEventMonitor(backend *ethclient.Client, contractAddrMap map[string]common.Address) EventMonitor { | ||
|
@@ -75,30 +81,42 @@ func (em *eventMonitor) SubscribeNewRound(ctx context.Context, subName string, l | |
Topics: [][]common.Hash{[]common.Hash{eventId}}, | ||
} | ||
|
||
sub, err := em.backend.SubscribeFilterLogs(ctx, q, logsCh) | ||
if err != nil { | ||
return nil, err | ||
subscribe := func() error { | ||
sub, err := em.backend.SubscribeFilterLogs(ctx, q, logsCh) | ||
if err != nil { | ||
glog.Errorf("SubscribeNewRound error: %v. Retrying...", err) | ||
return err | ||
} else { | ||
glog.Infof("SubscribeNewRound successful.") | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is this a one-time message or would it lead to additional logging after each round? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It's a one-time message per connection (it will re-print if there is a re-connection) |
||
} | ||
|
||
em.eventSubMap[subName] = &EventSubscription{ | ||
sub: sub, | ||
logsCh: logsCh, | ||
active: true, | ||
} | ||
|
||
return nil | ||
} | ||
|
||
em.eventSubMap[subName] = &EventSubscription{ | ||
sub: sub, | ||
logsCh: logsCh, | ||
b := backoff.NewExponentialBackOff() | ||
b.MaxElapsedTime = time.Second * 15 | ||
if err := backoff.Retry(subscribe, backoff.WithMaxRetries(b, SubscribeRetry)); err != nil { | ||
glog.Infof("SubscribeNewRound error: %v", err) | ||
return nil, err | ||
} | ||
|
||
go em.watchLogs(subName, cb, func() { | ||
glog.Infof("Trying to resubscribe for %v", subName) | ||
|
||
sub, err = em.backend.SubscribeFilterLogs(ctx, q, logsCh) | ||
if err != nil { | ||
glog.Error(err) | ||
b := backoff.NewExponentialBackOff() | ||
b.MaxElapsedTime = time.Second * 15 | ||
if err := backoff.Retry(subscribe, backoff.WithMaxRetries(b, SubscribeRetry)); err != nil { | ||
glog.Infof("Resubscription error: %v", err) | ||
return | ||
} | ||
|
||
em.eventSubMap[subName].sub = sub | ||
em.eventSubMap[subName].logsCh = logsCh | ||
}) | ||
|
||
return sub, nil | ||
return em.eventSubMap[subName].sub, nil | ||
} | ||
|
||
func (em *eventMonitor) SubscribeNewJob(ctx context.Context, subName string, logsCh chan types.Log, broadcasterAddr common.Address, cb logCallback) (ethereum.Subscription, error) { | ||
|
@@ -127,61 +145,82 @@ func (em *eventMonitor) SubscribeNewJob(ctx context.Context, subName string, log | |
} | ||
} | ||
|
||
sub, err := em.backend.SubscribeFilterLogs(ctx, q, logsCh) | ||
if err != nil { | ||
return nil, err | ||
subscribe := func() error { | ||
sub, err := em.backend.SubscribeFilterLogs(ctx, q, logsCh) | ||
if err != nil { | ||
glog.Errorf("SubscribeNewJob error: %v. retrying...", err) | ||
return err | ||
} else { | ||
glog.Infof("SubscribedNewJob successful.") | ||
} | ||
|
||
em.eventSubMap[subName] = &EventSubscription{ | ||
sub: sub, | ||
logsCh: logsCh, | ||
active: true, | ||
} | ||
return nil | ||
} | ||
|
||
em.eventSubMap[subName] = &EventSubscription{ | ||
sub: sub, | ||
logsCh: logsCh, | ||
b := backoff.NewExponentialBackOff() | ||
b.MaxElapsedTime = time.Second * 15 | ||
if err = backoff.Retry(subscribe, backoff.WithMaxRetries(b, 3)); err != nil { | ||
glog.Errorf("SubscribeNewJob failed: %v", err) | ||
return nil, err | ||
} | ||
|
||
go em.watchLogs(subName, cb, func() { | ||
glog.Infof("Trying to resubscribe for %v", subName) | ||
|
||
sub, err = em.backend.SubscribeFilterLogs(ctx, q, logsCh) | ||
if err != nil { | ||
glog.Error(err) | ||
b := backoff.NewExponentialBackOff() | ||
b.MaxElapsedTime = time.Second * 15 | ||
if err := backoff.Retry(subscribe, backoff.WithMaxRetries(b, SubscribeRetry)); err != nil { | ||
glog.Errorf("Resubscribe failed: %v", err) | ||
return | ||
} | ||
|
||
em.eventSubMap[subName].sub = sub | ||
em.eventSubMap[subName].logsCh = logsCh | ||
}) | ||
|
||
return sub, nil | ||
return em.eventSubMap[subName].sub, nil | ||
} | ||
|
||
func (em *eventMonitor) SubscribeNewBlock(ctx context.Context, subName string, headersCh chan *types.Header, cb headerCallback) (ethereum.Subscription, error) { | ||
if _, ok := em.eventSubMap[subName]; ok { | ||
return nil, fmt.Errorf("Event subscription already registered as active with name: %v", subName) | ||
} | ||
|
||
sub, err := em.backend.SubscribeNewHead(ctx, headersCh) | ||
if err != nil { | ||
return nil, err | ||
} | ||
subscribe := func() error { | ||
sub, err := em.backend.SubscribeNewHead(ctx, headersCh) | ||
if err != nil { | ||
glog.Errorf("SubscribeNewHead error: %v. retrying...", err) | ||
return err | ||
} else { | ||
glog.Infof("SubscribeNewHead successful.") | ||
} | ||
|
||
em.eventSubMap[subName] = &EventSubscription{ | ||
sub: sub, | ||
headersCh: headersCh, | ||
em.eventSubMap[subName] = &EventSubscription{ | ||
sub: sub, | ||
headersCh: headersCh, | ||
active: true, | ||
} | ||
return nil | ||
} | ||
b := backoff.NewExponentialBackOff() | ||
b.MaxElapsedTime = time.Second * 15 | ||
if err := backoff.Retry(subscribe, backoff.WithMaxRetries(b, SubscribeRetry)); err != nil { | ||
glog.Errorf("SubscribeNewHead failed: %v", err) | ||
return nil, err | ||
} | ||
|
||
go em.watchBlocks(subName, sub, headersCh, cb, func() { | ||
go em.watchBlocks(subName, em.eventSubMap[subName].sub, headersCh, cb, func() { | ||
glog.Infof("Trying to resubscribe for %v", subName) | ||
|
||
sub, err = em.backend.SubscribeNewHead(ctx, headersCh) | ||
if err != nil { | ||
glog.Error(err) | ||
b := backoff.NewExponentialBackOff() | ||
b.MaxElapsedTime = time.Second * 15 | ||
if err := backoff.Retry(subscribe, backoff.WithMaxRetries(b, SubscribeRetry)); err != nil { | ||
glog.Errorf("Resubscribe failed: %v", err) | ||
return | ||
} | ||
|
||
em.eventSubMap[subName].sub = sub | ||
em.eventSubMap[subName].headersCh = headersCh | ||
}) | ||
|
||
return sub, nil | ||
return em.eventSubMap[subName].sub, nil | ||
} | ||
|
||
func (em *eventMonitor) setSubActive(subName string) { | ||
|
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Replace with
git clone
?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good idea!