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

fix: retry embedded postgres port allocation#20371

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to ourterms of service andprivacy statement. We’ll occasionally send you account related emails.

Already on GitHub?Sign in to your account

Merged
zedkipp merged 3 commits intomainfromzedkipp/embedded-pg-port
Oct 21, 2025
Merged
Changes fromall commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
114 changes: 75 additions & 39 deletionscli/server.go
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -29,6 +29,7 @@ import (
"strings"
"sync"
"sync/atomic"
"testing"
"time"

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

func shutdownWithTimeout(shutdown func(context.Context) error, timeout time.Duration) error {
// nolint:gocritic // The magic number is parameterized.
ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel()
return shutdown(ctx)
Expand DownExpand Up@@ -2134,50 +2136,83 @@ func startBuiltinPostgres(ctx context.Context, cfg config.Root, logger slog.Logg
return "", nil, xerrors.New("The built-in PostgreSQL cannot run as the root user. Create a non-root user and run again!")
}

// Ensure a password and port have been generated!
connectionURL, err := embeddedPostgresURL(cfg)
if err != nil {
return "", nil, err
}
pgPassword, err := cfg.PostgresPassword().Read()
if err != nil {
return "", nil, xerrors.Errorf("read postgres password: %w", err)
}
pgPortRaw, err := cfg.PostgresPort().Read()
if err != nil {
return "", nil, xerrors.Errorf("read postgres port: %w", err)
}
pgPort, err := strconv.ParseUint(pgPortRaw, 10, 16)
if err != nil {
return "", nil, xerrors.Errorf("parse postgres port: %w", err)
}

cachePath := filepath.Join(cfg.PostgresPath(), "cache")
if customCacheDir != "" {
cachePath = filepath.Join(customCacheDir, "postgres")
}
stdlibLogger := slog.Stdlib(ctx, logger.Named("postgres"), slog.LevelDebug)
ep := embeddedpostgres.NewDatabase(
embeddedpostgres.DefaultConfig().
Version(embeddedpostgres.V13).
BinariesPath(filepath.Join(cfg.PostgresPath(), "bin")).
// Default BinaryRepositoryURL repo1.maven.org is flaky.
BinaryRepositoryURL("https://repo.maven.apache.org/maven2").
DataPath(filepath.Join(cfg.PostgresPath(), "data")).
RuntimePath(filepath.Join(cfg.PostgresPath(), "runtime")).
CachePath(cachePath).
Username("coder").
Password(pgPassword).
Database("coder").
Encoding("UTF8").
Port(uint32(pgPort)).
Logger(stdlibLogger.Writer()),
)
err = ep.Start()
if err != nil {
return "", nil, xerrors.Errorf("Failed to start built-in PostgreSQL. Optionally, specify an external deployment with `--postgres-url`: %w", err)

// If the port is not defined, an available port will be found dynamically.
maxAttempts := 1
Copy link
Member

@johnstcnjohnstcnOct 20, 2025
edited
Loading

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

Is the intention here to reduce the number of flakes in CI?

If so, I'd suggest usingos.Getenv("CI") == "true" for setting this conditionally.

ref:https://docs.github.com/en/actions/reference/workflows-and-actions/variables#:~:text=Description-,CI,-Always%20set%20to

zedkipp reacted with heart emoji
Copy link
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

Yes. I have updated this comment to more clearly indicate the motivation.

I have also adopted the CI environment variable check, TIL.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

I thought about this a little more and there's actually a better way:flag.Lookup("test.v") != nil.

Copy link
ContributorAuthor

@zedkippzedkippOct 21, 2025
edited
Loading

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

While looking up how that works under the hood, I also discoveredtesting.Testing().https://pkg.go.dev/testing#Testing. Seems to be the stdlib approach for this problem. Some context I foundhttps://redirect.github.com/golang/go/issues/52600

I've adopted the stdlib approach

_, err = cfg.PostgresPort().Read()
retryPortDiscovery := errors.Is(err, os.ErrNotExist) && testing.Testing()
if retryPortDiscovery {
// There is no way to tell Postgres to use an ephemeral port, so in order to avoid
// flaky tests in CI we need to retry EmbeddedPostgres.Start in case of a race
// condition where the port we quickly listen on and close in embeddedPostgresURL()
// is not free by the time the embedded postgres starts up. This maximum_should
// cover most cases where port conflicts occur in CI and cause flaky tests.
maxAttempts = 3
}

var startErr error
for attempt := 0; attempt < maxAttempts; attempt++ {
// Ensure a password and port have been generated.
connectionURL, err := embeddedPostgresURL(cfg)
if err != nil {
return "", nil, err
}
pgPassword, err := cfg.PostgresPassword().Read()
if err != nil {
return "", nil, xerrors.Errorf("read postgres password: %w", err)
}
pgPortRaw, err := cfg.PostgresPort().Read()
if err != nil {
return "", nil, xerrors.Errorf("read postgres port: %w", err)
}
pgPort, err := strconv.ParseUint(pgPortRaw, 10, 16)
if err != nil {
return "", nil, xerrors.Errorf("parse postgres port: %w", err)
}

ep := embeddedpostgres.NewDatabase(
embeddedpostgres.DefaultConfig().
Version(embeddedpostgres.V13).
BinariesPath(filepath.Join(cfg.PostgresPath(), "bin")).
// Default BinaryRepositoryURL repo1.maven.org is flaky.
BinaryRepositoryURL("https://repo.maven.apache.org/maven2").
DataPath(filepath.Join(cfg.PostgresPath(), "data")).
RuntimePath(filepath.Join(cfg.PostgresPath(), "runtime")).
CachePath(cachePath).
Username("coder").
Password(pgPassword).
Database("coder").
Encoding("UTF8").
Port(uint32(pgPort)).
Logger(stdlibLogger.Writer()),
)

startErr = ep.Start()
if startErr == nil {
return connectionURL, ep.Stop, nil
}

logger.Warn(ctx, "failed to start embedded postgres",
slog.F("attempt", attempt+1),
slog.F("max_attempts", maxAttempts),
slog.F("port", pgPort),
slog.Error(startErr),
)

if retryPortDiscovery {
// Since a retry is needed, we wipe the port stored here at the beginning of the loop.
_ = cfg.PostgresPort().Delete()
}
}
return connectionURL, ep.Stop, nil

return "", nil, xerrors.Errorf("failed to start built-in PostgreSQL after %d attempts. "+
"Optionally, specify an external deployment. See https://coder.com/docs/tutorials/external-database "+
"for more details: %w", maxAttempts, startErr)
}

func ConfigureHTTPClient(ctx context.Context, clientCertFile, clientKeyFile string, tlsClientCAFile string) (context.Context, *http.Client, error) {
Expand DownExpand Up@@ -2286,7 +2321,7 @@ func ConnectToPostgres(ctx context.Context, logger slog.Logger, driver string, d
var err error
var sqlDB *sql.DB
dbNeedsClosing := true
// Try to connect for 30 seconds.
//nolint:gocritic //Try to connect for 30 seconds.
ctx, cancel := context.WithTimeout(ctx, 30*time.Second)
defer cancel()

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

func pingPostgres(ctx context.Context, db *sql.DB) error {
// nolint:gocritic // This is a reasonable magic number for a ping timeout.
ctx, cancel := context.WithTimeout(ctx, 5*time.Second)
defer cancel()
return db.PingContext(ctx)
Expand Down
Loading

[8]ページ先頭

©2009-2025 Movatter.jp