Skip to content

Commit 8eca2d2

Browse files
committed
Add: database config for ro and rw pools
1 parent 2c8aa50 commit 8eca2d2

File tree

5 files changed

+112
-52
lines changed

5 files changed

+112
-52
lines changed

backend/.gitignore

+2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
tmp/
22
*.sqlite
3+
*.sqlite-shm
4+
*.sqlite-wal
35
main
46
stress/
57

backend/e2e/database_test.go

+79
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
package e2e
2+
3+
import (
4+
"context"
5+
"fmt"
6+
"testing"
7+
"time"
8+
9+
"github.com/robalb/morsechat/internal/db"
10+
)
11+
12+
func TestDatabase(t *testing.T) {
13+
t.Parallel()
14+
ctx := context.Background()
15+
ctx, cancel := context.WithTimeout(ctx, 5*time.Second)
16+
t.Cleanup(cancel)
17+
18+
19+
tempdb, cleandb, err := NewVolatileSqliteFile()
20+
if err != nil {
21+
t.Fatalf("Could not generate temporary db file")
22+
}
23+
t.Cleanup(cleandb)
24+
25+
dbWritePool, err := db.NewWritePool(tempdb, ctx)
26+
if err != nil {
27+
t.Fatalf("Failed to init database write pool: %v", err.Error())
28+
}
29+
dbReadPool, err := db.NewReadPool(tempdb, ctx)
30+
if err != nil {
31+
t.Fatalf("Failed to init database read pool: %v", err.Error())
32+
}
33+
err = db.ApplyMigrations(dbWritePool, ctx)
34+
if err != nil {
35+
t.Fatalf("Failed to apply database migrations: %v", err.Error())
36+
}
37+
38+
readQueries := db.New(dbReadPool)
39+
writeQueries := db.New(dbWritePool)
40+
41+
// perform a read query using the read pool
42+
{
43+
authors, err := readQueries.ListModerators(ctx)
44+
if err != nil {
45+
t.Fatalf("error query db: %v", err)
46+
}
47+
fmt.Println(authors)
48+
}
49+
50+
// perform a write query using the read pool
51+
{
52+
_, err := readQueries.CreateUser(ctx, db.CreateUserParams{
53+
Username: "lorem",
54+
Callsign: "US121X",
55+
Country: "US",
56+
RegistrationSession: "afakeuuidv4",
57+
})
58+
if err == nil {
59+
t.Fatalf("a write query on a readonly db connection did not raise errors")
60+
} else{
61+
fmt.Printf("db raised err as expected: %v", err)
62+
}
63+
}
64+
65+
// perform a write query using the write pool
66+
{
67+
_, err := writeQueries.CreateUser(ctx, db.CreateUserParams{
68+
Username: "lorem",
69+
Callsign: "US121X",
70+
Country: "US",
71+
RegistrationSession: "afakeuuidv4",
72+
})
73+
if err != nil {
74+
t.Fatalf("db error writing with a write connection")
75+
}
76+
}
77+
78+
}
79+

backend/internal/db/conn.go

+26-48
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,13 @@ import (
44
"context"
55
"database/sql"
66
_ "embed"
7+
"fmt"
8+
"strings"
79
)
810

911
//go:embed schema.sql
1012
var initscript string
1113

12-
// global, hardcoded sqlite config
13-
var sqliteConfig string = "?_foreign_keys=true"
14-
1514
// Temporary solution. We don't actually have migrations right now.
1615
// We just execute the content of schema.sql every time
1716
func ApplyMigrations(db *sql.DB, ctx context.Context) error {
@@ -26,6 +25,8 @@ func ApplyMigrations(db *sql.DB, ctx context.Context) error {
2625
// TODO: runtime pragmas, as seen here
2726
// https://github.com/mtlynch/picoshare/blob/master/store/sqlite/sqlite.go
2827
func NewTestConn(sqlitePath string, ctx context.Context) (*sql.DB, error) {
28+
// global, hardcoded sqlite config
29+
var sqliteConfig string = "?_foreign_keys=true"
2930
db, err := sql.Open("sqlite3", sqlitePath+sqliteConfig)
3031
if err != nil {
3132
return nil, err
@@ -35,58 +36,35 @@ func NewTestConn(sqlitePath string, ctx context.Context) (*sql.DB, error) {
3536
}
3637

3738
// Return a readonly connection to the sqlite database
38-
// TODO: proper config
3939
// TODO: ping()
4040
func NewReadPool(sqlitePath string, ctx context.Context) (*sql.DB, error) {
41-
return sql.Open("sqlite3", sqlitePath+sqliteConfig)
41+
// https://github.com/mattn/go-sqlite3/issues/1022#issuecomment-1067353980
42+
// https://github.com/mattn/go-sqlite3?tab=readme-ov-file#connection-string
43+
options := []string{
44+
"_foreign_keys=true",
45+
"_journal_mode=wal",
46+
"_busy_timeout=5000",
47+
"mode=ro",
48+
}
49+
connString := fmt.Sprintf("file:%s?%s", sqlitePath, strings.Join(options, "&"))
50+
return sql.Open("sqlite3", connString)
4251
}
4352

4453
// Return a read+write connection to the sqlite database
45-
// TODO: proper config
4654
func NewWritePool(sqlitePath string, ctx context.Context) (conn *sql.DB, err error) {
47-
conn, err = sql.Open("sqlite3", sqlitePath+sqliteConfig)
55+
// https://github.com/mattn/go-sqlite3/issues/1022#issuecomment-1067353980
56+
// https://github.com/mattn/go-sqlite3?tab=readme-ov-file#connection-string
57+
options := []string{
58+
"_foreign_keys=true",
59+
"_journal_mode=wal",
60+
"_txlock=immediate",
61+
"_busy_timeout=5000",
62+
"mode=rwc",
63+
}
64+
connString := fmt.Sprintf("file:%s?%s", sqlitePath, strings.Join(options, "&"))
65+
conn, err = sql.Open("sqlite3", connString)
4866
//sqlite does not support cuncurrent write
49-
//TODO: test performance implications
50-
// conn.SetMaxOpenConns(1)
67+
conn.SetMaxOpenConns(1)
5168
return
5269
}
5370

54-
// func SQLite(t *testing.T, migrations []string) (*sql.DB, func()) {
55-
// t.Helper()
56-
// // For each test, pick a new database name at random.
57-
// source, err := os.CreateTemp("", "sqltest_sqlite_")
58-
// if err != nil {
59-
// t.Fatal(err)
60-
// }
61-
// return CreateSQLiteDatabase(t, source.Name(), migrations)
62-
// }
63-
64-
// func CreateSQLiteDatabase(t *testing.T, path string, migrations []string) (*sql.DB, func()) {
65-
// t.Helper()
66-
67-
// t.Logf("open %s\n", path)
68-
// sdb, err := sql.Open("sqlite", path)
69-
// if err != nil {
70-
// t.Fatal(err)
71-
// }
72-
73-
// files, err := sqlpath.Glob(migrations)
74-
// if err != nil {
75-
// t.Fatal(err)
76-
// }
77-
// for _, f := range files {
78-
// blob, err := os.ReadFile(f)
79-
// if err != nil {
80-
// t.Fatal(err)
81-
// }
82-
// if _, err := sdb.Exec(string(blob)); err != nil {
83-
// t.Fatalf("%s: %s", filepath.Base(f), err)
84-
// }
85-
// }
86-
87-
// return sdb, func() {
88-
// if _, err := os.Stat(path); err == nil {
89-
// os.Remove(path)
90-
// }
91-
// }
92-
// }

backend/internal/db/conn_test.go

+1
Original file line numberDiff line numberDiff line change
@@ -61,3 +61,4 @@ func TestConn(t *testing.T) {
6161
}
6262

6363
}
64+

backend/internal/server/run.go

+4-4
Original file line numberDiff line numberDiff line change
@@ -51,14 +51,14 @@ func Run(
5151
// Init JWT auth
5252
tokenAuth := jwtauth.New("HS256", config.SecretBytes, nil)
5353
// Init db
54-
dbReadPool, err := db.NewReadPool(config.SqlitePath, ctx)
54+
dbWritePool, err := db.NewWritePool(config.SqlitePath, ctx)
5555
if err != nil {
56-
logger.Printf("Failed to init database read pool: %v", err.Error())
56+
logger.Printf("Failed to init database write pool: %v", err.Error())
5757
return err
5858
}
59-
dbWritePool, err := db.NewWritePool(config.SqlitePath, ctx)
59+
dbReadPool, err := db.NewReadPool(config.SqlitePath, ctx)
6060
if err != nil {
61-
logger.Printf("Failed to init database write pool: %v", err.Error())
61+
logger.Printf("Failed to init database read pool: %v", err.Error())
6262
return err
6363
}
6464
err = db.ApplyMigrations(dbWritePool, ctx)

0 commit comments

Comments
 (0)