@@ -33,7 +33,10 @@ import (
33
33
"go.uber.org/zap"
34
34
)
35
35
36
- const dbErrorDatabaseDoesNotExist = pgerrcode .InvalidCatalogName
36
+ const (
37
+ dbErrorDatabaseInvalidPassword = pgerrcode .InvalidPassword
38
+ dbErrorDatabaseDoesNotExist = pgerrcode .InvalidCatalogName
39
+ )
37
40
38
41
var ErrDatabaseDriverMismatch = errors .New ("database driver mismatch" )
39
42
@@ -50,7 +53,7 @@ func DbConnect(ctx context.Context, logger *zap.Logger, config Config, create bo
50
53
}
51
54
query := parsedURL .Query ()
52
55
var queryUpdated bool
53
- if len ( query .Get ("sslmode" )) == 0 {
56
+ if query .Get ("sslmode" ) == "" {
54
57
query .Set ("sslmode" , "prefer" )
55
58
queryUpdated = true
56
59
}
@@ -77,16 +80,14 @@ func DbConnect(ctx context.Context, logger *zap.Logger, config Config, create bo
77
80
logger .Fatal ("Failed to open database" , zap .Error (err ))
78
81
}
79
82
83
+ dbPing (ctx , logger , db , dbName )
84
+
80
85
if create {
81
86
var nakamaDBExists bool
82
87
if err = db .QueryRow ("SELECT EXISTS (SELECT 1 from pg_database WHERE datname = $1)" , dbName ).Scan (& nakamaDBExists ); err != nil {
83
- var pgErr * pgconn.PgError
84
- if errors .As (err , & pgErr ) && pgErr .Code == dbErrorDatabaseDoesNotExist {
85
- nakamaDBExists = false
86
- } else {
87
- db .Close ()
88
- logger .Fatal ("Failed to check if db exists" , zap .String ("db" , dbName ), zap .Error (err ))
89
- }
88
+ nakamaDBExists = false
89
+ db .Close ()
90
+ logger .Fatal ("Failed to check if db exists" , zap .String ("db" , dbName ), zap .Error (err ))
90
91
}
91
92
92
93
if ! nakamaDBExists {
@@ -104,45 +105,32 @@ func DbConnect(ctx context.Context, logger *zap.Logger, config Config, create bo
104
105
logger .Fatal ("Failed to create database" , zap .Error (err ))
105
106
}
106
107
db .Close ()
108
+
107
109
parsedURL .Path = fmt .Sprintf ("/%s" , dbName )
108
- db , err = sql .Open ("pgx" , parsedURL .String ())
109
- if err != nil {
110
- db .Close ()
111
- logger .Fatal ("Failed to open database" , zap .Error (err ))
112
- }
113
110
}
114
111
}
115
112
116
113
logger .Debug ("Complete database connection URL" , zap .String ("raw_url" , parsedURL .String ()))
117
114
db , err = sql .Open ("pgx" , parsedURL .String ())
118
115
if err != nil {
119
- logger .Fatal ("Error connecting to database" , zap .Error (err ))
120
- }
121
- // Limit max time allowed across database ping and version fetch to 15 seconds total.
122
- pingCtx , pingCtxCancelFn := context .WithTimeout (ctx , 15 * time .Second )
123
- defer pingCtxCancelFn ()
124
- if err = db .PingContext (pingCtx ); err != nil {
125
- if strings .HasSuffix (err .Error (), "does not exist (SQLSTATE 3D000)" ) {
126
- logger .Fatal ("Database schema not found, run `nakama migrate up`" , zap .Error (err ))
127
- }
128
- logger .Fatal ("Error pinging database" , zap .Error (err ))
116
+ logger .Fatal ("Failed to open database" , zap .Error (err ))
129
117
}
130
118
119
+ dbPing (ctx , logger , db , dbName )
120
+
131
121
db .SetConnMaxLifetime (time .Millisecond * time .Duration (config .GetDatabase ().ConnMaxLifetimeMs ))
132
122
db .SetMaxOpenConns (config .GetDatabase ().MaxOpenConns )
133
123
db .SetMaxIdleConns (config .GetDatabase ().MaxIdleConns )
134
124
135
125
var dbVersion string
136
- if err = db .QueryRowContext (pingCtx , "SELECT version()" ).Scan (& dbVersion ); err != nil {
137
- logger .Fatal ("Error querying database version" , zap .Error (err ))
126
+ versionCtx , versionCtxCancelFn := context .WithTimeout (ctx , 5 * time .Second )
127
+ defer versionCtxCancelFn ()
128
+ if err = db .QueryRowContext (versionCtx , "SELECT version()" ).Scan (& dbVersion ); err != nil {
129
+ logger .Fatal ("Failed to query database version" , zap .Error (err ))
138
130
}
139
131
140
132
logger .Info ("Database information" , zap .String ("version" , dbVersion ))
141
- if strings .Split (dbVersion , " " )[0 ] == "CockroachDB" {
142
- isCockroach = true
143
- } else {
144
- isCockroach = false
145
- }
133
+ isCockroach = strings .HasPrefix (dbVersion , "CockroachDB " )
146
134
147
135
// Periodically check database hostname for underlying address changes.
148
136
go func () {
@@ -237,6 +225,32 @@ func DbConnect(ctx context.Context, logger *zap.Logger, config Config, create bo
237
225
return db
238
226
}
239
227
228
+ func dbPing (ctx context.Context , logger * zap.Logger , db * sql.DB , dbName string ) error {
229
+ pingCtx , cancel := context .WithTimeout (ctx , 10 * time .Second )
230
+ defer cancel ()
231
+
232
+ err := db .PingContext (pingCtx )
233
+ if err != nil {
234
+ db .Close ()
235
+
236
+ errLogger := logger .With (zap .String ("db" , dbName ), zap .Error (err ))
237
+
238
+ var pgErr * pgconn.PgError
239
+ if errors .As (err , & pgErr ) {
240
+ switch pgErr .Code {
241
+ case dbErrorDatabaseInvalidPassword :
242
+ errLogger .Fatal ("Invalid credentials" )
243
+ case dbErrorDatabaseDoesNotExist :
244
+ errLogger .Fatal ("Database schema not found, run `nakama migrate up`" )
245
+ }
246
+ } else {
247
+ errLogger .Fatal ("Failed to ping database" )
248
+ }
249
+ }
250
+
251
+ return nil
252
+ }
253
+
240
254
func dbResolveAddress (ctx context.Context , logger * zap.Logger , host string ) ([]string , map [string ]struct {}) {
241
255
resolveCtx , resolveCtxCancelFn := context .WithTimeout (ctx , 15 * time .Second )
242
256
defer resolveCtxCancelFn ()
0 commit comments