Skip to content

Commit d429f29

Browse files
committed
Improve nakama migrate errors
Helps with #1096
1 parent dfddb0a commit d429f29

File tree

1 file changed

+45
-31
lines changed

1 file changed

+45
-31
lines changed

server/db.go

Lines changed: 45 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,10 @@ import (
3333
"go.uber.org/zap"
3434
)
3535

36-
const dbErrorDatabaseDoesNotExist = pgerrcode.InvalidCatalogName
36+
const (
37+
dbErrorDatabaseInvalidPassword = pgerrcode.InvalidPassword
38+
dbErrorDatabaseDoesNotExist = pgerrcode.InvalidCatalogName
39+
)
3740

3841
var ErrDatabaseDriverMismatch = errors.New("database driver mismatch")
3942

@@ -50,7 +53,7 @@ func DbConnect(ctx context.Context, logger *zap.Logger, config Config, create bo
5053
}
5154
query := parsedURL.Query()
5255
var queryUpdated bool
53-
if len(query.Get("sslmode")) == 0 {
56+
if query.Get("sslmode") == "" {
5457
query.Set("sslmode", "prefer")
5558
queryUpdated = true
5659
}
@@ -77,16 +80,14 @@ func DbConnect(ctx context.Context, logger *zap.Logger, config Config, create bo
7780
logger.Fatal("Failed to open database", zap.Error(err))
7881
}
7982

83+
dbPing(ctx, logger, db, dbName)
84+
8085
if create {
8186
var nakamaDBExists bool
8287
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))
9091
}
9192

9293
if !nakamaDBExists {
@@ -104,45 +105,32 @@ func DbConnect(ctx context.Context, logger *zap.Logger, config Config, create bo
104105
logger.Fatal("Failed to create database", zap.Error(err))
105106
}
106107
db.Close()
108+
107109
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-
}
113110
}
114111
}
115112

116113
logger.Debug("Complete database connection URL", zap.String("raw_url", parsedURL.String()))
117114
db, err = sql.Open("pgx", parsedURL.String())
118115
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))
129117
}
130118

119+
dbPing(ctx, logger, db, dbName)
120+
131121
db.SetConnMaxLifetime(time.Millisecond * time.Duration(config.GetDatabase().ConnMaxLifetimeMs))
132122
db.SetMaxOpenConns(config.GetDatabase().MaxOpenConns)
133123
db.SetMaxIdleConns(config.GetDatabase().MaxIdleConns)
134124

135125
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))
138130
}
139131

140132
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 ")
146134

147135
// Periodically check database hostname for underlying address changes.
148136
go func() {
@@ -237,6 +225,32 @@ func DbConnect(ctx context.Context, logger *zap.Logger, config Config, create bo
237225
return db
238226
}
239227

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+
240254
func dbResolveAddress(ctx context.Context, logger *zap.Logger, host string) ([]string, map[string]struct{}) {
241255
resolveCtx, resolveCtxCancelFn := context.WithTimeout(ctx, 15*time.Second)
242256
defer resolveCtxCancelFn()

0 commit comments

Comments
 (0)