@@ -2,6 +2,7 @@ package triton
2
2
3
3
import (
4
4
"fmt"
5
+ "log"
5
6
"time"
6
7
7
8
"github.com/aws/aws-sdk-go/aws"
@@ -28,11 +29,13 @@ type ShardStreamReader struct {
28
29
service KinesisService
29
30
records []* kinesis.Record
30
31
lastRequest * time.Time
32
+ retries int64
31
33
}
32
34
33
35
// Recommended minimum polling interval to keep from overloading a Kinesis
34
36
// shard.
35
- const MIN_POLL_INTERVAL = 1.0 * time .Second
37
+ const minPollInterval = 1.0 * time .Second
38
+ const maxRetries = 3
36
39
37
40
func (s * ShardStreamReader ) initIterator () (err error ) {
38
41
gsi := kinesis.GetShardIteratorInput {
@@ -56,7 +59,9 @@ func (s *ShardStreamReader) initIterator() (err error) {
56
59
57
60
func (s * ShardStreamReader ) wait (minInterval time.Duration ) {
58
61
if s .lastRequest != nil {
59
- sleepTime := minInterval - time .Since (* s .lastRequest )
62
+ retryDelay := time .Duration (s .retries * 250 ) * time .Millisecond
63
+
64
+ sleepTime := (minInterval - time .Since (* s .lastRequest )) + retryDelay
60
65
if sleepTime >= time .Duration (0 ) {
61
66
time .Sleep (sleepTime )
62
67
}
@@ -66,8 +71,44 @@ func (s *ShardStreamReader) wait(minInterval time.Duration) {
66
71
s .lastRequest = & n
67
72
}
68
73
74
+ // Documented Kinesis specific errors as well as common errors we
75
+ // should probably just retry on.
76
+ // http://docs.aws.amazon.com/kinesis/latest/APIReference/CommonErrors.html
77
+ var retryErrorCodes = [... ]string {
78
+ "ProvisionedThroughputExceededException" ,
79
+ "ServiceUnavailable" ,
80
+ "InternalFailure" ,
81
+ "Throttling" ,
82
+ }
83
+
84
+ func (s * ShardStreamReader ) isRetryError (err error ) bool {
85
+ if awsErr , ok := err .(awserr.Error ); ok {
86
+ retry := false
87
+
88
+ for _ , code := range retryErrorCodes {
89
+ if awsErr .Code () == code {
90
+ retry = true
91
+ break
92
+ }
93
+ }
94
+
95
+ if retry {
96
+ s .retries += 1
97
+ if s .retries <= maxRetries {
98
+ log .Printf ("%s: %s. Retrying" , awsErr .Code (), awsErr .Message ())
99
+ return true
100
+ } else {
101
+ log .Printf ("%s: %s. Max retries attempted" , awsErr .Code (), awsErr .Message ())
102
+ return false
103
+ }
104
+ }
105
+ }
106
+
107
+ return false
108
+ }
109
+
69
110
func (s * ShardStreamReader ) fetchMoreRecords () (err error ) {
70
- s .wait (MIN_POLL_INTERVAL )
111
+ s .wait (minPollInterval )
71
112
72
113
if s .NextIteratorValue == nil {
73
114
err := s .initIterator ()
@@ -83,9 +124,15 @@ func (s *ShardStreamReader) fetchMoreRecords() (err error) {
83
124
84
125
gro , err := s .service .GetRecords (gri )
85
126
if err != nil {
86
- return err
127
+ if s .isRetryError (err ) {
128
+ return nil
129
+ } else {
130
+ return err
131
+ }
87
132
}
88
133
134
+ s .retries = 0
135
+
89
136
s .records = gro .Records
90
137
s .NextIteratorValue = gro .NextShardIterator
91
138
0 commit comments