Skip to content

Commit 9fe7ab1

Browse files
authored
Merge pull request #374 from guggero/db-timeout
loopd+loopdb: add timeout to DB open
2 parents c4c1f32 + 39c8c92 commit 9fe7ab1

File tree

4 files changed

+48
-11
lines changed

4 files changed

+48
-11
lines changed

loopd/daemon.go

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import (
1111
"sync"
1212
"sync/atomic"
1313

14+
"github.com/coreos/bbolt"
1415
proxy "github.com/grpc-ecosystem/grpc-gateway/runtime"
1516
"github.com/lightninglabs/lndclient"
1617
"github.com/lightninglabs/loop"
@@ -128,6 +129,13 @@ func (d *Daemon) Start() error {
128129
// and error handlers. If this fails, then nothing has been started yet
129130
// and we can just return the error.
130131
err = d.initialize()
132+
if errors.Is(err, bbolt.ErrTimeout) {
133+
// We're trying to be started as a standalone Loop daemon, most
134+
// likely LiT is already running and blocking the DB
135+
return fmt.Errorf("%v: make sure no other loop daemon "+
136+
"process (standalone or embedded in "+
137+
"lightning-terminal) is running", err)
138+
}
131139
if err != nil {
132140
return err
133141
}
@@ -168,7 +176,14 @@ func (d *Daemon) StartAsSubserver(lndGrpc *lndclient.GrpcLndServices) error {
168176
// the swap server client, the RPC server instance and our main swap
169177
// handlers. If this fails, then nothing has been started yet and we can
170178
// just return the error.
171-
return d.initialize()
179+
err := d.initialize()
180+
if errors.Is(err, bbolt.ErrTimeout) {
181+
// We're trying to be started inside LiT so there most likely is
182+
// another standalone Loop process blocking the DB.
183+
return fmt.Errorf("%v: make sure no other loop daemon "+
184+
"process is running", err)
185+
}
186+
return err
172187
}
173188

174189
// ValidateMacaroon extracts the macaroon from the context's gRPC metadata,

loopd/macaroons.go

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,9 @@ import (
55
"fmt"
66
"io/ioutil"
77
"os"
8-
"time"
98

9+
"github.com/coreos/bbolt"
10+
"github.com/lightninglabs/loop/loopdb"
1011
"github.com/lightningnetwork/lnd/lnrpc"
1112
"github.com/lightningnetwork/lnd/macaroons"
1213
"google.golang.org/grpc"
@@ -17,10 +18,6 @@ const (
1718
// loopMacaroonLocation is the value we use for the loopd macaroons'
1819
// "Location" field when baking them.
1920
loopMacaroonLocation = "loop"
20-
21-
// macDatabaseOpenTimeout is how long we wait for acquiring the lock on
22-
// the macaroon database before we give up with an error.
23-
macDatabaseOpenTimeout = time.Second * 5
2421
)
2522

2623
var (
@@ -150,8 +147,14 @@ func (d *Daemon) startMacaroonService() error {
150147
var err error
151148
d.macaroonService, err = macaroons.NewService(
152149
d.cfg.DataDir, loopMacaroonLocation, false,
153-
macDatabaseOpenTimeout, macaroons.IPLockChecker,
150+
loopdb.DefaultLoopDBTimeout, macaroons.IPLockChecker,
154151
)
152+
if err == bbolt.ErrTimeout {
153+
return fmt.Errorf("%w: couldn't obtain exclusive lock on "+
154+
"%s/%s, timed out after %v", bbolt.ErrTimeout,
155+
d.cfg.DataDir, "macaroons.db",
156+
loopdb.DefaultLoopDBTimeout)
157+
}
155158
if err != nil {
156159
return fmt.Errorf("unable to set up macaroon authentication: "+
157160
"%v", err)

loopdb/store.go

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -99,9 +99,18 @@ var (
9999
keyLength = 33
100100
)
101101

102-
// DefaultLoopOutHtlcConfirmations is the default number of confirmations we
103-
// set for a loop out htlc.
104-
const DefaultLoopOutHtlcConfirmations uint32 = 1
102+
const (
103+
// DefaultLoopOutHtlcConfirmations is the default number of
104+
// confirmations we set for a loop out htlc.
105+
DefaultLoopOutHtlcConfirmations uint32 = 1
106+
107+
// DefaultLoopDBTimeout is the default maximum time we wait for the
108+
// Loop bbolt database to be opened. If the database is already opened
109+
// by another process, the unique lock cannot be obtained. With the
110+
// timeout we error out after the given time instead of just blocking
111+
// for forever.
112+
DefaultLoopDBTimeout = 5 * time.Second
113+
)
105114

106115
// fileExists returns true if the file exists, and false otherwise.
107116
func fileExists(path string) bool {
@@ -139,7 +148,14 @@ func NewBoltSwapStore(dbPath string, chainParams *chaincfg.Params) (
139148
// Now that we know that path exists, we'll open up bolt, which
140149
// implements our default swap store.
141150
path := filepath.Join(dbPath, dbFileName)
142-
bdb, err := bbolt.Open(path, 0600, nil)
151+
bdb, err := bbolt.Open(path, 0600, &bbolt.Options{
152+
Timeout: DefaultLoopDBTimeout,
153+
})
154+
if err == bbolt.ErrTimeout {
155+
return nil, fmt.Errorf("%w: couldn't obtain exclusive lock on "+
156+
"%s, timed out after %v", bbolt.ErrTimeout, path,
157+
DefaultLoopDBTimeout)
158+
}
143159
if err != nil {
144160
return nil, err
145161
}

release_notes.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,3 +19,6 @@ This file tracks release notes for the loop client.
1919
#### Breaking Changes
2020

2121
#### Bug Fixes
22+
- Instead of just blocking for forever without any apparent reason if another
23+
Loop daemon process is already running, we now exit with an error after 5
24+
seconds if acquiring the unique lock on the Loop `bbolt` DB fails.

0 commit comments

Comments
 (0)