diff options
author | Julien Dessaux | 2025-02-12 20:20:02 +0100 |
---|---|---|
committer | Julien Dessaux | 2025-02-12 20:31:25 +0100 |
commit | 5611f5b468303aee16f5cb21c8eda9a44bb5d013 (patch) | |
tree | 41e99f399b358d8f58cf584b8dd9c215d6402fd4 /golang/pkg/database/db.go | |
parent | [golang] updated dependencies (diff) | |
download | spacetraders-5611f5b468303aee16f5cb21c8eda9a44bb5d013.tar.gz spacetraders-5611f5b468303aee16f5cb21c8eda9a44bb5d013.tar.bz2 spacetraders-5611f5b468303aee16f5cb21c8eda9a44bb5d013.zip |
[golang] rewrite database handling
Diffstat (limited to '')
-rw-r--r-- | golang/pkg/database/db.go | 114 |
1 files changed, 110 insertions, 4 deletions
diff --git a/golang/pkg/database/db.go b/golang/pkg/database/db.go index 3e07db3..cb15e52 100644 --- a/golang/pkg/database/db.go +++ b/golang/pkg/database/db.go @@ -3,17 +3,123 @@ package database import ( "context" "database/sql" + "fmt" + "runtime" ) +func initDB(ctx context.Context, url string) (*sql.DB, error) { + db, err := sql.Open("sqlite3", url) + if err != nil { + return nil, fmt.Errorf("failed to open database: %w", err) + } + defer func() { + if err != nil { + _ = db.Close() + } + }() + if _, err = db.ExecContext(ctx, "PRAGMA busy_timeout = 5000"); err != nil { + return nil, fmt.Errorf("failed to set pragma: %w", err) + } + + return db, nil +} + type DB struct { - ctx context.Context - db *sql.DB + ctx context.Context + readDB *sql.DB + writeDB *sql.DB +} + +func NewDB(ctx context.Context, url string) (*DB, error) { + readDB, err := initDB(ctx, url) + if err != nil { + return nil, fmt.Errorf("failed to init read database connection: %w", err) + } + defer func() { + if err != nil { + _ = readDB.Close() + } + }() + readDB.SetMaxOpenConns(max(4, runtime.NumCPU())) + + writeDB, err := initDB(ctx, url) + if err != nil { + return nil, fmt.Errorf("failed to init write database connection: %w", err) + } + defer func() { + if err != nil { + _ = writeDB.Close() + } + }() + writeDB.SetMaxOpenConns(1) + + db := DB{ + ctx: ctx, + readDB: readDB, + writeDB: writeDB, + } + pragmas := []struct { + key string + value string + }{ + {"foreign_keys", "ON"}, + {"cache_size", "10000000"}, + {"journal_mode", "WAL"}, + {"synchronous", "NORMAL"}, + } + for _, pragma := range pragmas { + if _, err = db.Exec(fmt.Sprintf("PRAGMA %s = %s", pragma.key, pragma.value)); err != nil { + return nil, fmt.Errorf("failed to set pragma: %w", err) + } + } + if err = db.migrate(); err != nil { + return nil, fmt.Errorf("failed to migrate: %w", err) + } + + return &db, nil +} + +func (db *DB) Close() error { + if err := db.readDB.Close(); err != nil { + _ = db.writeDB.Close() + return fmt.Errorf("failed to close read database connection: %w", err) + } + if err := db.writeDB.Close(); err != nil { + return fmt.Errorf("failed to close write database connection: %w", err) + } + return nil } func (db *DB) Exec(query string, args ...any) (sql.Result, error) { - return db.db.ExecContext(db.ctx, query, args...) + return db.writeDB.ExecContext(db.ctx, query, args...) +} + +func (db *DB) Query(query string, args ...any) (*sql.Rows, error) { + return db.readDB.QueryContext(db.ctx, query, args...) } func (db *DB) QueryRow(query string, args ...any) *sql.Row { - return db.db.QueryRowContext(db.ctx, query, args...) + return db.readDB.QueryRowContext(db.ctx, query, args...) +} + +func (db *DB) WithTransaction(f func(tx *sql.Tx) error) error { + tx, err := db.writeDB.Begin() + if err != nil { + return fmt.Errorf("failed to begin transaction: %w", err) + } + defer func() { + if err != nil { + if err2 := tx.Rollback(); err2 != nil { + panic(fmt.Sprintf("failed to rollback transaction: %+v. Reason for rollback: %+v", err2, err)) + } + } + }() + if err = f(tx); err != nil { + return fmt.Errorf("failed to execute function inside transaction: %w", err) + } else { + if err = tx.Commit(); err != nil { + err = fmt.Errorf("failed to commit transaction: %w", err) + } + } + return err } |