fix: timeout while waiting for SSH to become available (#92)

In #68 I reduced the general limits for the back off, thinking that it
would speed up the upload on average because it was retrying faster. But
because it was retrying faster, the 10 available retries were used up
before SSH became available.

The new 100 retries match the 3 minutes of total timeout that the
previous solution had, and should fix all issues.

In addition, I discovered that my implementation in
`hcloudimages/backoff.ExponentialBackoffWithLimit` has a bug where the
calculated offset could overflow before the limit was applied, resulting
in negative durations. I did not fix the issue because `hcloud-go`
provides such a method natively nowadays. Instead, I marked the method
as deprecated, to be removed in a later release.
This commit is contained in:
Julian Tölle 2025-05-09 16:15:07 +02:00 committed by GitHub
parent b093e1eda8
commit e490b9a7f3
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 9 additions and 5 deletions

View File

@ -9,7 +9,6 @@ import (
"github.com/spf13/cobra"
"github.com/apricote/hcloud-upload-image/hcloudimages"
"github.com/apricote/hcloud-upload-image/hcloudimages/backoff"
"github.com/apricote/hcloud-upload-image/hcloudimages/contextlogger"
"github.com/apricote/hcloud-upload-image/internal/ui"
"github.com/apricote/hcloud-upload-image/internal/version"
@ -89,7 +88,7 @@ func initClient(cmd *cobra.Command, _ []string) {
opts := []hcloud.ClientOption{
hcloud.WithToken(os.Getenv("HCLOUD_TOKEN")),
hcloud.WithApplication("hcloud-upload-image", version.Version),
hcloud.WithPollOpts(hcloud.PollOpts{BackoffFunc: backoff.ExponentialBackoffWithLimit(2, 1*time.Second, 30*time.Second)}),
hcloud.WithPollOpts(hcloud.PollOpts{BackoffFunc: hcloud.ExponentialBackoffWithOpts(hcloud.ExponentialBackoffOpts{Multiplier: 2, Base: 1 * time.Second, Cap: 30 * time.Second})}),
}
if os.Getenv("HCLOUD_DEBUG") != "" || verbose >= 2 {

View File

@ -16,6 +16,10 @@ import (
// It uses the formula:
//
// min(b^retries * d, limit)
//
// This function has a known overflow issue and should not be used anymore.
//
// Deprecated: Use BackoffFuncWithOpts from github.com/hetznercloud/hcloud-go/v2/hcloud instead.
func ExponentialBackoffWithLimit(b float64, d time.Duration, limit time.Duration) hcloud.BackoffFunc {
return func(retries int) time.Duration {
current := time.Duration(math.Pow(b, float64(retries))) * d

View File

@ -316,7 +316,7 @@ func (s *Client) Upload(ctx context.Context, options UploadOptions) (*hcloud.Ima
err = control.Retry(
contextlogger.New(ctx, logger.With("operation", "ssh")),
10,
100, // ~ 3 minutes
func() error {
var err error
logger.DebugContext(ctx, "trying to connect to server", "ip", server.PublicNet.IPv4.IP)

View File

@ -8,7 +8,8 @@ import (
"context"
"time"
"github.com/apricote/hcloud-upload-image/hcloudimages/backoff"
"github.com/hetznercloud/hcloud-go/v2/hcloud"
"github.com/apricote/hcloud-upload-image/hcloudimages/contextlogger"
)
@ -18,7 +19,7 @@ func Retry(ctx context.Context, maxTries int, f func() error) error {
var err error
backoffFunc := backoff.ExponentialBackoffWithLimit(2, 200*time.Millisecond, 2*time.Second)
backoffFunc := hcloud.ExponentialBackoffWithOpts(hcloud.ExponentialBackoffOpts{Multiplier: 2, Base: 200 * time.Millisecond, Cap: 2 * time.Second})
for try := 0; try < maxTries; try++ {
if ctx.Err() != nil {