@@ -5,12 +5,16 @@ package geoipupdate
5
5
import (
6
6
"context"
7
7
"encoding/json"
8
+ "errors"
8
9
"fmt"
9
10
"log"
10
11
"os"
11
12
"sync"
12
13
"time"
13
14
15
+ "github.com/cenkalti/backoff/v4"
16
+ "golang.org/x/net/http2"
17
+
14
18
"github.com/maxmind/geoipupdate/v6/pkg/geoipupdate/database"
15
19
"github.com/maxmind/geoipupdate/v6/pkg/geoipupdate/internal"
16
20
)
@@ -85,20 +89,11 @@ func (c *Client) Run(ctx context.Context) error {
85
89
for _ , editionID := range c .config .EditionIDs {
86
90
editionID := editionID
87
91
processFunc := func (ctx context.Context ) error {
88
- editionHash , err := writer . GetHash ( editionID )
92
+ edition , err := c . downloadEdition ( ctx , editionID , reader , writer )
89
93
if err != nil {
90
94
return err
91
95
}
92
96
93
- edition , err := reader .Read (ctx , editionID , editionHash )
94
- if err != nil {
95
- return err
96
- }
97
-
98
- if err := writer .Write (edition ); err != nil {
99
- return err
100
- }
101
-
102
97
edition .CheckedAt = time .Now ().In (time .UTC )
103
98
104
99
mu .Lock ()
@@ -126,3 +121,58 @@ func (c *Client) Run(ctx context.Context) error {
126
121
127
122
return nil
128
123
}
124
+
125
+ // downloadEdition downloads the file with retries on HTTP2 INTERNAL_ERRORs.
126
+ func (c * Client ) downloadEdition (
127
+ ctx context.Context ,
128
+ editionID string ,
129
+ r database.Reader ,
130
+ w database.Writer ,
131
+ ) (* database.ReadResult , error ) {
132
+ editionHash , err := w .GetHash (editionID )
133
+ if err != nil {
134
+ return nil , err
135
+ }
136
+
137
+ // RetryFor value of 0 means that no retries should be performed.
138
+ // Max zero retries has to be set to achieve that
139
+ // because the backoff never stops if MaxElapsedTime is zero.
140
+ exp := backoff .NewExponentialBackOff ()
141
+ exp .MaxElapsedTime = c .config .RetryFor
142
+ b := backoff .BackOff (exp )
143
+ if exp .MaxElapsedTime == 0 {
144
+ b = backoff .WithMaxRetries (exp , 0 )
145
+ }
146
+
147
+ var edition * database.ReadResult
148
+ err = backoff .RetryNotify (
149
+ func () error {
150
+ edition , err = r .Read (ctx , editionID , editionHash )
151
+ if err != nil {
152
+ return backoff .Permanent (err )
153
+ }
154
+
155
+ if err = w .Write (edition ); err != nil {
156
+ streamErr := http2.StreamError {}
157
+ if errors .As (err , & streamErr ) && streamErr .Code .String () == "INTERNAL_ERROR" {
158
+ return err
159
+ }
160
+
161
+ return backoff .Permanent (err )
162
+ }
163
+
164
+ return nil
165
+ },
166
+ b ,
167
+ func (err error , d time.Duration ) {
168
+ if c .config .Verbose {
169
+ log .Printf ("Couldn't download %s, retrying in %v: %v" , editionID , d , err )
170
+ }
171
+ },
172
+ )
173
+ if err != nil {
174
+ return nil , err
175
+ }
176
+
177
+ return edition , nil
178
+ }
0 commit comments