initial database connection and migration

This commit is contained in:
Annika Hannig 2022-01-12 11:50:51 +01:00
parent f589322cfe
commit 2c7fab0a38
8 changed files with 273 additions and 8 deletions

1
db/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
_data/

13
db/compose.yml Normal file
View File

@ -0,0 +1,13 @@
version: "3"
services:
postgres:
image: postgres:11
ports:
- 5432:5432
environment:
POSTGRES_PASSWORD: postgres
volumes:
- ./_data:/var/lib/postgresql/data

70
db/init.sh Executable file
View File

@ -0,0 +1,70 @@
#!/usr/bin/env sh
######################################################################
# @author : annika
# @file : init
# @created : Tuesday Jan 11, 2022 15:35:20 CET
# @description : Initialize the database
######################################################################
if [ -z $PSQL ]; then
PSQL="psql"
fi
if [ -z $PGHOST ]; then
export PGHOST="localhost"
fi
if [ -z $PGPORT ]; then
export PGPORT="5432"
fi
if [ -z $PGDATABASE ]; then
export PGDATABASE="alice"
fi
if [ -z $PGUSER ]; then
export PGUSER="postgres"
fi
if [ -z $PGPASSWORD ]; then
export PGPASSWORD="postgres"
fi
## Commandline opts:
OPT_USAGE=0
OPT_TESTING=0
OPT_CLEAR=0
while [ $# -gt 0 ]; do
case "$1" in
-h) OPT_USAGE=1 ;;
-t) OPT_TESTING=1 ;;
-c) OPT_CLEAR=1 ;;
esac
shift
done
if [ $OPT_USAGE -eq 1 ]; then
echo "Options:"
echo " -h Show this helpful text"
echo " -c Drop and create the database"
echo " -t Make a test database"
exit
fi
if [ $OPT_TESTING -eq 1 ]; then
echo "++ using test database"
NAME="${PGDATABASE}_test"
export PGDATABASE=$NAME
fi
if [ $OPT_CLEAR -eq 1 ]; then
echo "++ clearing database"
$PSQL template1 -c "DROP DATABASE $PGDATABASE"
$PSQL template1 -c "CREATE DATABASE $PGDATABASE"
fi
######################################################################

View File

@ -10,11 +10,6 @@ import (
)
var (
// ErrNotInitialized will be returned if the
// database pool is accessed before initializing using
// Connect.
ErrNotInitialized = errors.New("store not initialized, pool not ready")
// ErrMaxConnsUnconfigured will be returned, if the
// the maximum connections are zero.
ErrMaxConnsUnconfigured = errors.New("MaxConns not configured")

View File

@ -0,0 +1,46 @@
package postgres
import (
"context"
"os"
"testing"
"github.com/jackc/pgx/v4/pgxpool"
)
// ConnectTest uses defaults for the connection
// environment variable is not set.
func ConnectTest() *pgxpool.Pool {
ctx := context.Background()
url := os.Getenv("ALICE_TEST_DB_URL")
if url == "" {
url = "postgres://postgres:postgres@localhost:5432/alice_test"
}
p, err := Connect(ctx, &ConnectOpts{
URL: url,
MinConns: 2,
MaxConns: 16})
if err != nil {
panic(err)
}
m := NewManager(p)
err = m.Initialize(ctx)
if err != nil {
panic(err)
}
return p
}
func TestInitialize(t *testing.T) {
p := ConnectTest()
m := NewManager(p)
s := m.Status(context.Background())
if s.Error != nil {
t.Error(s.Error)
}
if s.Migrated == false {
t.Error("schema is not migrated, current:", s.SchemaVersion)
}
}

View File

@ -0,0 +1,102 @@
package postgres
import (
"context"
_ "embed" // embed schema
"log"
"time"
"github.com/jackc/pgx/v4/pgxpool"
)
// Include the schema through embedding
//go:embed schema.sql
var schema string
// CurrentSchemaVersion is the current version of the schema
const CurrentSchemaVersion = 1
var (
// ErrNotInitialized is returned when the database
// schema is not migrated yet
)
// Status is the database / store status
type Status struct {
Migrated bool `json:"migrated"`
SchemaVersion int `json:"schema_version"`
SchemaAppliedAt time.Time `json:"schema_applied_at"`
Error error `json:"error"`
}
// Log writes the status into the log
func (s *Status) Log() {
log.Println(
"Database migrated:", s.Migrated)
log.Println(
"Schema version:", s.SchemaVersion,
"applied at:", s.SchemaAppliedAt)
}
// The Manager supervises the database. It can migrate the
// schema and retrieve a status.
type Manager struct {
pool *pgxpool.Pool
}
// NewManager creates a new database manager
func NewManager(pool *pgxpool.Pool) *Manager {
return &Manager{
pool: pool,
}
}
// Start the background jobs for database management
func (m *Manager) Start(ctx context.Context) {
m.Status(ctx).Log()
}
// Status retrieves the current schema version
// and checks if migrated. In case an error occures,
// it will be included in the result.
func (m *Manager) Status(ctx context.Context) *Status {
status := &Status{}
qry := `
SELECT version, applied_at FROM __meta__
ORDER BY version DESC
LIMIT 1
`
err := m.pool.QueryRow(ctx, qry).Scan(
&status.SchemaVersion,
&status.SchemaAppliedAt,
)
if err != nil {
status.Error = err
return status
}
// Is the database migrated?
status.Migrated = CurrentSchemaVersion == status.SchemaVersion
return status
}
// Migrate applies the database intialisation script if required.
func (m *Manager) Migrate(ctx context.Context) error {
s := m.Status(ctx)
if s.Migrated {
return nil
}
return m.Initialize(ctx)
}
// Initialize will apply the database schema. This will clear the
// database. However for now we treat the state as disposable.
func (m *Manager) Initialize(ctx context.Context) error {
_, err := m.pool.Exec(ctx, schema)
if err != nil {
return err
}
return nil
}

View File

@ -0,0 +1,33 @@
package postgres
import (
"context"
"github.com/alice-lg/alice-lg/pkg/api"
"github.com/jackc/pgx/v4/pgxpool"
)
// NeighborsBackend implements a neighbors store
// using a postgres database
type NeighborsBackend struct {
pool *pgxpool.Pool
}
// NewNeighborsBackend initializes the backend
// with a pool.
func NewNeighborsBackend(pool *pgxpool.Pool) *NeighborsBackend {
b := &NeighborsBackend{
pool: pool,
}
return b
}
// SetNeighbors updates the current neighbors of
// a route server identified by sourceID
func (b *NeighborsBackend) SetNeighbors(
ctx context.Context,
sourceID string,
neighbors api.Neighbors,
) error {
return nil
}

View File

@ -5,9 +5,14 @@
-- ----------------------
--
-- %% Author: annika
-- %% Description: Create alice-lg db schema.
-- %% Description: Apply alice-lg db schema.
--
-- Clear state
DROP TABLE IF EXISTS routes;
DROP TABLE IF EXISTS neighbors;
DROP TABLE IF EXISTS __meta__;
-- Neighbors
CREATE TABLE neighbors (
id VARCHAR(255) NOT NULL PRIMARY KEY,
@ -19,7 +24,7 @@ CREATE TABLE neighbors (
neighbor jsonb NOT NULL,
-- Timestamps
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
);
CREATE INDEX idx_neighbors_rs_id
@ -39,7 +44,7 @@ CREATE TABLE routes (
route jsonb NOT NULL,
-- Timestamps
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
);
CREATE INDEX idx_routes_network ON routes ( network );