Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commite73f9d3

Browse files
authored
fix: retry embedded postgres port allocation (#20371)
Sometimes tests would fail because the port embedded postgres tries touse is already in use. This is because there's no way to tell postgresto use an ephemeral port in tests. This change adds retries to startingembedded postgres when the port is not explicitly defined (e.g. tests) whichshould rid of, or at least significantly reduce, these flakes.coder/internal#658
1 parent87ce021 commite73f9d3

File tree

1 file changed

+75
-39
lines changed

1 file changed

+75
-39
lines changed

‎cli/server.go‎

Lines changed: 75 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ import (
2929
"strings"
3030
"sync"
3131
"sync/atomic"
32+
"testing"
3233
"time"
3334

3435
"github.com/charmbracelet/lipgloss"
@@ -1377,6 +1378,7 @@ func IsLocalURL(ctx context.Context, u *url.URL) (bool, error) {
13771378
}
13781379

13791380
funcshutdownWithTimeout(shutdownfunc(context.Context)error,timeout time.Duration)error {
1381+
// nolint:gocritic // The magic number is parameterized.
13801382
ctx,cancel:=context.WithTimeout(context.Background(),timeout)
13811383
defercancel()
13821384
returnshutdown(ctx)
@@ -2134,50 +2136,83 @@ func startBuiltinPostgres(ctx context.Context, cfg config.Root, logger slog.Logg
21342136
return"",nil,xerrors.New("The built-in PostgreSQL cannot run as the root user. Create a non-root user and run again!")
21352137
}
21362138

2137-
// Ensure a password and port have been generated!
2138-
connectionURL,err:=embeddedPostgresURL(cfg)
2139-
iferr!=nil {
2140-
return"",nil,err
2141-
}
2142-
pgPassword,err:=cfg.PostgresPassword().Read()
2143-
iferr!=nil {
2144-
return"",nil,xerrors.Errorf("read postgres password: %w",err)
2145-
}
2146-
pgPortRaw,err:=cfg.PostgresPort().Read()
2147-
iferr!=nil {
2148-
return"",nil,xerrors.Errorf("read postgres port: %w",err)
2149-
}
2150-
pgPort,err:=strconv.ParseUint(pgPortRaw,10,16)
2151-
iferr!=nil {
2152-
return"",nil,xerrors.Errorf("parse postgres port: %w",err)
2153-
}
2154-
21552139
cachePath:=filepath.Join(cfg.PostgresPath(),"cache")
21562140
ifcustomCacheDir!="" {
21572141
cachePath=filepath.Join(customCacheDir,"postgres")
21582142
}
21592143
stdlibLogger:=slog.Stdlib(ctx,logger.Named("postgres"),slog.LevelDebug)
2160-
ep:=embeddedpostgres.NewDatabase(
2161-
embeddedpostgres.DefaultConfig().
2162-
Version(embeddedpostgres.V13).
2163-
BinariesPath(filepath.Join(cfg.PostgresPath(),"bin")).
2164-
// Default BinaryRepositoryURL repo1.maven.org is flaky.
2165-
BinaryRepositoryURL("https://repo.maven.apache.org/maven2").
2166-
DataPath(filepath.Join(cfg.PostgresPath(),"data")).
2167-
RuntimePath(filepath.Join(cfg.PostgresPath(),"runtime")).
2168-
CachePath(cachePath).
2169-
Username("coder").
2170-
Password(pgPassword).
2171-
Database("coder").
2172-
Encoding("UTF8").
2173-
Port(uint32(pgPort)).
2174-
Logger(stdlibLogger.Writer()),
2175-
)
2176-
err=ep.Start()
2177-
iferr!=nil {
2178-
return"",nil,xerrors.Errorf("Failed to start built-in PostgreSQL. Optionally, specify an external deployment with `--postgres-url`: %w",err)
2144+
2145+
// If the port is not defined, an available port will be found dynamically.
2146+
maxAttempts:=1
2147+
_,err=cfg.PostgresPort().Read()
2148+
retryPortDiscovery:=errors.Is(err,os.ErrNotExist)&&testing.Testing()
2149+
ifretryPortDiscovery {
2150+
// There is no way to tell Postgres to use an ephemeral port, so in order to avoid
2151+
// flaky tests in CI we need to retry EmbeddedPostgres.Start in case of a race
2152+
// condition where the port we quickly listen on and close in embeddedPostgresURL()
2153+
// is not free by the time the embedded postgres starts up. This maximum_should
2154+
// cover most cases where port conflicts occur in CI and cause flaky tests.
2155+
maxAttempts=3
2156+
}
2157+
2158+
varstartErrerror
2159+
forattempt:=0;attempt<maxAttempts;attempt++ {
2160+
// Ensure a password and port have been generated.
2161+
connectionURL,err:=embeddedPostgresURL(cfg)
2162+
iferr!=nil {
2163+
return"",nil,err
2164+
}
2165+
pgPassword,err:=cfg.PostgresPassword().Read()
2166+
iferr!=nil {
2167+
return"",nil,xerrors.Errorf("read postgres password: %w",err)
2168+
}
2169+
pgPortRaw,err:=cfg.PostgresPort().Read()
2170+
iferr!=nil {
2171+
return"",nil,xerrors.Errorf("read postgres port: %w",err)
2172+
}
2173+
pgPort,err:=strconv.ParseUint(pgPortRaw,10,16)
2174+
iferr!=nil {
2175+
return"",nil,xerrors.Errorf("parse postgres port: %w",err)
2176+
}
2177+
2178+
ep:=embeddedpostgres.NewDatabase(
2179+
embeddedpostgres.DefaultConfig().
2180+
Version(embeddedpostgres.V13).
2181+
BinariesPath(filepath.Join(cfg.PostgresPath(),"bin")).
2182+
// Default BinaryRepositoryURL repo1.maven.org is flaky.
2183+
BinaryRepositoryURL("https://repo.maven.apache.org/maven2").
2184+
DataPath(filepath.Join(cfg.PostgresPath(),"data")).
2185+
RuntimePath(filepath.Join(cfg.PostgresPath(),"runtime")).
2186+
CachePath(cachePath).
2187+
Username("coder").
2188+
Password(pgPassword).
2189+
Database("coder").
2190+
Encoding("UTF8").
2191+
Port(uint32(pgPort)).
2192+
Logger(stdlibLogger.Writer()),
2193+
)
2194+
2195+
startErr=ep.Start()
2196+
ifstartErr==nil {
2197+
returnconnectionURL,ep.Stop,nil
2198+
}
2199+
2200+
logger.Warn(ctx,"failed to start embedded postgres",
2201+
slog.F("attempt",attempt+1),
2202+
slog.F("max_attempts",maxAttempts),
2203+
slog.F("port",pgPort),
2204+
slog.Error(startErr),
2205+
)
2206+
2207+
ifretryPortDiscovery {
2208+
// Since a retry is needed, we wipe the port stored here at the beginning of the loop.
2209+
_=cfg.PostgresPort().Delete()
2210+
}
21792211
}
2180-
returnconnectionURL,ep.Stop,nil
2212+
2213+
return"",nil,xerrors.Errorf("failed to start built-in PostgreSQL after %d attempts. "+
2214+
"Optionally, specify an external deployment. See https://coder.com/docs/tutorials/external-database "+
2215+
"for more details: %w",maxAttempts,startErr)
21812216
}
21822217

21832218
funcConfigureHTTPClient(ctx context.Context,clientCertFile,clientKeyFilestring,tlsClientCAFilestring) (context.Context,*http.Client,error) {
@@ -2286,7 +2321,7 @@ func ConnectToPostgres(ctx context.Context, logger slog.Logger, driver string, d
22862321
varerrerror
22872322
varsqlDB*sql.DB
22882323
dbNeedsClosing:=true
2289-
// Try to connect for 30 seconds.
2324+
//nolint:gocritic //Try to connect for 30 seconds.
22902325
ctx,cancel:=context.WithTimeout(ctx,30*time.Second)
22912326
defercancel()
22922327

@@ -2382,6 +2417,7 @@ func ConnectToPostgres(ctx context.Context, logger slog.Logger, driver string, d
23822417
}
23832418

23842419
funcpingPostgres(ctx context.Context,db*sql.DB)error {
2420+
// nolint:gocritic // This is a reasonable magic number for a ping timeout.
23852421
ctx,cancel:=context.WithTimeout(ctx,5*time.Second)
23862422
defercancel()
23872423
returndb.PingContext(ctx)

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp