Skip to content

Commit 1132011

Browse files
Optimize SQLite writes and add PostgreSQL migration script
- Enabled WAL mode and set synchronous to NORMAL for SQLite to reduce CPU usage during writes. - Added support for PostgreSQL via the DATABASE_URL environment variable. - Created a migration script in cmd/migrate to facilitate moving data from SQLite to PostgreSQL. - Updated documentation and environment examples for the new features. Co-authored-by: birabittoh <26506860+birabittoh@users.noreply.github.com>
1 parent 65fce5e commit 1132011

File tree

6 files changed

+129
-15
lines changed

6 files changed

+129
-15
lines changed

.env.example

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
11
OWM_API_KEY=
22
OWM_LATITUDE=
33
OWM_LONGITUDE=
4+
5+
# Optional: PostgreSQL connection string
6+
# DATABASE_URL=postgres://user:password@localhost:5432/dbname

README.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,17 @@ docker compose up -d
6161
-------------|----------------
6262
`OWM_CRON` |`0 0/30 * * * *`
6363
`APP_ADDRESS`|`:3000`
64+
`DATABASE_URL`| (If set, PostgreSQL is used instead of SQLite)
65+
66+
## Migration to PostgreSQL
67+
If you want to migrate your existing SQLite data to PostgreSQL:
68+
69+
1. Set `DATABASE_URL` in your `.env` file.
70+
2. (Optional) Set `SQLITE_PATH` if your database is not in `data/data.sqlite`.
71+
3. Run the migration script:
72+
```sh
73+
go run ./cmd/migrate
74+
```
6475

6576
## License
6677
Rainbbit is licensed under MIT.

cmd/migrate/main.go

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
package main
2+
3+
import (
4+
"log"
5+
"os"
6+
7+
"github.com/birabittoh/rainbbit/src"
8+
"github.com/glebarez/sqlite"
9+
"github.com/joho/godotenv"
10+
"gorm.io/driver/postgres"
11+
"gorm.io/gorm"
12+
)
13+
14+
func main() {
15+
err := godotenv.Load()
16+
if err != nil {
17+
log.Println("No .env file found")
18+
}
19+
20+
sqlitePath := os.Getenv("SQLITE_PATH")
21+
if sqlitePath == "" {
22+
sqlitePath = "data/data.sqlite"
23+
}
24+
25+
pgDSN := os.Getenv("DATABASE_URL")
26+
if pgDSN == "" {
27+
log.Fatal("DATABASE_URL environment variable is required")
28+
}
29+
30+
log.Printf("Connecting to SQLite: %s", sqlitePath)
31+
sqliteDB, err := gorm.Open(sqlite.Open(sqlitePath), &gorm.Config{})
32+
if err != nil {
33+
log.Fatalf("Failed to connect to SQLite: %v", err)
34+
}
35+
36+
log.Println("Connecting to PostgreSQL...")
37+
pgDB, err := gorm.Open(postgres.Open(pgDSN), &gorm.Config{})
38+
if err != nil {
39+
log.Fatalf("Failed to connect to PostgreSQL: %v", err)
40+
}
41+
42+
log.Println("Migrating schema to PostgreSQL...")
43+
err = pgDB.AutoMigrate(&src.Record{})
44+
if err != nil {
45+
log.Fatalf("Failed to migrate PostgreSQL schema: %v", err)
46+
}
47+
48+
var records []src.Record
49+
log.Println("Fetching records from SQLite...")
50+
err = sqliteDB.Find(&records).Error
51+
if err != nil {
52+
log.Fatalf("Failed to fetch records from SQLite: %v", err)
53+
}
54+
55+
log.Printf("Found %d records. Starting migration...", len(records))
56+
57+
if len(records) > 0 {
58+
// Batch insert to PostgreSQL
59+
err = pgDB.CreateInBatches(records, 100).Error
60+
if err != nil {
61+
log.Fatalf("Failed to insert records into PostgreSQL: %v", err)
62+
}
63+
}
64+
65+
log.Println("Migration completed successfully!")
66+
}

go.mod

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,16 +25,23 @@ require (
2525
github.com/glebarez/go-sqlite v1.22.0 // indirect
2626
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 // indirect
2727
github.com/google/uuid v1.6.0 // indirect
28+
github.com/jackc/pgpassfile v1.0.0 // indirect
29+
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 // indirect
30+
github.com/jackc/pgx/v5 v5.6.0 // indirect
31+
github.com/jackc/puddle/v2 v2.2.2 // indirect
2832
github.com/jinzhu/inflection v1.0.0 // indirect
2933
github.com/jinzhu/now v1.1.5 // indirect
3034
github.com/mattn/go-isatty v0.0.20 // indirect
3135
github.com/ncruces/go-strftime v0.1.9 // indirect
3236
github.com/pmezard/go-difflib v1.0.0 // indirect
3337
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
38+
golang.org/x/crypto v0.31.0 // indirect
3439
golang.org/x/exp v0.0.0-20250506013437-ce4c2cf36ca6 // indirect
3540
golang.org/x/image v0.27.0 // indirect
41+
golang.org/x/sync v0.14.0 // indirect
3642
golang.org/x/sys v0.33.0 // indirect
3743
golang.org/x/text v0.25.0 // indirect
44+
gorm.io/driver/postgres v1.6.0 // indirect
3845
modernc.org/libc v1.65.6 // indirect
3946
modernc.org/mathutil v1.7.1 // indirect
4047
modernc.org/memory v1.10.0 // indirect

go.sum

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ github.com/briandowns/openweathermap v0.21.1 h1:TPbuixuF+aGJP1mpgTNny6eUkdbvj7gq
2323
github.com/briandowns/openweathermap v0.21.1/go.mod h1:0GLnknqicWxXnGi1IqoOaZIw+kIe5hkt+YM5WY3j8+0=
2424
github.com/campoy/embedmd v1.0.0 h1:V4kI2qTJJLf4J29RzI/MAt2c3Bl4dQSYPuflzwFH2hY=
2525
github.com/campoy/embedmd v1.0.0/go.mod h1:oxyr9RCiSXg0M3VJ3ks0UGfp98BpSSGr0kpiX3MzVl8=
26+
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
2627
github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
2728
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
2829
github.com/glebarez/go-sqlite v1.22.0 h1:uAcMJhaA6r3LHMTFgP0SifzgXg46yJkgxqyuyec+ruQ=
@@ -37,6 +38,14 @@ github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
3738
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
3839
github.com/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k=
3940
github.com/hashicorp/golang-lru/v2 v2.0.7/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM=
41+
github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM=
42+
github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg=
43+
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 h1:iCEnooe7UlwOQYpKFhBabPMi4aNAfoODPEFNiAnClxo=
44+
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM=
45+
github.com/jackc/pgx/v5 v5.6.0 h1:SWJzexBzPL5jb0GEsrPMLIsi/3jOo7RHlzTjcAeDrPY=
46+
github.com/jackc/pgx/v5 v5.6.0/go.mod h1:DNZ/vlrUnhWCoFGxHAG8U2ljioxukquj7utPDgtQdTw=
47+
github.com/jackc/puddle/v2 v2.2.2 h1:PR8nw+E/1w0GLuRFSmiioY6UooMp6KJv0/61nB7icHo=
48+
github.com/jackc/puddle/v2 v2.2.2/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4=
4049
github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E=
4150
github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
4251
github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ=
@@ -54,10 +63,15 @@ github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94
5463
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
5564
github.com/robfig/cron/v3 v3.0.1 h1:WdRxkvbJztn8LMz/QEvLN5sBU+xKpSqwwUO1Pjr4qDs=
5665
github.com/robfig/cron/v3 v3.0.1/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro=
66+
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
67+
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
68+
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
5769
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
5870
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
5971
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
6072
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
73+
golang.org/x/crypto v0.31.0 h1:ihbySMvVjLAeSH1IbfcRTkD/iNscyz8rGzjF/E5hV6U=
74+
golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk=
6175
golang.org/x/exp v0.0.0-20250506013437-ce4c2cf36ca6 h1:y5zboxd6LQAqYIhHnB48p0ByQ/GnQx2BE33L8BOHQkI=
6276
golang.org/x/exp v0.0.0-20250506013437-ce4c2cf36ca6/go.mod h1:U6Lno4MTRCDY+Ba7aCcauB9T60gsv5s4ralQzP72ZoQ=
6377
golang.org/x/image v0.27.0 h1:C8gA4oWU/tKkdCfYT6T2u4faJu3MeNS5O8UPWlPF61w=
@@ -97,6 +111,10 @@ gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk=
97111
gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E=
98112
gonum.org/v1/plot v0.16.0 h1:dK28Qx/Ky4VmPUN/2zeW0ELyM6ucDnBAj5yun7M9n1g=
99113
gonum.org/v1/plot v0.16.0/go.mod h1:Xz6U1yDMi6Ni6aaXILqmVIb6Vro8E+K7Q/GeeH+Pn0c=
114+
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
115+
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
116+
gorm.io/driver/postgres v1.6.0 h1:2dxzU8xJ+ivvqTRph34QX+WrRaJlmfyPqXmoGVjMBa4=
117+
gorm.io/driver/postgres v1.6.0/go.mod h1:vUw0mrGgrTK+uPHEhAdV4sfFELrByKVGnaVRkXDhtWo=
100118
gorm.io/gorm v1.26.1 h1:ghB2gUI9FkS46luZtn6DLZ0f6ooBJ5IbVej2ENFDjRw=
101119
gorm.io/gorm v1.26.1/go.mod h1:8Z33v652h4//uMA76KjeDH8mJXPm1QNCYrMeatR0DOE=
102120
honnef.co/go/tools v0.1.3/go.mod h1:NgwopIslSNH47DimFoV78dnkksY2EFtX0ajyb3K/las=

src/db.go

Lines changed: 24 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -10,14 +10,15 @@ import (
1010

1111
"github.com/glebarez/sqlite"
1212
"github.com/hashicorp/golang-lru/v2/expirable"
13+
"gorm.io/driver/postgres"
1314
"gorm.io/gorm"
1415
"gorm.io/gorm/schema"
1516
)
1617

1718
const (
1819
dataDir = "data"
1920
dbPath = dataDir + string(os.PathSeparator) + "data.sqlite"
20-
dbOptions = "?_pragma=foreign_keys(1)"
21+
dbOptions = "?_pragma=foreign_keys(1)&_pragma=journal_mode(WAL)&_pragma=synchronous(NORMAL)"
2122
zonePath = dataDir + string(os.PathSeparator) + "zone.txt"
2223
)
2324

@@ -173,28 +174,36 @@ func getDataPoints(requestedMeasures []string, f, t *int64) (dp []DataPoint, err
173174
}
174175

175176
func initDB() (err error) {
176-
// Assicuriamoci che la directory "data" esista
177-
if err := os.MkdirAll(dataDir, os.ModePerm); err != nil {
178-
return errors.New("Errore nella creazione della directory 'data': " + err.Error())
179-
}
177+
dsn := os.Getenv("DATABASE_URL")
178+
if dsn != "" {
179+
db, err = gorm.Open(postgres.Open(dsn), &gorm.Config{})
180+
if err != nil {
181+
return errors.New("Errore nell'apertura del database PostgreSQL: " + err.Error())
182+
}
183+
} else {
184+
// Assicuriamoci che la directory "data" esista
185+
if err := os.MkdirAll(dataDir, os.ModePerm); err != nil {
186+
return errors.New("Errore nella creazione della directory 'data': " + err.Error())
187+
}
180188

181-
// Inizializzazione del database SQLite con GORM
182-
db, err = gorm.Open(sqlite.Open(dbPath+dbOptions), &gorm.Config{})
183-
if err != nil {
184-
return errors.New("Errore nell'apertura del database: " + err.Error())
189+
// Inizializzazione del database SQLite con GORM
190+
db, err = gorm.Open(sqlite.Open(dbPath+dbOptions), &gorm.Config{})
191+
if err != nil {
192+
return errors.New("Errore nell'apertura del database SQLite: " + err.Error())
193+
}
194+
195+
// Limitazione delle connessioni per SQLite
196+
sqlDB, err := db.DB()
197+
if err == nil {
198+
sqlDB.SetMaxOpenConns(1)
199+
}
185200
}
186201

187202
// Migrazione dello schema per il modello Record
188203
if err := db.AutoMigrate(&Record{}); err != nil {
189204
return errors.New("Errore nella migrazione del database: " + err.Error())
190205
}
191206

192-
// Limitazione delle connessioni per SQLite
193-
sqlDB, err := db.DB()
194-
if err == nil {
195-
sqlDB.SetMaxOpenConns(1)
196-
}
197-
198207
// Inizializzazione delle colonne
199208
s, err := schema.Parse(&Record{}, &sync.Map{}, schema.NamingStrategy{})
200209
if err != nil {

0 commit comments

Comments
 (0)