mirror of
https://github.com/lightningnetwork/lnd.git
synced 2024-11-19 09:53:54 +01:00
5a7d7c2e4f
This commit provides the scaffolding for using the new sql stores. The new interfaces, structs and methods are in sync with other projects like Taproot Assets. - Transactional Queries: the sqldb package defines the interfaces required to execute transactional queries to our storage interface. - Migration Files Embedded: the migration files are embedded into the binary. - Database Migrations: I kept the use of 'golang-migrate' to ensure our codebase remains in sync with the other projects, but can be changed. - Build Flags for Conditional DB Target: flexibility to specify our database target at compile-time based on the build flags in the same way we do with our kv stores. - Update modules: ran `go mod tidy`.
142 lines
3.9 KiB
Go
142 lines
3.9 KiB
Go
package sqldb
|
|
|
|
import (
|
|
"database/sql"
|
|
"fmt"
|
|
"testing"
|
|
"time"
|
|
|
|
postgres_migrate "github.com/golang-migrate/migrate/v4/database/postgres"
|
|
_ "github.com/golang-migrate/migrate/v4/source/file" // Read migrations from files. // nolint:lll
|
|
"github.com/lightningnetwork/lnd/sqldb/sqlc"
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
const (
|
|
dsnTemplate = "postgres://%v:%v@%v:%d/%v?sslmode=%v"
|
|
)
|
|
|
|
var (
|
|
// DefaultPostgresFixtureLifetime is the default maximum time a Postgres
|
|
// test fixture is being kept alive. After that time the docker
|
|
// container will be terminated forcefully, even if the tests aren't
|
|
// fully executed yet. So this time needs to be chosen correctly to be
|
|
// longer than the longest expected individual test run time.
|
|
DefaultPostgresFixtureLifetime = 10 * time.Minute
|
|
)
|
|
|
|
// PostgresConfig holds the postgres database configuration.
|
|
//
|
|
//nolint:lll
|
|
type PostgresConfig struct {
|
|
SkipMigrations bool `long:"skipmigrations" description:"Skip applying migrations on startup."`
|
|
Host string `long:"host" description:"Database server hostname."`
|
|
Port int `long:"port" description:"Database server port."`
|
|
User string `long:"user" description:"Database user."`
|
|
Password string `long:"password" description:"Database user's password."`
|
|
DBName string `long:"dbname" description:"Database name to use."`
|
|
MaxOpenConnections int `long:"maxconnections" description:"Max open connections to keep alive to the database server."`
|
|
RequireSSL bool `long:"requiressl" description:"Whether to require using SSL (mode: require) when connecting to the server."`
|
|
}
|
|
|
|
// DSN returns the dns to connect to the database.
|
|
func (s *PostgresConfig) DSN(hidePassword bool) string {
|
|
var sslMode = "disable"
|
|
if s.RequireSSL {
|
|
sslMode = "require"
|
|
}
|
|
|
|
password := s.Password
|
|
if hidePassword {
|
|
// Placeholder used for logging the DSN safely.
|
|
password = "****"
|
|
}
|
|
|
|
return fmt.Sprintf(dsnTemplate, s.User, password, s.Host, s.Port,
|
|
s.DBName, sslMode)
|
|
}
|
|
|
|
// PostgresStore is a database store implementation that uses a Postgres
|
|
// backend.
|
|
type PostgresStore struct {
|
|
cfg *PostgresConfig
|
|
|
|
*BaseDB
|
|
}
|
|
|
|
// NewPostgresStore creates a new store that is backed by a Postgres database
|
|
// backend.
|
|
func NewPostgresStore(cfg *PostgresConfig) (*PostgresStore, error) {
|
|
log.Infof("Using SQL database '%s'", cfg.DSN(true))
|
|
|
|
rawDB, err := sql.Open("pgx", cfg.DSN(false))
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
maxConns := defaultMaxConns
|
|
if cfg.MaxOpenConnections > 0 {
|
|
maxConns = cfg.MaxOpenConnections
|
|
}
|
|
|
|
rawDB.SetMaxOpenConns(maxConns)
|
|
rawDB.SetMaxIdleConns(maxConns)
|
|
rawDB.SetConnMaxLifetime(connIdleLifetime)
|
|
|
|
if !cfg.SkipMigrations {
|
|
// Now that the database is open, populate the database with
|
|
// our set of schemas based on our embedded in-memory file
|
|
// system.
|
|
//
|
|
// First, we'll need to open up a new migration instance for
|
|
// our current target database: sqlite.
|
|
driver, err := postgres_migrate.WithInstance(
|
|
rawDB, &postgres_migrate.Config{},
|
|
)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
postgresFS := newReplacerFS(sqlSchemas, map[string]string{
|
|
"BLOB": "BYTEA",
|
|
"INTEGER PRIMARY KEY": "SERIAL PRIMARY KEY",
|
|
"TIMESTAMP": "TIMESTAMP WITHOUT TIME ZONE",
|
|
})
|
|
|
|
err = applyMigrations(
|
|
postgresFS, driver, "sqlc/migrations", cfg.DBName,
|
|
)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
|
|
queries := sqlc.New(rawDB)
|
|
|
|
return &PostgresStore{
|
|
cfg: cfg,
|
|
BaseDB: &BaseDB{
|
|
DB: rawDB,
|
|
Queries: queries,
|
|
},
|
|
}, nil
|
|
}
|
|
|
|
// NewTestPostgresDB is a helper function that creates a Postgres database for
|
|
// testing.
|
|
func NewTestPostgresDB(t *testing.T) *PostgresStore {
|
|
t.Helper()
|
|
|
|
t.Logf("Creating new Postgres DB for testing")
|
|
|
|
sqlFixture := NewTestPgFixture(t, DefaultPostgresFixtureLifetime)
|
|
store, err := NewPostgresStore(sqlFixture.GetConfig())
|
|
require.NoError(t, err)
|
|
|
|
t.Cleanup(func() {
|
|
sqlFixture.TearDown(t)
|
|
})
|
|
|
|
return store
|
|
}
|