firewaldb: single implementation of BBolt DBExecutor

The `kvStores` and `privacyMapDB` types have very similar looking
`Update` and `View` methods. Instead of the duplication, here we let
things be more generic by defining a generic `kvdbExecutor` which has
Update and View methods defined.
This commit is contained in:
Elle Mouton 2025-03-11 15:37:42 -05:00
parent 62723bd2f3
commit a395cb9b3f
No known key found for this signature in database
GPG key ID: D7D916376026F177
3 changed files with 66 additions and 90 deletions

44
firewalldb/kvdb_store.go Normal file
View file

@ -0,0 +1,44 @@
package firewalldb
import (
"context"
"go.etcd.io/bbolt"
)
// kvdbExecutor is a concrete implementation of the DBExecutor interface that
// uses a bbolt database as its backing store.
type kvdbExecutor[T any] struct {
db *bbolt.DB
wrapTx func(tx *bbolt.Tx) T
}
// Update opens a database read/write transaction and executes the function f
// with the transaction passed as a parameter. After f exits, if f did not
// error, the transaction is committed. Otherwise, if f did error, the
// transaction is rolled back. If the rollback fails, the original error
// returned by f is still returned. If the commit fails, the commit error is
// returned.
//
// NOTE: this is part of the DBExecutor interface.
func (e *kvdbExecutor[T]) Update(ctx context.Context,
fn func(ctx context.Context, tx T) error) error {
return e.db.Update(func(tx *bbolt.Tx) error {
return fn(ctx, e.wrapTx(tx))
})
}
// View opens a database read transaction and executes the function f with the
// transaction passed as a parameter. After f exits, the transaction is rolled
// back. If f errors, its error is returned, not a rollback error (if any
// occur).
//
// NOTE: this is part of the DBExecutor interface.
func (e *kvdbExecutor[T]) View(ctx context.Context,
fn func(ctx context.Context, tx T) error) error {
return e.db.View(func(tx *bbolt.Tx) error {
return fn(ctx, e.wrapTx(tx))
})
}

View file

@ -107,62 +107,28 @@ type RulesDB interface {
func (db *DB) GetKVStores(rule string, groupID session.ID,
feature string) KVStores {
return &kvStores{
db: db.DB,
ruleName: rule,
groupID: groupID,
featureName: feature,
return &kvdbExecutor[KVStoreTx]{
db: db.DB,
wrapTx: func(tx *bbolt.Tx) KVStoreTx {
return &kvStoreTx{
boltTx: tx,
kvStores: &kvStores{
ruleName: rule,
groupID: groupID,
featureName: feature,
},
}
},
}
}
// kvStores implements the rules.KVStores interface.
type kvStores struct {
db *bbolt.DB
ruleName string
groupID session.ID
featureName string
}
// Update opens a database read/write transaction and executes the function f
// with the transaction passed as a parameter. After f exits, if f did not
// error, the transaction is committed. Otherwise, if f did error, the
// transaction is rolled back. If the rollback fails, the original error
// returned by f is still returned. If the commit fails, the commit error is
// returned.
//
// NOTE: this is part of the KVStores interface.
func (s *kvStores) Update(ctx context.Context, fn func(ctx context.Context,
tx KVStoreTx) error) error {
return s.db.Update(func(tx *bbolt.Tx) error {
boltTx := &kvStoreTx{
boltTx: tx,
kvStores: s,
}
return fn(ctx, boltTx)
})
}
// View opens a database read transaction and executes the function f with the
// transaction passed as a parameter. After f exits, the transaction is rolled
// back. If f errors, its error is returned, not a rollback error (if any
// occur).
//
// NOTE: this is part of the KVStores interface.
func (s *kvStores) View(ctx context.Context, fn func(ctx context.Context,
tx KVStoreTx) error) error {
return s.db.View(func(tx *bbolt.Tx) error {
boltTx := &kvStoreTx{
boltTx: tx,
kvStores: s,
}
return fn(ctx, boltTx)
})
}
// getBucketFunc defines the signature of the bucket creation/fetching function
// required by kvStoreTx. If create is true, then all the bucket (and all
// buckets leading up to the bucket) should be created if they do not already

View file

@ -42,9 +42,16 @@ type NewPrivacyMapDB func(groupID session.ID) PrivacyMapDB
// PrivacyDB constructs a PrivacyMapDB that will be indexed under the given
// group ID key.
func (db *DB) PrivacyDB(groupID session.ID) PrivacyMapDB {
return &privacyMapDB{
db: db,
groupID: groupID,
return &kvdbExecutor[PrivacyMapTx]{
db: db.DB,
wrapTx: func(tx *bbolt.Tx) PrivacyMapTx {
return &privacyMapTx{
boltTx: tx,
privacyMapDB: &privacyMapDB{
groupID: groupID,
},
}
},
}
}
@ -74,50 +81,9 @@ type PrivacyMapTx interface {
// privacyMapDB is an implementation of PrivacyMapDB.
type privacyMapDB struct {
db *DB
groupID session.ID
}
// Update opens a database read/write transaction and executes the function f
// with the transaction passed as a parameter. After f exits, if f did not
// error, the transaction is committed. Otherwise, if f did error, the
// transaction is rolled back. If the rollback fails, the original error
// returned by f is still returned. If the commit fails, the commit error is
// returned.
//
// NOTE: this is part of the PrivacyMapDB interface.
func (p *privacyMapDB) Update(ctx context.Context, fn func(ctx context.Context,
tx PrivacyMapTx) error) error {
return p.db.Update(func(tx *bbolt.Tx) error {
boltTx := &privacyMapTx{
privacyMapDB: p,
boltTx: tx,
}
return fn(ctx, boltTx)
})
}
// View opens a database read transaction and executes the function f with the
// transaction passed as a parameter. After f exits, the transaction is rolled
// back. If f errors, its error is returned, not a rollback error (if any
// occur).
//
// NOTE: this is part of the PrivacyMapDB interface.
func (p *privacyMapDB) View(ctx context.Context, fn func(ctx context.Context,
tx PrivacyMapTx) error) error {
return p.db.View(func(tx *bbolt.Tx) error {
boltTx := &privacyMapTx{
privacyMapDB: p,
boltTx: tx,
}
return fn(ctx, boltTx)
})
}
// privacyMapTx is an implementation of PrivacyMapTx.
type privacyMapTx struct {
*privacyMapDB