From a4723bbd9421db0ecd7777699da16ac5f8197bab Mon Sep 17 00:00:00 2001 From: lenisko <10072920+lenisko@users.noreply.github.com> Date: Tue, 26 May 2026 12:55:44 +0200 Subject: [PATCH 1/2] fix(overpass): retry on 5xx/429 instead of failing Upstream nginx 502s were treated as fatal, stopping the importer. Classify 5xx and 429 as transient, retry with exponential backoff (1s..60s, max 10 attempts). --- overpass/client.go | 20 ++++++++++++++++++++ overpass/errors.go | 1 + 2 files changed, 21 insertions(+) diff --git a/overpass/client.go b/overpass/client.go index 1570ae3..9cfe5bc 100644 --- a/overpass/client.go +++ b/overpass/client.go @@ -69,6 +69,9 @@ func (cli *Client) doSingleQuery(ctx context.Context, v url.Values) (*osm.OSM, e if err != nil { return nil, err } + if resp.StatusCode == http.StatusTooManyRequests || resp.StatusCode >= 500 { + return nil, fmt.Errorf("%w: received status code %d", errTransient, resp.StatusCode) + } return nil, fmt.Errorf("received status code %d: body: %s", resp.StatusCode, string(respBytes)) } @@ -98,6 +101,8 @@ func (cli *Client) GetPossibleNestLocations(ctx context.Context, bound orb.Bound } max_tries := 5 + max_transient_tries := 10 + transient_attempt := 0 for { osm_data, err := cli.doSingleQuery(ctx, urlValues) @@ -111,6 +116,21 @@ func (cli *Client) GetPossibleNestLocations(ctx context.Context, bound orb.Bound } continue } + if errors.Is(err, errTransient) { + if transient_attempt >= max_transient_tries { + return nil, err + } + backoff := time.Duration(1< 60*time.Second { + backoff = 60 * time.Second + } + cli.logger.Warnf("overpass transient error: %v. retry %d/%d after %s.", err, transient_attempt+1, max_transient_tries, backoff) + if err := util.SleepContext(ctx, backoff); err != nil { + return nil, err + } + transient_attempt++ + continue + } if err == errDupeQuery { if max_tries <= 0 { return nil, err diff --git a/overpass/errors.go b/overpass/errors.go index 416ad34..24f7780 100644 --- a/overpass/errors.go +++ b/overpass/errors.go @@ -9,6 +9,7 @@ import ( var ( errTimeout = errors.New("timeout occurred") errDupeQuery = errors.New("dupe query") + errTransient = errors.New("transient http error") readAndIdxBytes = []byte("Dispatcher_Client::request_read_and_idx::") errReadAndIdxTokens = []struct { From 003bd5aef2edf477ca20a03598b720a5531d5d4e Mon Sep 17 00:00:00 2001 From: lenisko <10072920+lenisko@users.noreply.github.com> Date: Sat, 30 May 2026 12:37:00 +0200 Subject: [PATCH 2/2] fix: snake to camelCase --- overpass/client.go | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/overpass/client.go b/overpass/client.go index 9cfe5bc..bcd0283 100644 --- a/overpass/client.go +++ b/overpass/client.go @@ -100,9 +100,9 @@ func (cli *Client) GetPossibleNestLocations(ctx context.Context, bound orb.Bound }, } - max_tries := 5 - max_transient_tries := 10 - transient_attempt := 0 + maxTries := 5 + maxTransientTries := 10 + transientAttempt := 0 for { osm_data, err := cli.doSingleQuery(ctx, urlValues) @@ -117,27 +117,27 @@ func (cli *Client) GetPossibleNestLocations(ctx context.Context, bound orb.Bound continue } if errors.Is(err, errTransient) { - if transient_attempt >= max_transient_tries { + if transientAttempt >= maxTransientTries { return nil, err } - backoff := time.Duration(1< 60*time.Second { backoff = 60 * time.Second } - cli.logger.Warnf("overpass transient error: %v. retry %d/%d after %s.", err, transient_attempt+1, max_transient_tries, backoff) + cli.logger.Warnf("overpass transient error: %v. retry %d/%d after %s.", err, transientAttempt+1, maxTransientTries, backoff) if err := util.SleepContext(ctx, backoff); err != nil { return nil, err } - transient_attempt++ + transientAttempt++ continue } if err == errDupeQuery { - if max_tries <= 0 { + if maxTries <= 0 { return nil, err } bound, bbox = cli.fuzzBound(bound) urlValues["data"][0] = searchPrefix + bbox + searchSuffix - max_tries-- + maxTries-- continue } return nil, err