Skip to content

Commit

Permalink
Remove dbMetrics field from SQLStore (#1327)
Browse files Browse the repository at this point in the history
  • Loading branch information
ChrisSchinnerl authored Jun 24, 2024
1 parent 866bf8f commit 1b1be9d
Show file tree
Hide file tree
Showing 7 changed files with 108 additions and 58 deletions.
3 changes: 2 additions & 1 deletion bus/accounts.go
Original file line number Diff line number Diff line change
Expand Up @@ -164,14 +164,15 @@ func (a *accounts) SetBalance(id rhpv3.Account, hk types.PublicKey, balance *big
acc.Balance.Set(balance)
acc.CleanShutdown = true
acc.RequiresSync = false // resetting the balance resets the sync field
balanceAfter := acc.Balance.String()
acc.mu.Unlock()

// Log resets.
a.logger.Infow("account balance was reset",
"account", acc.ID,
"host", acc.HostKey.String(),
"balanceBefore", balanceBefore,
"balanceAfter", acc.Balance.String(),
"balanceAfter", balanceAfter,
"driftBefore", driftBefore,
"driftAfter", acc.Drift.String(),
"delta", delta.String())
Expand Down
27 changes: 23 additions & 4 deletions internal/node/node.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@ import (
"go.sia.tech/renterd/bus"
"go.sia.tech/renterd/config"
"go.sia.tech/renterd/stores"
"go.sia.tech/renterd/stores/sql"
"go.sia.tech/renterd/stores/sql/mysql"
"go.sia.tech/renterd/stores/sql/sqlite"
"go.sia.tech/renterd/wallet"
"go.sia.tech/renterd/webhooks"
"go.sia.tech/renterd/worker"
Expand Down Expand Up @@ -98,7 +101,8 @@ func NewBus(cfg BusConfig, dir string, seed types.PrivateKey, l *zap.Logger) (ht
}

// create database connections
var dbConn, dbMetricsConn gorm.Dialector
var dbConn gorm.Dialector
var dbMetrics sql.MetricsDatabase
if cfg.Database.MySQL.URI != "" {
// create MySQL connections
dbConn = stores.NewMySQLConnection(
Expand All @@ -107,12 +111,19 @@ func NewBus(cfg BusConfig, dir string, seed types.PrivateKey, l *zap.Logger) (ht
cfg.Database.MySQL.URI,
cfg.Database.MySQL.Database,
)
dbMetricsConn = stores.NewMySQLConnection(
dbm, err := mysql.Open(
cfg.Database.MySQL.User,
cfg.Database.MySQL.Password,
cfg.Database.MySQL.URI,
cfg.Database.MySQL.MetricsDatabase,
)
if err != nil {
return nil, nil, nil, fmt.Errorf("failed to open MySQL metrics database: %w", err)
}
dbMetrics, err = mysql.NewMetricsDatabase(dbm, l.Named("metrics").Sugar(), cfg.DatabaseLog.SlowThreshold, cfg.DatabaseLog.SlowThreshold)
if err != nil {
return nil, nil, nil, fmt.Errorf("failed to create MySQL metrics database: %w", err)
}
} else {
// create database directory
dbDir := filepath.Join(dir, "db")
Expand All @@ -122,7 +133,15 @@ func NewBus(cfg BusConfig, dir string, seed types.PrivateKey, l *zap.Logger) (ht

// create SQLite connections
dbConn = stores.NewSQLiteConnection(filepath.Join(dbDir, "db.sqlite"))
dbMetricsConn = stores.NewSQLiteConnection(filepath.Join(dbDir, "metrics.sqlite"))

dbm, err := sqlite.Open(filepath.Join(dbDir, "metrics.sqlite"))
if err != nil {
return nil, nil, nil, fmt.Errorf("failed to open SQLite metrics database: %w", err)
}
dbMetrics, err = sqlite.NewMetricsDatabase(dbm, l.Named("metrics").Sugar(), cfg.DatabaseLog.SlowThreshold, cfg.DatabaseLog.SlowThreshold)
if err != nil {
return nil, nil, nil, fmt.Errorf("failed to create SQLite metrics database: %w", err)
}
}

// create database logger
Expand All @@ -141,8 +160,8 @@ func NewBus(cfg BusConfig, dir string, seed types.PrivateKey, l *zap.Logger) (ht
announcementMaxAge := time.Duration(cfg.AnnouncementMaxAgeHours) * time.Hour
sqlStore, ccid, err := stores.NewSQLStore(stores.Config{
Conn: dbConn,
ConnMetrics: dbMetricsConn,
Alerts: alerts.WithOrigin(alertsMgr, "bus"),
DBMetrics: dbMetrics,
PartialSlabDir: sqlStoreDir,
Migrate: true,
AnnouncementMaxAge: announcementMaxAge,
Expand Down
2 changes: 1 addition & 1 deletion stores/metrics_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -224,7 +224,7 @@ func TestContractMetrics(t *testing.T) {

// Check how many metrics were recorded.
var n int64
if err := ss.dbMetrics.Raw("SELECT COUNT(*) FROM contracts").Scan(&n).Error; err != nil {
if err := ss.DBMetrics().QueryRow(context.Background(), "SELECT COUNT(*) FROM contracts").Scan(&n); err != nil {
t.Fatal(err)
} else if n != 2 {
t.Fatalf("expected 2 metrics, got %v", n)
Expand Down
36 changes: 10 additions & 26 deletions stores/sql.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ type (
// Config contains all params for creating a SQLStore
Config struct {
Conn gorm.Dialector
ConnMetrics gorm.Dialector
DBMetrics sql.MetricsDatabase
Alerts alerts.Alerter
PartialSlabDir string
Migrate bool
Expand All @@ -56,12 +56,11 @@ type (

// SQLStore is a helper type for interacting with a SQL-based backend.
SQLStore struct {
alerts alerts.Alerter
db *gorm.DB
dbMetrics *gorm.DB
bMain sql.Database
bMetrics sql.MetricsDatabase
logger *zap.SugaredLogger
alerts alerts.Alerter
db *gorm.DB
bMain sql.Database
bMetrics sql.MetricsDatabase
logger *zap.SugaredLogger

slabBufferMgr *SlabBufferManager

Expand Down Expand Up @@ -171,38 +170,24 @@ func NewSQLStore(cfg Config) (*SQLStore, modules.ConsensusChangeID, error) {
if err != nil {
return nil, modules.ConsensusChangeID{}, fmt.Errorf("failed to open SQL db")
}
dbMetrics, err := gorm.Open(cfg.ConnMetrics, &gorm.Config{
Logger: cfg.GormLogger, // custom logger
})
if err != nil {
return nil, modules.ConsensusChangeID{}, fmt.Errorf("failed to open metrics db")
}
l := cfg.Logger.Named("sql")

sqlDB, err := db.DB()
if err != nil {
return nil, modules.ConsensusChangeID{}, fmt.Errorf("failed to fetch db: %v", err)
}
sqlDBMetrics, err := dbMetrics.DB()
if err != nil {
return nil, modules.ConsensusChangeID{}, fmt.Errorf("failed to fetch metrics db: %v", err)
}

// Print DB version
var dbMain sql.Database
var bMetrics sql.MetricsDatabase
var mainErr, metricsErr error
dbMetrics := cfg.DBMetrics
var mainErr error
if cfg.Conn.Name() == "sqlite" {
dbMain, mainErr = sqlite.NewMainDatabase(sqlDB, l, cfg.LongQueryDuration, cfg.LongTxDuration)
bMetrics, metricsErr = sqlite.NewMetricsDatabase(sqlDBMetrics, l, cfg.LongQueryDuration, cfg.LongTxDuration)
} else {
dbMain, mainErr = mysql.NewMainDatabase(sqlDB, l, cfg.LongQueryDuration, cfg.LongTxDuration)
bMetrics, metricsErr = mysql.NewMetricsDatabase(sqlDBMetrics, l, cfg.LongQueryDuration, cfg.LongTxDuration)
}
if mainErr != nil {
return nil, modules.ConsensusChangeID{}, fmt.Errorf("failed to create main database: %v", mainErr)
} else if metricsErr != nil {
return nil, modules.ConsensusChangeID{}, fmt.Errorf("failed to create metrics database: %v", metricsErr)
}

ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
Expand All @@ -217,7 +202,7 @@ func NewSQLStore(cfg Config) (*SQLStore, modules.ConsensusChangeID, error) {
if cfg.Migrate {
if err := dbMain.Migrate(context.Background()); err != nil {
return nil, modules.ConsensusChangeID{}, fmt.Errorf("failed to perform migrations: %v", err)
} else if err := bMetrics.Migrate(context.Background()); err != nil {
} else if err := dbMetrics.Migrate(context.Background()); err != nil {
return nil, modules.ConsensusChangeID{}, fmt.Errorf("failed to perform migrations for metrics db: %v", err)
}
}
Expand Down Expand Up @@ -250,9 +235,8 @@ func NewSQLStore(cfg Config) (*SQLStore, modules.ConsensusChangeID, error) {
alerts: cfg.Alerts,
ccid: ccid,
db: db,
dbMetrics: dbMetrics,
bMain: dbMain,
bMetrics: bMetrics,
bMetrics: dbMetrics,
logger: l,
knownContracts: isOurContract,
lastSave: time.Now(),
Expand Down
5 changes: 5 additions & 0 deletions stores/sql/mysql/common.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package mysql

import (
"context"
dsql "database/sql"
"embed"
"fmt"

Expand All @@ -12,6 +13,10 @@ var deadlockMsgs = []string{
"Deadlock found when trying to get lock",
}

func Open(user, password, addr, dbName string) (*dsql.DB, error) {
return dsql.Open("mysql", fmt.Sprintf("%s:%s@tcp(%s)/%s?charset=utf8mb4&parseTime=True&loc=Local&multiStatements=true", user, password, addr, dbName))
}

//go:embed all:migrations/*
var migrationsFs embed.FS

Expand Down
8 changes: 8 additions & 0 deletions stores/sql/sqlite/common.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,14 @@ var deadlockMsgs = []string{
//go:embed all:migrations/*
var migrationsFs embed.FS

func Open(path string) (*dsql.DB, error) {
return dsql.Open("sqlite3", fmt.Sprintf("file:%s?_busy_timeout=30000&_foreign_keys=1&_journal_mode=WAL&_secure_delete=false&_cache_size=65536", path))
}

func OpenEphemeral(name string) (*dsql.DB, error) {
return dsql.Open("sqlite3", fmt.Sprintf("file:%s?mode=memory&cache=shared&_foreign_keys=1", name))
}

func applyMigration(ctx context.Context, db *sql.DB, fn func(tx sql.Tx) (bool, error)) (err error) {
if _, err := db.Exec(ctx, "PRAGMA foreign_keys=OFF"); err != nil {
return fmt.Errorf("failed to disable foreign keys: %w", err)
Expand Down
85 changes: 59 additions & 26 deletions stores/sql_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package stores
import (
"bytes"
"context"
dsql "database/sql"
"encoding/hex"
"errors"
"fmt"
Expand All @@ -18,7 +19,11 @@ import (
"go.sia.tech/renterd/alerts"
"go.sia.tech/renterd/api"
"go.sia.tech/renterd/config"
isql "go.sia.tech/renterd/internal/sql"
"go.sia.tech/renterd/object"
sql "go.sia.tech/renterd/stores/sql"
"go.sia.tech/renterd/stores/sql/mysql"
"go.sia.tech/renterd/stores/sql/sqlite"
"go.sia.tech/siad/modules"
"go.uber.org/zap"
"go.uber.org/zap/zapcore"
Expand Down Expand Up @@ -64,47 +69,63 @@ func randomDBName() string {
return "db" + hex.EncodeToString(frand.Bytes(16))
}

func (cfg *testSQLStoreConfig) dbConnections() (gorm.Dialector, gorm.Dialector, error) {
// create MySQL connections if URI is set
if mysql := config.MySQLConfigFromEnv(); mysql.URI != "" {
func (cfg *testSQLStoreConfig) dbConnections() (gorm.Dialector, sql.MetricsDatabase, error) {
var connMain gorm.Dialector
var dbm *dsql.DB
var dbMetrics sql.MetricsDatabase
var err error
if mysqlCfg := config.MySQLConfigFromEnv(); mysqlCfg.URI != "" {
// create MySQL connections if URI is set

// sanity check config
if cfg.persistent {
return nil, nil, errors.New("invalid store config, can't use both persistent and dbURI")
}

// use db names from config if not set
if mysql.Database == "" {
mysql.Database = cfg.dbName
if mysqlCfg.Database == "" {
mysqlCfg.Database = cfg.dbName
}
if mysql.MetricsDatabase == "" {
mysql.MetricsDatabase = cfg.dbMetricsName
if mysqlCfg.MetricsDatabase == "" {
mysqlCfg.MetricsDatabase = cfg.dbMetricsName
}

// use a tmp connection to precreate the two databases
if tmpDB, err := gorm.Open(NewMySQLConnection(mysql.User, mysql.Password, mysql.URI, "")); err != nil {
if tmpDB, err := gorm.Open(NewMySQLConnection(mysqlCfg.User, mysqlCfg.Password, mysqlCfg.URI, "")); err != nil {
return nil, nil, err
} else if err := tmpDB.Exec(fmt.Sprintf("CREATE DATABASE IF NOT EXISTS `%s`", mysql.Database)).Error; err != nil {
} else if err := tmpDB.Exec(fmt.Sprintf("CREATE DATABASE IF NOT EXISTS `%s`", mysqlCfg.Database)).Error; err != nil {
return nil, nil, err
} else if err := tmpDB.Exec(fmt.Sprintf("CREATE DATABASE IF NOT EXISTS `%s`", mysql.MetricsDatabase)).Error; err != nil {
} else if err := tmpDB.Exec(fmt.Sprintf("CREATE DATABASE IF NOT EXISTS `%s`", mysqlCfg.MetricsDatabase)).Error; err != nil {
return nil, nil, err
}

return NewMySQLConnection(mysql.User, mysql.Password, mysql.URI, mysql.Database),
NewMySQLConnection(mysql.User, mysql.Password, mysql.URI, mysql.MetricsDatabase),
nil
connMain = NewMySQLConnection(mysqlCfg.User, mysqlCfg.Password, mysqlCfg.URI, mysqlCfg.Database)
dbm, err = mysql.Open(mysqlCfg.User, mysqlCfg.Password, mysqlCfg.URI, mysqlCfg.MetricsDatabase)
if err != nil {
return nil, nil, fmt.Errorf("failed to open MySQL metrics database: %w", err)
}
dbMetrics, err = mysql.NewMetricsDatabase(dbm, zap.NewNop().Sugar(), 100*time.Millisecond, 100*time.Millisecond)
} else if cfg.persistent {
// create SQL connections if we want a persistent store
connMain = NewSQLiteConnection(filepath.Join(cfg.dir, "db.sqlite"))
dbm, err = sqlite.Open(filepath.Join(cfg.dir, "metrics.sqlite"))
if err != nil {
return nil, nil, fmt.Errorf("failed to open SQLite metrics database: %w", err)
}
dbMetrics, err = sqlite.NewMetricsDatabase(dbm, zap.NewNop().Sugar(), 100*time.Millisecond, 100*time.Millisecond)
} else {
// otherwise return ephemeral connections
connMain = NewEphemeralSQLiteConnection(cfg.dbName)
dbm, err = sqlite.OpenEphemeral(cfg.dbMetricsName)
if err != nil {
return nil, nil, fmt.Errorf("failed to open ephemeral SQLite metrics database: %w", err)
}
dbMetrics, err = sqlite.NewMetricsDatabase(dbm, zap.NewNop().Sugar(), 100*time.Millisecond, 100*time.Millisecond)
}

// create SQL connections if we want a persistent store
if cfg.persistent {
return NewSQLiteConnection(filepath.Join(cfg.dir, "db.sqlite")),
NewSQLiteConnection(filepath.Join(cfg.dir, "metrics.sqlite")),
nil
if err != nil {
return nil, nil, fmt.Errorf("failed to create metrics database: %w", err)
}

// otherwise return ephemeral connections
return NewEphemeralSQLiteConnection(cfg.dbName),
NewEphemeralSQLiteConnection(cfg.dbMetricsName),
nil
return connMain, dbMetrics, nil
}

// newTestSQLStore creates a new SQLStore for testing.
Expand All @@ -125,7 +146,7 @@ func newTestSQLStore(t *testing.T, cfg testSQLStoreConfig) *testSQLStore {
}

// create db connections
conn, connMetrics, err := cfg.dbConnections()
conn, dbMetrics, err := cfg.dbConnections()
if err != nil {
t.Fatal("failed to create db connections", err)
}
Expand All @@ -134,8 +155,8 @@ func newTestSQLStore(t *testing.T, cfg testSQLStoreConfig) *testSQLStore {
alerts := alerts.WithOrigin(alerts.NewManager(), "test")
sqlStore, _, err := NewSQLStore(Config{
Conn: conn,
ConnMetrics: connMetrics,
Alerts: alerts,
DBMetrics: dbMetrics,
PartialSlabDir: cfg.dir,
Migrate: !cfg.skipMigrate,
AnnouncementMaxAge: time.Hour,
Expand Down Expand Up @@ -165,6 +186,18 @@ func newTestSQLStore(t *testing.T, cfg testSQLStoreConfig) *testSQLStore {
}
}

func (s *testSQLStore) DBMetrics() *isql.DB {
switch db := s.bMetrics.(type) {
case *sqlite.MetricsDatabase:
return db.DB()
case *mysql.MetricsDatabase:
return db.DB()
default:
s.t.Fatal("unknown db type", db)
}
panic("unreachable")
}

func (s *testSQLStore) Close() error {
if err := s.SQLStore.Close(); err != nil {
s.t.Error(err)
Expand Down

0 comments on commit 1b1be9d

Please sign in to comment.