Skip to content

Commit 8a0fbae

Browse files
authored
DB perf pass: shared pools, bounded retries, query optimizations (#116)
1 parent d6c2561 commit 8a0fbae

File tree

19 files changed

+406
-154
lines changed

19 files changed

+406
-154
lines changed

auth-service/main.go

Lines changed: 46 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ import (
44
"database/sql"
55
"fmt"
66
"log"
7+
"os"
8+
"strconv"
79
"time"
810

911
"auth-service/config"
@@ -64,20 +66,61 @@ func setupDatabase(cfg *config.Config) (*sql.DB, error) {
6466
return nil, err
6567
}
6668

67-
// Test connection with exponential backoff retry
69+
applyDBPoolSettings(db)
70+
71+
// Test connection with exponential backoff retry (bounded)
72+
maxWaitSec := envInt([]string{"AUTH_SERVICE_DB_PING_MAX_WAIT_SEC", "DB_PING_MAX_WAIT_SEC"}, 60)
73+
deadline := time.Now().Add(time.Duration(maxWaitSec) * time.Second)
6874
var waitInterval time.Duration = 1 * time.Second
6975
for {
70-
if err := db.Ping(); err == nil {
76+
pingErr := db.Ping()
77+
if pingErr == nil {
7178
break // Connection successful
7279
}
73-
log.Printf("Database connection failed, retrying in %v: %v", waitInterval, err)
80+
if time.Now().After(deadline) {
81+
return nil, fmt.Errorf("database ping timeout after %ds: %w", maxWaitSec, pingErr)
82+
}
83+
log.Printf("Database connection failed, retrying in %v: %v", waitInterval, pingErr)
7484
time.Sleep(waitInterval)
7585
waitInterval *= 2 // Exponential backoff: 1s, 2s, 4s, 8s, ...
86+
if waitInterval > 30*time.Second {
87+
waitInterval = 30 * time.Second
88+
}
7689
}
7790

7891
return db, nil
7992
}
8093

94+
func applyDBPoolSettings(db *sql.DB) {
95+
maxOpen := envInt([]string{"AUTH_SERVICE_DB_MAX_OPEN_CONNS", "DB_MAX_OPEN_CONNS"}, 20)
96+
maxIdle := envInt([]string{"AUTH_SERVICE_DB_MAX_IDLE_CONNS", "DB_MAX_IDLE_CONNS"}, 10)
97+
maxLifetimeMin := envInt([]string{"AUTH_SERVICE_DB_CONN_MAX_LIFETIME_MIN", "DB_CONN_MAX_LIFETIME_MIN"}, 5)
98+
99+
if maxOpen > 0 {
100+
db.SetMaxOpenConns(maxOpen)
101+
}
102+
if maxIdle > 0 {
103+
db.SetMaxIdleConns(maxIdle)
104+
}
105+
if maxLifetimeMin > 0 {
106+
db.SetConnMaxLifetime(time.Duration(maxLifetimeMin) * time.Minute)
107+
}
108+
}
109+
110+
func envInt(keys []string, def int) int {
111+
for _, key := range keys {
112+
v := os.Getenv(key)
113+
if v == "" {
114+
continue
115+
}
116+
n, err := strconv.Atoi(v)
117+
if err == nil && n > 0 {
118+
return n
119+
}
120+
}
121+
return def
122+
}
123+
81124
func setupRouter(service *database.AuthService, cfg *config.Config) *gin.Engine {
82125
router := gin.Default()
83126

backend/db/db.go

Lines changed: 17 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -23,39 +23,30 @@ import (
2323

2424
func CreateOrUpdateUser(db *sql.DB, u *api.UserArgs, teamGen func(string) util.TeamColor, disbursers []*disburse.Disburser) (*api.UserResp, error) {
2525
{
26-
avRows, err := db.Query("SELECT id FROM users WHERE avatar = ?", u.Avatar)
27-
if err != nil {
26+
var existingID string
27+
err := db.QueryRow("SELECT id FROM users WHERE avatar = ? LIMIT 1", u.Avatar).Scan(&existingID)
28+
if err != nil && err != sql.ErrNoRows {
2829
log.Errorf("Error getting user with avatar %s, %w", u.Avatar, err)
2930
return nil, err
3031
}
31-
defer avRows.Close()
32-
33-
if avRows.Next() {
34-
// Check for duplication.
35-
var id string
36-
if err := avRows.Scan(&id); err != nil {
37-
return nil, err
38-
}
39-
if id != u.Id {
40-
return &api.UserResp{
41-
DupAvatar: true,
42-
}, fmt.Errorf("duplicated avatar %s for the user %s: avatar already exists for the user %s",
43-
u.Avatar,
44-
u.Id,
45-
id)
46-
}
32+
if err == nil && existingID != u.Id {
33+
return &api.UserResp{
34+
DupAvatar: true,
35+
}, fmt.Errorf("duplicated avatar %s for the user %s: avatar already exists for the user %s",
36+
u.Avatar,
37+
u.Id,
38+
existingID)
4739
}
4840
}
4941
var initialKitn = 0
5042
{
51-
idRows, err := db.Query("SELECT id FROM users WHERE id = ?", u.Id)
52-
if err != nil {
43+
var existingID string
44+
err := db.QueryRow("SELECT id FROM users WHERE id = ? LIMIT 1", u.Id).Scan(&existingID)
45+
if err != nil && err != sql.ErrNoRows {
5346
log.Errorf("Error getting user with id %s, %w", u.Id, err)
5447
return nil, err
5548
}
56-
defer idRows.Close()
57-
58-
if !idRows.Next() {
49+
if err == sql.ErrNoRows {
5950
initialKitn = 1
6051
// No existing user yet, it's a user creation. Sending 1 KITN to the user.
6152
for _, disburser := range disbursers {
@@ -147,11 +138,12 @@ func SaveReport(db *sql.DB, r *api.ReportArgs) (*api.Report, error) {
147138
return nil, err
148139
}
149140

150-
var seq int
151-
if err := tx.QueryRowContext(ctx, `SELECT MAX(seq) FROM reports`).Scan(&seq); err != nil {
141+
lastInsertID, err := result.LastInsertId()
142+
if err != nil {
152143
log.Errorf("Error getting last insert id: %w", err)
153144
return nil, err
154145
}
146+
seq := int(lastInsertID)
155147
r.Image = compressedImage
156148

157149
result, err = tx.ExecContext(ctx, `UPDATE users SET kitns_daily = kitns_daily + 1 WHERE id = ?`, r.Id)

backend/server/action.go

Lines changed: 6 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ import (
55

66
"cleanapp/backend/db"
77
"cleanapp/backend/server/api"
8-
"cleanapp/common"
98

109
"github.com/apex/log"
1110
"github.com/gin-gonic/gin"
@@ -27,12 +26,11 @@ func CreateAction(c *gin.Context) {
2726

2827
args.Record.Id = randRefGen()
2928

30-
dbc, err := common.DBConnect()
29+
dbc, err := getServerDB()
3130
if err != nil {
3231
log.Errorf("DB connection error: %w", err)
3332
return
3433
}
35-
defer dbc.Close()
3634

3735
response, err := db.CreateAction(dbc, args)
3836
if err != nil {
@@ -58,12 +56,11 @@ func UpdateAction(c *gin.Context) {
5856
return
5957
}
6058

61-
dbc, err := common.DBConnect()
59+
dbc, err := getServerDB()
6260
if err != nil {
6361
log.Errorf("DB connection error: %w", err)
6462
return
6563
}
66-
defer dbc.Close()
6764

6865
response, err := db.UpdateAction(dbc, args)
6966
if err != nil {
@@ -89,12 +86,11 @@ func DeleteAction(c *gin.Context) {
8986
return
9087
}
9188

92-
dbc, err := common.DBConnect()
89+
dbc, err := getServerDB()
9390
if err != nil {
9491
log.Errorf("DB connection error: %w", err)
9592
return
9693
}
97-
defer dbc.Close()
9894

9995
err = db.DeleteAction(dbc, args)
10096
if err != nil {
@@ -115,12 +111,11 @@ func GetAction(c *gin.Context) {
115111
return
116112
}
117113

118-
dbc, err := common.DBConnect()
114+
dbc, err := getServerDB()
119115
if err != nil {
120116
log.Errorf("DB connection error: %w", err)
121117
return
122118
}
123-
defer dbc.Close()
124119

125120
response, err := db.GetActions(dbc, id)
126121
if err != nil {
@@ -133,12 +128,11 @@ func GetAction(c *gin.Context) {
133128
}
134129

135130
func GetActions(c *gin.Context) {
136-
dbc, err := common.DBConnect()
131+
dbc, err := getServerDB()
137132
if err != nil {
138133
log.Errorf("DB connection error: %w", err)
139134
return
140135
}
141-
defer dbc.Close()
142136

143137
response, err := db.GetActions(dbc, "")
144138
if err != nil {
@@ -164,12 +158,11 @@ func UpdateUserAction(c *gin.Context) {
164158
return
165159
}
166160

167-
dbc, err := common.DBConnect()
161+
dbc, err := getServerDB()
168162
if err != nil {
169163
log.Errorf("DB connection error: %w", err)
170164
return
171165
}
172-
defer dbc.Close()
173166

174167
if err = db.UpdateUserAction(dbc, args); err != nil {
175168
log.Errorf("Failed to update user actions with %w", err)

backend/server/dbpool.go

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
package server
2+
3+
import (
4+
"database/sql"
5+
"sync"
6+
7+
"cleanapp/common"
8+
)
9+
10+
var (
11+
serverDBOnce sync.Once
12+
serverDB *sql.DB
13+
serverDBErr error
14+
)
15+
16+
func getServerDB() (*sql.DB, error) {
17+
serverDBOnce.Do(func() {
18+
serverDB, serverDBErr = common.DBConnect()
19+
})
20+
return serverDB, serverDBErr
21+
}
22+
23+
func closeServerDB() {
24+
if serverDB != nil {
25+
_ = serverDB.Close()
26+
}
27+
}

backend/server/privacy.go

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
package server
22

33
import (
4-
"cleanapp/common"
54
"net/http"
65

76
"cleanapp/backend/db"
@@ -25,12 +24,11 @@ func UpdatePrivacyAndTOC(c *gin.Context) {
2524
return
2625
}
2726

28-
dbc, err := common.DBConnect()
27+
dbc, err := getServerDB()
2928
if err != nil {
3029
log.Errorf("DB connection error: %w", err)
3130
return
3231
}
33-
defer dbc.Close()
3432

3533
err = db.UpdatePrivacyAndTOC(dbc, &args)
3634
if err != nil {

backend/server/read_report.go

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
package server
22

33
import (
4-
"cleanapp/common"
54
"net/http"
65

76
"cleanapp/backend/db"
@@ -25,12 +24,11 @@ func ReadReport(c *gin.Context) {
2524
return
2625
}
2726

28-
dbc, err := common.DBConnect()
27+
dbc, err := getServerDB()
2928
if err != nil {
3029
log.Errorf("DB connection error: %w", err)
3130
return
3231
}
33-
defer dbc.Close()
3432

3533
result, err := db.ReadReport(dbc, args)
3634
if err != nil {

backend/server/referral.go

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ import (
55

66
"cleanapp/backend/db"
77
"cleanapp/backend/server/api"
8-
"cleanapp/common"
98

109
"github.com/apex/log"
1110
"github.com/gin-gonic/gin"
@@ -18,12 +17,11 @@ func ReadReferral(c *gin.Context) {
1817
return
1918
}
2019

21-
dbc, err := common.DBConnect()
20+
dbc, err := getServerDB()
2221
if err != nil {
2322
log.Errorf("%v", err)
2423
return
2524
}
26-
defer dbc.Close()
2725

2826
refValue, err := db.ReadReferral(dbc, refQuery.RefKey)
2927
if err != nil {
@@ -47,12 +45,11 @@ func WriteReferral(c *gin.Context) {
4745
return
4846
}
4947

50-
dbc, err := common.DBConnect()
48+
dbc, err := getServerDB()
5149
if err != nil {
5250
log.Errorf("%v", err)
5351
return
5452
}
55-
defer dbc.Close()
5653

5754
if err := db.WriteReferral(dbc, refData.RefKey, refData.RefValue); err != nil {
5855
c.Error(err)
@@ -79,12 +76,11 @@ func GenerateReferral(c *gin.Context) {
7976
return
8077
}
8178

82-
dbc, err := common.DBConnect()
79+
dbc, err := getServerDB()
8380
if err != nil {
8481
log.Errorf("%v", err)
8582
return
8683
}
87-
defer dbc.Close()
8884

8985
ref, err := db.GenerateReferral(dbc, req, randRefGen)
9086
if err != nil {

backend/server/report.go

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
package server
22

33
import (
4-
"cleanapp/common"
54
"cleanapp/common/disburse"
65
"net/http"
76

@@ -29,12 +28,11 @@ func Report(c *gin.Context) {
2928
return
3029
}
3130

32-
dbc, err := common.DBConnect()
31+
dbc, err := getServerDB()
3332
if err != nil {
3433
log.Errorf("Error connecting to DB: %w", err)
3534
return
3635
}
37-
defer dbc.Close()
3836

3937
// Add report to the database.
4038
savedReport, err := db.SaveReport(dbc, report)

0 commit comments

Comments
 (0)