11package dynamo
22
33import (
4- "github.com/aws/aws-sdk-go/aws"
54 "github.com/aws/aws-sdk-go/service/dynamodb"
65 "github.com/halprin/delete-dynamodb-items/parallel"
76 "log"
7+ "math/rand"
88 "time"
99)
1010
@@ -22,14 +22,33 @@ func deleteItems(dynamoItems []map[string]*dynamodb.AttributeValue, tableName st
2222
2323 dynamoItemsChunks := chunkItems (dynamoItems )
2424
25+ concurrency , err := determineConcurrency (tableName )
26+ if err != nil {
27+ log .Println ("Unable determine the concurrency" )
28+ return err
29+ }
30+
31+ goroutinePool := parallel .NewPool (concurrency , len (dynamoItemsChunks ))
32+ defer goroutinePool .Release ()
33+
2534 var errorChannels []chan error
2635
2736 for _ , currentItemsChunk := range dynamoItemsChunks {
28- errorChannel := make (chan error )
37+
38+ errorChannel := make (chan error , 1 )
2939 errorChannels = append (errorChannels , errorChannel )
30- go deleteChunkGoroutine (currentItemsChunk , tableName , errorChannel )
40+
41+ //wrapping in a function to make a copy of the currentItemsChunk and errorChannel arguments that are passed in,
42+ //else all executions try to delete the same chunk of items
43+ func (currentItemsChunk []map [string ]* dynamodb.AttributeValue , errorChannel chan error ) {
44+ goroutinePool .Submit (func () {
45+ deleteChunkGoroutine (currentItemsChunk , tableName , errorChannel )
46+ })
47+ }(currentItemsChunk , errorChannel )
3148 }
3249
50+ log .Println ("Waiting for all deletion goroutines to complete" )
51+
3352 for errorFromGoroutine := range parallel .MergeErrorChannels (errorChannels ) {
3453 if errorFromGoroutine != nil {
3554 log .Println ("One of the delete goroutines failed" )
@@ -62,16 +81,10 @@ func deleteChunk(currentItemsChunk []map[string]*dynamodb.AttributeValue, tableN
6281}
6382
6483func getTableKeys (tableName string ) ([]* dynamodb.KeySchemaElement , error ) {
65- describeTableInput := & dynamodb.DescribeTableInput {
66- TableName : aws .String (tableName ),
67- }
68-
69- tableInfo , err := dynamoService .DescribeTable (describeTableInput )
84+ tableInfo , err := describeTable (tableName )
7085 if err != nil {
71- log .Println ("Unable to describe the the table" )
7286 return nil , err
7387 }
74-
7588 return tableInfo .Table .KeySchema , nil
7689}
7790
@@ -124,7 +137,15 @@ func convertItemToKey(item map[string]*dynamodb.AttributeValue) map[string]*dyna
124137}
125138
126139func incrementallyBatchDelete (requestItems map [string ][]* dynamodb.WriteRequest ) error {
127- millisecondsToWait := 20
140+ //used to induce jitter
141+ randomGenerator := rand .New (rand .NewSource (time .Now ().UnixNano ()))
142+
143+ baseMillisecondsToWait := 20
144+ maxMillisecondsToWait := 40
145+ millisecondsToWait := randomGenerator .Intn (maxMillisecondsToWait )
146+
147+ //start of waiting so all the goroutines don't call batch delete at the same time
148+ time .Sleep (time .Duration (millisecondsToWait ) * time .Millisecond )
128149
129150 for {
130151 batchWriteItemInput := & dynamodb.BatchWriteItemInput {
@@ -149,9 +170,10 @@ func incrementallyBatchDelete(requestItems map[string][]*dynamodb.WriteRequest)
149170 break
150171 }
151172
152- //do an exponential back-off
173+ //do an exponential back-off with jitter
153174 time .Sleep (time .Duration (millisecondsToWait ) * time .Millisecond )
154- millisecondsToWait *= 2
175+ maxMillisecondsToWait *= 2
176+ millisecondsToWait = baseMillisecondsToWait + randomGenerator .Intn (maxMillisecondsToWait )
155177 }
156178
157179 return nil
0 commit comments