Skip to content

Commit faba39f

Browse files
committed
kvdb/sqlite: enable incremental auto_vacuum on DB creation
In this commit, we make a change that enables the `auto_vacuum = incremental` pragma for SQLite databases, but only when the database file is first created. Incremental auto-vacuum allows SQLite to reclaim unused space within the database file over time, preventing indefinite growth. According to the SQLite documentation, the `auto_vacuum` mode must be set *before* any tables are created in the database. To achieve this, a new boolean parameter `isCreate` has been added to the `NewSqliteBackend` function. The `createDBDriver` now passes `true` for this parameter, ensuring the pragma is set during initial database setup. Conversely, `openDBDriver` passes `false` when opening an existing database, leaving the existing vacuum mode untouched. We also, the internal representation of pragma options was refactored to use a named struct `pragmaOption` for improved clarity.
1 parent 6a3845b commit faba39f

File tree

2 files changed

+48
-7
lines changed

2 files changed

+48
-7
lines changed

kvdb/sqlite/db.go

Lines changed: 21 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -24,17 +24,21 @@ const (
2424
sqliteTxLockImmediate = "_txlock=immediate"
2525
)
2626

27+
// pragmaOption holds a key-value pair for a SQLite pragma setting.
28+
type pragmaOption struct {
29+
name string
30+
value string
31+
}
32+
2733
// NewSqliteBackend returns a db object initialized with the passed backend
2834
// config. If a sqlite connection cannot be established, then an error is
2935
// returned.
3036
func NewSqliteBackend(ctx context.Context, cfg *Config, dbPath, fileName,
31-
prefix string) (walletdb.DB, error) {
37+
prefix string, isCreate bool) (walletdb.DB, error) {
3238

3339
// First, we add a set of mandatory pragma options to the query.
34-
pragmaOptions := []struct {
35-
name string
36-
value string
37-
}{
40+
// Use the new named struct pragmaOption.
41+
pragmaOptions := []pragmaOption{
3842
{
3943
name: "busy_timeout",
4044
value: fmt.Sprintf(
@@ -50,6 +54,18 @@ func NewSqliteBackend(ctx context.Context, cfg *Config, dbPath, fileName,
5054
value: "WAL",
5155
},
5256
}
57+
58+
// If the database is being created, set auto_vacuum to incremental.
59+
// This must be done before any tables are created. According to the
60+
// SQLite docs, this corresponds to mode 2.
61+
if isCreate {
62+
// Use the new named struct pragmaOption here as well.
63+
pragmaOptions = append(pragmaOptions, pragmaOption{
64+
name: "auto_vacuum",
65+
value: "incremental",
66+
})
67+
}
68+
5369
sqliteOptions := make(url.Values)
5470
for _, option := range pragmaOptions {
5571
sqliteOptions.Add(

kvdb/sqlite/driver.go

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ package sqlite
55
import (
66
"context"
77
"fmt"
8+
"os"
9+
"path/filepath"
810

911
"github.com/btcsuite/btcwallet/walletdb"
1012
)
@@ -69,7 +71,8 @@ func createDBDriver(args ...interface{}) (walletdb.DB, error) {
6971
return nil, err
7072
}
7173

72-
return NewSqliteBackend(ctx, config, dbPath, filename, prefix)
74+
// Pass 'true' for isCreate when creating the database via walletdb.Create.
75+
return NewSqliteBackend(ctx, config, dbPath, filename, prefix, true)
7376
}
7477

7578
// openDBDriver is the callback provided during driver registration that opens
@@ -80,7 +83,29 @@ func openDBDriver(args ...interface{}) (walletdb.DB, error) {
8083
return nil, err
8184
}
8285

83-
return NewSqliteBackend(ctx, config, dbPath, filename, prefix)
86+
// Check if the database file already exists. The walletdb.Open function
87+
// is meant to open existing databases, but the underlying driver might
88+
// create one if it doesn't exist. We need to know if we are creating
89+
// it here to set the initial pragmas correctly (like auto_vacuum).
90+
fullPath := filepath.Join(dbPath, filename)
91+
_, err = os.Stat(fullPath)
92+
isCreate := false
93+
if err != nil {
94+
// If the error is specifically that the file doesn't exist,
95+
// then we are effectively creating it now.
96+
if os.IsNotExist(err) {
97+
isCreate = true
98+
} else {
99+
// For any other error (e.g., permission issues),
100+
// return the error.
101+
return nil, fmt.Errorf("error checking db file "+
102+
"%s: %w", fullPath, err)
103+
}
104+
}
105+
106+
// Pass the determined isCreate flag to NewSqliteBackend.
107+
// It will be true only if os.Stat reported IsNotExist.
108+
return NewSqliteBackend(ctx, config, dbPath, filename, prefix, isCreate)
84109
}
85110

86111
func init() {

0 commit comments

Comments
 (0)