Skip to content

Commit 2b83f45

Browse files
authored
Merge pull request #879 from bhandras/notification-manager-backoff-fixup
notifications: implement incremental backoff for invalid L402 tokens
2 parents bb859c5 + 61e1528 commit 2b83f45

File tree

2 files changed

+328
-22
lines changed

2 files changed

+328
-22
lines changed

notifications/manager.go

Lines changed: 70 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import (
77

88
"github.com/lightninglabs/aperture/l402"
99
"github.com/lightninglabs/loop/swapserverrpc"
10+
"github.com/lightningnetwork/lnd/lntypes"
1011
"google.golang.org/grpc"
1112
)
1213

@@ -26,6 +27,13 @@ const (
2627
NotificationTypeStaticLoopInSweepRequest
2728
)
2829

30+
const (
31+
// defaultMinAliveConnTime is the default minimum time that the
32+
// connection to the server needs to be alive before we consider it a
33+
// successful connection.
34+
defaultMinAliveConnTime = time.Minute
35+
)
36+
2937
// Client is the interface that the notification manager needs to implement in
3038
// order to be able to subscribe to notifications.
3139
type Client interface {
@@ -45,6 +53,10 @@ type Config struct {
4553
// CurrentToken returns the token that is currently contained in the
4654
// store or an l402.ErrNoToken error if there is none.
4755
CurrentToken func() (*l402.Token, error)
56+
57+
// MinAliveConnTime is the minimum time that the connection to the
58+
// server needs to be alive before we consider it a successful.
59+
MinAliveConnTime time.Duration
4860
}
4961

5062
// Manager is a manager for notifications that the swap server sends to the
@@ -60,6 +72,11 @@ type Manager struct {
6072

6173
// NewManager creates a new notification manager.
6274
func NewManager(cfg *Config) *Manager {
75+
// Set the default minimum alive connection time if it's not set.
76+
if cfg.MinAliveConnTime == 0 {
77+
cfg.MinAliveConnTime = defaultMinAliveConnTime
78+
}
79+
6380
return &Manager{
6481
cfg: cfg,
6582
subscribers: make(map[NotificationType][]subscriber),
@@ -128,13 +145,18 @@ func (m *Manager) SubscribeStaticLoopInSweepRequests(ctx context.Context,
128145
// close the readyChan to signal that the manager is ready.
129146
func (m *Manager) Run(ctx context.Context) error {
130147
// Initially we want to immediately try to connect to the server.
131-
waitTime := time.Duration(0)
148+
var (
149+
waitTime time.Duration
150+
backoff time.Duration
151+
attempts int
152+
)
132153

133154
// Start the notification runloop.
134155
for {
135-
timer := time.NewTimer(waitTime)
136156
// Increase the wait time for the next iteration.
137-
waitTime += time.Second * 1
157+
backoff = waitTime + time.Duration(attempts)*time.Second
158+
waitTime = 0
159+
timer := time.NewTimer(backoff)
138160

139161
// Return if the context has been canceled.
140162
select {
@@ -145,37 +167,66 @@ func (m *Manager) Run(ctx context.Context) error {
145167
}
146168

147169
// In order to create a valid l402 we first are going to call
148-
// the FetchL402 method. As a client might not have outbound capacity
149-
// yet, we'll retry until we get a valid response.
170+
// the FetchL402 method. As a client might not have outbound
171+
// capacity yet, we'll retry until we get a valid response.
150172
if !m.hasL402 {
151-
_, err := m.cfg.CurrentToken()
173+
token, err := m.cfg.CurrentToken()
152174
if err != nil {
153-
// We only log the error if it's not the case that we
154-
// don't have a token yet to avoid spamming the logs.
175+
// We only log the error if it's not the case
176+
// that we don't have a token yet to avoid
177+
// spamming the logs.
155178
if err != l402.ErrNoToken {
156-
log.Errorf("Error getting L402 from store: %v", err)
179+
log.Errorf("Error getting L402 from "+
180+
"the store: %v", err)
157181
}
158182
continue
159183
}
160-
m.hasL402 = true
161-
}
162184

163-
connectedFunc := func() {
164-
// Reset the wait time to 10 seconds.
165-
waitTime = time.Second * 10
185+
// If the preimage is empty, we don't have a valid L402
186+
// yet so we'll continue to retry with the incremental
187+
// backoff.
188+
emptyPreimage := lntypes.Preimage{}
189+
if token.Preimage == emptyPreimage {
190+
attempts++
191+
continue
192+
}
193+
194+
attempts = 0
195+
m.hasL402 = true
166196
}
167197

168-
err := m.subscribeNotifications(ctx, connectedFunc)
198+
connectAttempted := time.Now()
199+
err := m.subscribeNotifications(ctx)
169200
if err != nil {
170-
log.Errorf("Error subscribing to notifications: %v", err)
201+
log.Errorf("Error subscribing to notifications: %v",
202+
err)
203+
}
204+
connectionAliveTime := time.Since(connectAttempted)
205+
206+
// Note that we may be able to connet to the stream but not
207+
// able to use it if the client is unable to pay for their
208+
// L402. In this case the subscription will fail on the first
209+
// read immediately after connecting. We'll therefore only
210+
// consider the connection successful if we were able to use
211+
// the stream for at least the minimum alive connection time
212+
// (which defaults to 1 minute).
213+
if connectionAliveTime > m.cfg.MinAliveConnTime {
214+
// Reset the backoff to 10 seconds and the connect
215+
// attempts to zero if we were really connected for a
216+
// considerable amount of time (1 minute).
217+
waitTime = time.Second * 10
218+
attempts = 0
219+
} else {
220+
// We either failed to connect or the stream
221+
// disconnected immediately, so we just increase the
222+
// backoff.
223+
attempts++
171224
}
172225
}
173226
}
174227

175228
// subscribeNotifications subscribes to the notifications from the server.
176-
func (m *Manager) subscribeNotifications(ctx context.Context,
177-
connectedFunc func()) error {
178-
229+
func (m *Manager) subscribeNotifications(ctx context.Context) error {
179230
callCtx, cancel := context.WithCancel(ctx)
180231
defer cancel()
181232

@@ -186,8 +237,6 @@ func (m *Manager) subscribeNotifications(ctx context.Context,
186237
return err
187238
}
188239

189-
// Signal that we're connected to the server.
190-
connectedFunc()
191240
log.Debugf("Successfully subscribed to server notifications")
192241

193242
for {

0 commit comments

Comments
 (0)