kinesis-consumer/pipeline.go

98 lines
2.8 KiB
Go
Raw Normal View History

package connector
import (
"time"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/awserr"
"github.com/aws/aws-sdk-go/service/kinesis"
)
// Pipeline is used as a record processor to configure a pipline.
//
// The user should implement this such that each method returns a configured implementation of each
// interface. It has a data type (Model) as Records come in as a byte[] and are transformed to a Model.
// Then they are buffered in Model form and when the buffer is full, Models's are passed to the emitter.
type Pipeline struct {
Buffer Buffer
Checkpoint Checkpoint
Emitter Emitter
Filter Filter
Kinesis *kinesis.Kinesis
StreamName string
Transformer Transformer
checkpointSequenceNumber string
}
// ProcessShard is a long running process that handles reading records from a Kinesis shard.
func (p Pipeline) ProcessShard(shardID string) {
svc := kinesis.New(&aws.Config{Region: "us-east-1"})
args := &kinesis.GetShardIteratorInput{
ShardID: aws.String(shardID),
StreamName: aws.String(p.StreamName),
}
if p.Checkpoint.CheckpointExists(shardID) {
args.ShardIteratorType = aws.String("AFTER_SEQUENCE_NUMBER")
args.StartingSequenceNumber = aws.String(p.Checkpoint.SequenceNumber())
} else {
args.ShardIteratorType = aws.String("TRIM_HORIZON")
}
resp, err := svc.GetShardIterator(args)
if err != nil {
if awsErr, ok := err.(awserr.Error); ok {
logger.Log("error", "GetShardIterator", "code", awsErr.Code(), "msg", awsErr.Message(), "origError", awsErr.OrigErr())
return
}
}
shardIterator := resp.ShardIterator
for {
args := &kinesis.GetRecordsInput{ShardIterator: shardIterator}
resp, err := svc.GetRecords(args)
if err != nil {
if awsErr, ok := err.(awserr.Error); ok {
if awsErr.Code() == "ProvisionedThroughputExceededException" {
logger.Log("info", "GetRecords", "shardId", shardID, "msg", "rateLimit")
time.Sleep(5 * time.Second)
continue
} else {
logger.Log("error", "GetRecords", "shardId", shardID, "code", awsErr.Code(), "msg", awsErr.Message())
break
}
}
}
if len(resp.Records) > 0 {
for _, r := range resp.Records {
transformedRecord := p.Transformer.ToRecord(r.Data)
if p.Filter.KeepRecord(transformedRecord) {
p.Buffer.ProcessRecord(transformedRecord, *r.SequenceNumber)
}
p.checkpointSequenceNumber = *r.SequenceNumber
}
if p.Buffer.ShouldFlush() {
p.Emitter.Emit(p.Buffer, p.Transformer)
p.Checkpoint.SetCheckpoint(shardID, p.checkpointSequenceNumber)
p.Buffer.Flush()
}
} else if resp.NextShardIterator == aws.String("") || shardIterator == resp.NextShardIterator {
2015-05-26 04:51:53 +00:00
logger.Log("error", "NextShardIterator", "msg", err.Error())
break
} else {
time.Sleep(1 * time.Second)
}
shardIterator = resp.NextShardIterator
}
}