44 "context"
55 "crypto/sha1"
66 "encoding/hex"
7+ "fmt"
78 "io"
89 "time"
910
@@ -16,6 +17,7 @@ import (
1617 notificationApi "github.com/xmtp/xmtpd/pkg/proto/xmtpv4/message_api"
1718 "github.com/xmtp/xmtpd/pkg/topic"
1819 "go.uber.org/zap"
20+ "google.golang.org/grpc"
1921 "google.golang.org/protobuf/proto"
2022)
2123
@@ -25,6 +27,7 @@ type V4Listener struct {
2527 ctx context.Context
2628 cancelFunc func ()
2729 v4Client notificationApi.NotificationApiClient
30+ v4Conn * grpc.ClientConn
2831 opts options.XmtpOptions
2932 envelopeChannel chan * envelopesProto.OriginatorEnvelope
3033 installations interfaces.Installations
@@ -43,7 +46,7 @@ func NewV4Listener(
4346 clientVersion string ,
4447 appVersion string ,
4548) (* V4Listener , error ) {
46- client , err := NewV4Client (ctx , opts .GrpcAddress , opts .UseTls , clientVersion , appVersion )
49+ client , conn , err := NewV4Client (ctx , opts .GrpcAddress , opts .UseTls , clientVersion , appVersion )
4750 if err != nil {
4851 return nil , err
4952 }
@@ -56,6 +59,7 @@ func NewV4Listener(
5659 cancelFunc : cancel ,
5760 logger : namedLogger ,
5861 v4Client : client ,
62+ v4Conn : conn ,
5963 opts : opts ,
6064 envelopeChannel : make (chan * envelopesProto.OriginatorEnvelope , 100 ),
6165 installations : installations ,
@@ -83,11 +87,18 @@ func (l *V4Listener) startEnvelopeListener() {
8387 l .logger .Info ("starting V4 envelope listener" )
8488 sleepTime := STARTING_SLEEP_TIME
8589 for {
90+ select {
91+ case <- l .ctx .Done ():
92+ close (l .envelopeChannel )
93+ return
94+ default :
95+ }
96+
8697 stream , err := l .v4Client .SubscribeAllEnvelopes (l .ctx , & notificationApi.SubscribeAllEnvelopesRequest {})
8798 if err != nil {
8899 l .logger .Error ("error connecting to V4 stream" , zap .Error (err ))
89100 time .Sleep (sleepTime )
90- sleepTime = sleepTime * 2
101+ sleepTime = cappedBackoff ( sleepTime )
91102 if err = l .refreshV4Client (); err != nil {
92103 l .logger .Error ("error refreshing V4 client" , zap .Error (err ))
93104 }
@@ -108,17 +119,15 @@ func (l *V4Listener) startEnvelopeListener() {
108119
109120 if err != nil {
110121 l .logger .Error ("error reading from V4 stream" , zap .Error (err ))
111- // Wait to avoid hammering the API and getting rate limited
112122 time .Sleep (sleepTime )
113- sleepTime = sleepTime * 2
123+ sleepTime = cappedBackoff ( sleepTime )
114124 if err = l .refreshV4Client (); err != nil {
115125 l .logger .Error ("error refreshing V4 client" , zap .Error (err ))
116126 }
117127 break streamLoop
118128 }
119129
120130 if resp != nil {
121- // Reset the sleep time on first successful message
122131 sleepTime = STARTING_SLEEP_TIME
123132 for _ , env := range resp .GetEnvelopes () {
124133 l .envelopeChannel <- env
@@ -366,19 +375,25 @@ func logV4ConversionIssue(logger *zap.Logger, clientEnvProto *envelopesProto.Cli
366375}
367376
368377func buildV4IdempotencyKey (env * envelopesProto.OriginatorEnvelope ) string {
369- h := sha1 .New ()
370378 b , err := proto .Marshal (env )
371- if err == nil {
372- h .Write (b )
379+ if err != nil {
380+ // Fall back to a timestamp-based key if marshaling fails
381+ return hex .EncodeToString ([]byte (fmt .Sprintf ("v4-%d" , time .Now ().UnixNano ())))
373382 }
383+ h := sha1 .New ()
384+ h .Write (b )
374385 return hex .EncodeToString (h .Sum (nil ))
375386}
376387
377388func (l * V4Listener ) refreshV4Client () error {
378- client , err := NewV4Client (l .ctx , l .opts .GrpcAddress , l .opts .UseTls , l .clientVersion , l .appVersion )
389+ if l .v4Conn != nil {
390+ _ = l .v4Conn .Close ()
391+ }
392+ client , conn , err := NewV4Client (l .ctx , l .opts .GrpcAddress , l .opts .UseTls , l .clientVersion , l .appVersion )
379393 if err != nil {
380394 return err
381395 }
382396 l .v4Client = client
397+ l .v4Conn = conn
383398 return nil
384399}
0 commit comments