Skip to content

Commit 3f70ecb

Browse files
committed
feat: allow passing extra parameters to sqlite conn string
Do not expose the base parameters, as they are required for COSI to work correctly. Add a flag for the additional parameters, which can for example be used for configuring the SQLite db to be "suitable" for litestream to be able to make backups of it. Also, add `_pragma=synchronous(NORMAL)` to the base parameters, as it is recommended for better performance when WAL mode is enabled. Signed-off-by: Utku Ozdemir <[email protected]>
1 parent 59f4fff commit 3f70ecb

File tree

5 files changed

+54
-14
lines changed

5 files changed

+54
-14
lines changed

cmd/omni/cmd/cmd.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import (
2424
"github.com/siderolabs/omni/client/pkg/panichandler"
2525
"github.com/siderolabs/omni/cmd/omni/pkg/app"
2626
"github.com/siderolabs/omni/internal/backend/runtime/omni"
27+
"github.com/siderolabs/omni/internal/backend/runtime/omni/sqlite"
2728
"github.com/siderolabs/omni/internal/pkg/auth/actor"
2829
"github.com/siderolabs/omni/internal/pkg/config"
2930
"github.com/siderolabs/omni/internal/version"
@@ -629,6 +630,13 @@ func defineStorageFlags() {
629630
cmdConfig.Storage.SQLite.Path,
630631
"path of the file for sqlite-backed secondary storage for frequently updated data, machine and audit logs.",
631632
)
633+
rootCmd.Flags().StringVar(
634+
&cmdConfig.Storage.SQLite.ExtraParams,
635+
"sqlite-storage-extra-params",
636+
cmdConfig.Storage.SQLite.ExtraParams,
637+
fmt.Sprintf("extra DSN (connection string) parameters for sqlite database connection. "+
638+
"they will be appended to base params of %q. they must not start with ampersand (&).", sqlite.BaseParams),
639+
)
632640
}
633641

634642
func defineRegistriesFlags() {
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
// Copyright (c) 2025 Sidero Labs, Inc.
2+
//
3+
// Use of this software is governed by the Business Source License
4+
// included in the LICENSE file.
5+
6+
package sqlite
7+
8+
import (
9+
"database/sql"
10+
"fmt"
11+
"os"
12+
"path/filepath"
13+
14+
"github.com/siderolabs/omni/internal/pkg/config"
15+
)
16+
17+
// BaseParams are the base DSN parameters used for SQLite connections.
18+
const BaseParams = "_txlock=immediate&_pragma=busy_timeout(50000)&_pragma=journal_mode(WAL)&_pragma=synchronous(NORMAL)"
19+
20+
// OpenDB opens a SQLite database with the given configuration.
21+
func OpenDB(config config.SQLite) (*sql.DB, error) {
22+
if err := os.MkdirAll(filepath.Dir(config.Path), 0o755); err != nil {
23+
return nil, fmt.Errorf("failed to create directory for sqlite database %q: %w", config.Path, err)
24+
}
25+
26+
dsn := "file:" + config.Path + "?" + BaseParams
27+
if config.ExtraParams != "" {
28+
dsn += "&" + config.ExtraParams
29+
}
30+
31+
db, err := sql.Open("sqlite", dsn)
32+
if err != nil {
33+
return nil, fmt.Errorf("failed to open sqlite database %q: %w", dsn, err)
34+
}
35+
36+
return db, nil
37+
}

internal/backend/runtime/omni/state.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -127,7 +127,7 @@ func NewState(ctx context.Context, params *config.Params, logger *zap.Logger, me
127127
virtualState := virtual.NewState(state.WrapCore(defaultPersistentState.State))
128128

129129
secondaryPersistentState, err := newSQLitePersistentState(
130-
ctx, params.Storage.SQLite.Path, logger)
130+
ctx, params.Storage.SQLite, logger)
131131
if err != nil {
132132
return nil, fmt.Errorf("failed to create SQLite state for secondary storage: %w", err)
133133
}

internal/backend/runtime/omni/state_sqlite.go

Lines changed: 6 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,8 @@ package omni
77

88
import (
99
"context"
10-
"database/sql"
1110
"fmt"
1211
"os"
13-
"path/filepath"
1412

1513
"github.com/cosi-project/runtime/pkg/resource"
1614
"github.com/cosi-project/runtime/pkg/state"
@@ -22,18 +20,14 @@ import (
2220

2321
"github.com/siderolabs/omni/client/pkg/omni/resources"
2422
"github.com/siderolabs/omni/client/pkg/omni/resources/omni"
23+
omnisqlite "github.com/siderolabs/omni/internal/backend/runtime/omni/sqlite"
24+
"github.com/siderolabs/omni/internal/pkg/config"
2525
)
2626

27-
func newSQLitePersistentState(ctx context.Context, path string, logger *zap.Logger) (*PersistentState, error) {
28-
if err := os.MkdirAll(filepath.Dir(path), 0o755); err != nil {
29-
return nil, fmt.Errorf("failed to create directory for sqlite database %q: %w", path, err)
30-
}
31-
32-
dsn := "file:" + path + "?_txlock=immediate&_pragma=busy_timeout(50000)&_pragma=journal_mode(WAL)"
33-
34-
db, err := sql.Open("sqlite", dsn)
27+
func newSQLitePersistentState(ctx context.Context, config config.SQLite, logger *zap.Logger) (*PersistentState, error) {
28+
db, err := omnisqlite.OpenDB(config)
3529
if err != nil {
36-
return nil, fmt.Errorf("failed to open sqlite database %q: %w", dsn, err)
30+
return nil, err
3731
}
3832

3933
st, err := sqlite.NewState(ctx, db, store.ProtobufMarshaler{},
@@ -43,7 +37,7 @@ func newSQLitePersistentState(ctx context.Context, path string, logger *zap.Logg
4337
if err != nil {
4438
db.Close() //nolint:errcheck
4539

46-
return nil, fmt.Errorf("failed to create sqlite state (database %q): %w", dsn, err)
40+
return nil, fmt.Errorf("failed to create sqlite state (path %q): %w", config.Path, err)
4741
}
4842

4943
return &PersistentState{

internal/pkg/config/storage.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,8 @@ type BoltDB struct {
4545

4646
// SQLite defines sqlite storage configs.
4747
type SQLite struct {
48-
Path string `yaml:"path"`
48+
Path string `yaml:"path"`
49+
ExtraParams string `yaml:"extraParams"`
4950
}
5051

5152
// EtcdParams defines etcd storage configs.

0 commit comments

Comments
 (0)