|
4 | 4 | "math/rand"
|
5 | 5 | "net/url"
|
6 | 6 | "strings"
|
7 |
| - "sync" |
8 | 7 | "time"
|
9 | 8 |
|
10 | 9 | "github.com/aws/aws-sdk-go/aws"
|
@@ -149,124 +148,16 @@ func (d dynamoRequestAdapter) Error() error {
|
149 | 148 |
|
150 | 149 | type dynamoDBBackoffClient struct {
|
151 | 150 | client DynamoDBClient
|
152 |
| - |
153 |
| - dynamoRequests chan dynamoOp |
154 |
| - dynamoRequestsDone sync.WaitGroup |
155 | 151 | }
|
156 | 152 |
|
157 | 153 | func newDynamoDBBackoffClient(client DynamoDBClient) *dynamoDBBackoffClient {
|
158 |
| - c := &dynamoDBBackoffClient{ |
159 |
| - client: client, |
160 |
| - dynamoRequests: make(chan dynamoOp), |
161 |
| - } |
162 |
| - |
163 |
| - c.dynamoRequestsDone.Add(numDynamoRequests) |
164 |
| - for i := 0; i < numDynamoRequests; i++ { |
165 |
| - go c.dynamoRequestLoop() |
| 154 | + return &dynamoDBBackoffClient{ |
| 155 | + client: client, |
166 | 156 | }
|
167 |
| - |
168 |
| - return c |
169 |
| -} |
170 |
| - |
171 |
| -// Stop background goroutines. |
172 |
| -func (c *dynamoDBBackoffClient) Stop() { |
173 |
| - close(c.dynamoRequests) |
174 |
| - c.dynamoRequestsDone.Wait() |
175 | 157 | }
|
176 | 158 |
|
177 | 159 | // batchWriteDynamo writes many requests to dynamo in a single batch.
|
178 | 160 | func (c *dynamoDBBackoffClient) batchWriteDynamo(ctx context.Context, reqs map[string][]*dynamodb.WriteRequest) error {
|
179 |
| - req := &dynamoBatchWriteItemsOp{ |
180 |
| - ctx: ctx, |
181 |
| - reqs: reqs, |
182 |
| - dynamodb: c.client, |
183 |
| - done: make(chan error), |
184 |
| - } |
185 |
| - c.dynamoRequests <- req |
186 |
| - return <-req.done |
187 |
| -} |
188 |
| - |
189 |
| -func (c *dynamoDBBackoffClient) queryPages(ctx context.Context, input *dynamodb.QueryInput, callback func(resp interface{}, lastPage bool) (shouldContinue bool)) error { |
190 |
| - page, _ := c.client.QueryRequest(input) |
191 |
| - req := &dynamoQueryPagesOp{ |
192 |
| - ctx: ctx, |
193 |
| - request: page, |
194 |
| - callback: callback, |
195 |
| - done: make(chan error), |
196 |
| - } |
197 |
| - c.dynamoRequests <- req |
198 |
| - return <-req.done |
199 |
| -} |
200 |
| - |
201 |
| -func (c *dynamoDBBackoffClient) dynamoRequestLoop() { |
202 |
| - defer c.dynamoRequestsDone.Done() |
203 |
| - for { |
204 |
| - select { |
205 |
| - case request, ok := <-c.dynamoRequests: |
206 |
| - if !ok { |
207 |
| - return |
208 |
| - } |
209 |
| - request.do() |
210 |
| - } |
211 |
| - } |
212 |
| -} |
213 |
| - |
214 |
| -type dynamoOp interface { |
215 |
| - do() |
216 |
| -} |
217 |
| - |
218 |
| -type dynamoQueryPagesOp struct { |
219 |
| - ctx context.Context |
220 |
| - request dynamoRequest |
221 |
| - callback func(resp interface{}, lastPage bool) (shouldContinue bool) |
222 |
| - done chan error |
223 |
| -} |
224 |
| - |
225 |
| -type dynamoBatchWriteItemsOp struct { |
226 |
| - ctx context.Context |
227 |
| - reqs map[string][]*dynamodb.WriteRequest |
228 |
| - dynamodb DynamoDBClient |
229 |
| - done chan error |
230 |
| -} |
231 |
| - |
232 |
| -func (r *dynamoQueryPagesOp) do() { |
233 |
| - backoff := minBackoff |
234 |
| - |
235 |
| - for page := r.request; page != nil; page = page.NextPage() { |
236 |
| - err := instrument.TimeRequestHistogram(r.ctx, "DynamoDB.QueryPages", dynamoRequestDuration, func(_ context.Context) error { |
237 |
| - return page.Send() |
238 |
| - }) |
239 |
| - |
240 |
| - if cc := page.Data().(*dynamodb.QueryOutput).ConsumedCapacity; cc != nil { |
241 |
| - dynamoConsumedCapacity.WithLabelValues("DynamoDB.QueryPages"). |
242 |
| - Add(float64(*cc.CapacityUnits)) |
243 |
| - } |
244 |
| - |
245 |
| - if err != nil { |
246 |
| - recordDynamoError(err) |
247 |
| - |
248 |
| - if awsErr, ok := err.(awserr.Error); ok && awsErr.Code() == provisionedThroughputExceededException { |
249 |
| - time.Sleep(backoff) |
250 |
| - backoff = nextBackoff(backoff) |
251 |
| - continue |
252 |
| - } |
253 |
| - |
254 |
| - r.done <- page.Error() |
255 |
| - return |
256 |
| - } |
257 |
| - |
258 |
| - if getNextPage := r.callback(page.Data(), !page.HasNextPage()); !getNextPage { |
259 |
| - r.done <- page.Error() |
260 |
| - return |
261 |
| - } |
262 |
| - |
263 |
| - backoff = minBackoff |
264 |
| - } |
265 |
| - |
266 |
| - r.done <- nil |
267 |
| -} |
268 |
| - |
269 |
| -func (r *dynamoBatchWriteItemsOp) do() { |
270 | 161 | min := func(i, j int) int {
|
271 | 162 | if i < j {
|
272 | 163 | return i
|
@@ -306,17 +197,17 @@ func (r *dynamoBatchWriteItemsOp) do() {
|
306 | 197 | }
|
307 | 198 | }
|
308 | 199 |
|
309 |
| - outstanding, unprocessed := r.reqs, map[string][]*dynamodb.WriteRequest{} |
| 200 | + outstanding, unprocessed := reqs, map[string][]*dynamodb.WriteRequest{} |
310 | 201 | backoff := minBackoff
|
311 | 202 | for dictLen(outstanding)+dictLen(unprocessed) > 0 {
|
312 | 203 | reqs := map[string][]*dynamodb.WriteRequest{}
|
313 | 204 | fillReq(unprocessed, reqs)
|
314 | 205 | fillReq(outstanding, reqs)
|
315 | 206 |
|
316 | 207 | var resp *dynamodb.BatchWriteItemOutput
|
317 |
| - err := instrument.TimeRequestHistogram(r.ctx, "DynamoDB.BatchWriteItem", dynamoRequestDuration, func(_ context.Context) error { |
| 208 | + err := instrument.TimeRequestHistogram(ctx, "DynamoDB.BatchWriteItem", dynamoRequestDuration, func(_ context.Context) error { |
318 | 209 | var err error
|
319 |
| - resp, err = r.dynamodb.BatchWriteItem(&dynamodb.BatchWriteItemInput{ |
| 210 | + resp, err = c.client.BatchWriteItem(&dynamodb.BatchWriteItemInput{ |
320 | 211 | RequestItems: reqs,
|
321 | 212 | ReturnConsumedCapacity: aws.String(dynamodb.ReturnConsumedCapacityTotal),
|
322 | 213 | })
|
@@ -350,14 +241,49 @@ func (r *dynamoBatchWriteItemsOp) do() {
|
350 | 241 |
|
351 | 242 | // All other errors are fatal.
|
352 | 243 | if err != nil {
|
353 |
| - r.done <- err |
354 |
| - return |
| 244 | + return err |
| 245 | + } |
| 246 | + |
| 247 | + backoff = minBackoff |
| 248 | + } |
| 249 | + |
| 250 | + return nil |
| 251 | +} |
| 252 | + |
| 253 | +func (c *dynamoDBBackoffClient) queryPages(ctx context.Context, input *dynamodb.QueryInput, callback func(resp interface{}, lastPage bool) (shouldContinue bool)) error { |
| 254 | + request, _ := c.client.QueryRequest(input) |
| 255 | + backoff := minBackoff |
| 256 | + |
| 257 | + for page := request; page != nil; page = page.NextPage() { |
| 258 | + err := instrument.TimeRequestHistogram(ctx, "DynamoDB.QueryPages", dynamoRequestDuration, func(_ context.Context) error { |
| 259 | + return page.Send() |
| 260 | + }) |
| 261 | + |
| 262 | + if cc := page.Data().(*dynamodb.QueryOutput).ConsumedCapacity; cc != nil { |
| 263 | + dynamoConsumedCapacity.WithLabelValues("DynamoDB.QueryPages"). |
| 264 | + Add(float64(*cc.CapacityUnits)) |
| 265 | + } |
| 266 | + |
| 267 | + if err != nil { |
| 268 | + recordDynamoError(err) |
| 269 | + |
| 270 | + if awsErr, ok := err.(awserr.Error); ok && awsErr.Code() == provisionedThroughputExceededException { |
| 271 | + time.Sleep(backoff) |
| 272 | + backoff = nextBackoff(backoff) |
| 273 | + continue |
| 274 | + } |
| 275 | + |
| 276 | + return page.Error() |
| 277 | + } |
| 278 | + |
| 279 | + if getNextPage := callback(page.Data(), !page.HasNextPage()); !getNextPage { |
| 280 | + return page.Error() |
355 | 281 | }
|
356 | 282 |
|
357 | 283 | backoff = minBackoff
|
358 | 284 | }
|
359 | 285 |
|
360 |
| - r.done <- nil |
| 286 | + return nil |
361 | 287 | }
|
362 | 288 |
|
363 | 289 | func nextBackoff(lastBackoff time.Duration) time.Duration {
|
|
0 commit comments