Skip to content

Commit 43a1ca4

Browse files
committed
kvdb/postgres: remove global application level lock
In this commit, we remove the global application level lock from the postgres backend. This lock prevents multiple write transactions from happening at the same time, and will also block a writer if a read is on going. Since this lock was added, we know always open DB connections with the strongest level of concurrency control available: `LevelSerializable`. In concert with the new auto retry logic, we ensure that if db transactions conflict (writing the same key/row in this case), then the tx is retried automatically. Removing this lock should increase perf for the postgres backend, as now concurrent write transactions can proceed, being serialized as needed. Rather then trying to handle concurrency at the application level, we'll set postgres do its job, with the application only needing to retry as necessary.
1 parent 1acc839 commit 43a1ca4

File tree

3 files changed

+0
-53
lines changed

3 files changed

+0
-53
lines changed

kvdb/postgres/db.go

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,6 @@ func newPostgresBackend(ctx context.Context, config *Config, prefix string) (
2828
Schema: "public",
2929
TableNamePrefix: prefix,
3030
SQLiteCmdReplacements: sqliteCmdReplacements,
31-
WithTxLevelLock: true,
3231
}
3332

3433
return sqlbase.NewSqlBackend(ctx, cfg)

kvdb/sqlbase/db.go

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -55,10 +55,6 @@ type Config struct {
5555
// commands. Note that the sqlite keywords to be replaced are
5656
// case-sensitive.
5757
SQLiteCmdReplacements SQLiteCmdReplacements
58-
59-
// WithTxLevelLock when set will ensure that there is a transaction
60-
// level lock.
61-
WithTxLevelLock bool
6258
}
6359

6460
// db holds a reference to the sql db connection.
@@ -79,10 +75,6 @@ type db struct {
7975
// db is the underlying database connection instance.
8076
db *sql.DB
8177

82-
// lock is the global write lock that ensures single writer. This is
83-
// only used if cfg.WithTxLevelLock is set.
84-
lock sync.RWMutex
85-
8678
// table is the name of the table that contains the data for all
8779
// top-level buckets that have keys that cannot be mapped to a distinct
8880
// sql table.

kvdb/sqlbase/readwrite_tx.go

Lines changed: 0 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ package sqlbase
55
import (
66
"context"
77
"database/sql"
8-
"sync"
98

109
"github.com/btcsuite/btcwallet/walletdb"
1110
)
@@ -20,28 +19,11 @@ type readWriteTx struct {
2019

2120
// active is true if the transaction hasn't been committed yet.
2221
active bool
23-
24-
// locker is a pointer to the global db lock.
25-
locker sync.Locker
2622
}
2723

2824
// newReadWriteTx creates an rw transaction using a connection from the
2925
// specified pool.
3026
func newReadWriteTx(db *db, readOnly bool) (*readWriteTx, error) {
31-
locker := newNoopLocker()
32-
if db.cfg.WithTxLevelLock {
33-
// Obtain the global lock instance. An alternative here is to
34-
// obtain a database lock from Postgres. Unfortunately there is
35-
// no database-level lock in Postgres, meaning that each table
36-
// would need to be locked individually. Perhaps an advisory
37-
// lock could perform this function too.
38-
locker = &db.lock
39-
if readOnly {
40-
locker = db.lock.RLocker()
41-
}
42-
}
43-
locker.Lock()
44-
4527
// Start the transaction. Don't use the timeout context because it would
4628
// be applied to the transaction as a whole. If possible, mark the
4729
// transaction as read-only to make sure that potential programming
@@ -54,15 +36,13 @@ func newReadWriteTx(db *db, readOnly bool) (*readWriteTx, error) {
5436
},
5537
)
5638
if err != nil {
57-
locker.Unlock()
5839
return nil, err
5940
}
6041

6142
return &readWriteTx{
6243
db: db,
6344
tx: tx,
6445
active: true,
65-
locker: locker,
6646
}, nil
6747
}
6848

@@ -94,7 +74,6 @@ func (tx *readWriteTx) Rollback() error {
9474

9575
// Unlock the transaction regardless of the error result.
9676
tx.active = false
97-
tx.locker.Unlock()
9877
return err
9978
}
10079

@@ -162,7 +141,6 @@ func (tx *readWriteTx) Commit() error {
162141

163142
// Unlock the transaction regardless of the error result.
164143
tx.active = false
165-
tx.locker.Unlock()
166144

167145
return err
168146
}
@@ -204,25 +182,3 @@ func (tx *readWriteTx) Exec(query string, args ...interface{}) (sql.Result,
204182

205183
return tx.tx.ExecContext(ctx, query, args...)
206184
}
207-
208-
// noopLocker is an implementation of a no-op sync.Locker.
209-
type noopLocker struct{}
210-
211-
// newNoopLocker creates a new noopLocker.
212-
func newNoopLocker() sync.Locker {
213-
return &noopLocker{}
214-
}
215-
216-
// Lock is a noop.
217-
//
218-
// NOTE: this is part of the sync.Locker interface.
219-
func (n *noopLocker) Lock() {
220-
}
221-
222-
// Unlock is a noop.
223-
//
224-
// NOTE: this is part of the sync.Locker interface.
225-
func (n *noopLocker) Unlock() {
226-
}
227-
228-
var _ sync.Locker = (*noopLocker)(nil)

0 commit comments

Comments
 (0)